Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to reproduce the same cell pattern around different indexes? #15

Closed
FrogOfJuly opened this issue Aug 13, 2023 · 8 comments
Closed

Comments

@FrogOfJuly
Copy link

I asked the same question on stackoverflow, but it seems like it won't be answered.

I need some way of address the cell's neghbours depending their relative position. As I understand the order is not determined even in the safe variants of neighbor traversal.

Is there any way to have local coordinates in h3o?

The only relevant thing I found is the Direction enum, but it is used for other thing.

P.S. Feel free to remove the issue if it is a wrong place to ask this.

@isaacbrodsky
Copy link

isaacbrodsky commented Aug 13, 2023

There is a set of functions for working with local coordinates, called LocalIJ. In C they are called cellToLocalIj and localIjToCell. In Rust h3o I believe you need to use the LocalIJ type. These functions give you local I, J coordinates for some cell. (Note that the origin or anchor cell probably won't be at 0,0 in this coordinate space.) You can then apply a translation to those coordinates to draw your pattern.

@FrogOfJuly
Copy link
Author

Is there any good description of how to address specific cells? I found some here, but I can't make sense of it.

  • What is the origin and how it is different from the anchor?
  • Why there are only i and j, but no k?

For more context here is an example of what pattern I am talking about:

Screenshot 2023-08-13 at 23 30 18

@grim7reaper
Copy link
Contributor

In Rust h3o I believe you need to use the LocalIJ type.

Yup that's right.

To get the LocalIJ coordinate of a given CellIndex, use to_local_ij and specify your anchor:

let local_ij = cell_index.to_local_ij(anchor);

To get a CellIndex from a LocalIJ coordinate, use a TryFrom:

let cell_index = CellIndex::try_from(local_ij);

@FrogOfJuly
Copy link
Author

How do I choose anchor?

@grim7reaper
Copy link
Contributor

  • What is the origin and how it is different from the anchor?

They are the same in this context, that's two different way to call the same thing.
In your SO question you said:

I want to encode a local cell pattern to draw it around a given index.

The origin/anchor in this case is the index you want to draw around.

  • Why there are only i and j, but no k?

The i of IJ is i - k and the j of IJ is j - k, that's why k doesn't appear in IJ compared to IJK.
As to why IJ is exposed insted of IJK, @isaacbrodsky would probably know better than me.


So, if I understand your use case correctly, the code you need could look like this:

fn transpose_pattern(src: CellIndex, dst: CellIndex, pattern: &[CellIndex]) -> Vec<CellIndex> {
    // Compute translation offset.
    let src_coord = src.to_local_ij(src).expect("src coord");
    let dst_coord = dst.to_local_ij(dst).expect("dst coord");
    let i_offset = dst_coord.i() - src_coord.i();
    let j_offset = dst_coord.j() - src_coord.j();

    // Transpose the pattern from src to dst.
    pattern
        .iter()
        .copied()
        .map(|cell| {
            // Compute the local IJ coordinate wrt original center cell.
            let src_ij = cell.to_local_ij(src).expect("local IJ");
            // Apply translation and re-anchor at destination center cell.
            let dst_ij = LocalIJ::new_unchecked(dst, src_ij.i() + i_offset, src_ij.j() + j_offset);
            // Convert back to cell index.
            CellIndex::try_from(dst_ij).expect("dst cell")
        })
        .collect::<Vec<_>>()
}

With:

  • src: the original index around which the pattern was drawn
  • dst: the new index around which you want to draw the pattern
  • pattern: the cells that compose your pattern

⚠️ Note that those conversions between cells and IJ coordinate can fail when the cell index is too far away from the anchor/is on the other side of a pentagon.
So depending how large your pattern are/their location, this may not work.

⚠️ Also, the LocalIJ::new_unchecked I use in this snippet is currently a non-publicly exposed method (you can still call it but it's not part of the public API yet). Currently you can only build a LocalIJ from a CellIndex (to avoid crafting invalid LocalIJ).

I do plan to have a public method for that, but I've postponed it until now since I didn't have a use case for it.
There is little work to do beforehand, but I can probably cobble up something this week 🙂 (meanwhile, you can use this hidden method).

@FrogOfJuly
Copy link
Author

FrogOfJuly commented Aug 13, 2023

Thank you for the snippet!

As I understand this can only move patterns until local coordinates are valid. I am ok with the restriction on the size of the pattern, but is there a way to reconstruct the pattern in dst without restricting the distance between patterns?

For example, somehow to remember LocalIJ coordinates and reproduce them around the dst index.

@grim7reaper
Copy link
Contributor

is there a way to reconstruct the pattern in dst without restricting the distance between patterns?

The snippet does that, it's not limited by the distance between src and dst.

Pattern could be stored as a struct like that:

struct Pattern {
    anchor: CellIndex,
    pattern: Vec<(i32, i32)>,
}

Where the pattern field is a list of CoordIJ (maybe I should expose this type as well, meanwhile a tuple of i32 will do the trick), one for each cell.

Then you could have an API like that:

impl Pattern {
    pub fn new(anchor: CellIndex, pattern: &[CellIndex]) -> Self {
        Self {
            anchor,
            pattern: pattern
                .iter()
                .map(|&cell| {
                    let coord = cell.to_local_ij(anchor).expect("local IJ");
                    (coord.i(), coord.j())
                })
                .collect(),
        }
    }

    pub fn draw(&self, anchor: CellIndex) -> Vec<CellIndex> {
        // Compute translation offset.
        let src_coord = self.anchor.to_local_ij(self.anchor).expect("src coord");
        let dst_coord = anchor.to_local_ij(anchor).expect("dst coord");
        let i_offset = dst_coord.i() - src_coord.i();
        let j_offset = dst_coord.j() - src_coord.j();

        self.pattern
            .iter()
            .copied()
            .map(|coord| {
                let dst_coord =
                    LocalIJ::new_unchecked(anchor, coord.0 + i_offset, coord.1 + j_offset);
                // Convert back to cell index.
                CellIndex::try_from(dst_coord).expect("dst cell")
            })
            .collect::<Vec<_>>()
    }
}

The new encode/remember the pattern in coordinates.
The draw project them around the new anchor/destination.

@FrogOfJuly
Copy link
Author

It works!!! Thank you so much

grim7reaper added a commit that referenced this issue Aug 14, 2023
This simplifies/makes possible use cases like
#15

But now that user can create arbitrary local IJ coordinates, the code
that handles it must be more robust (hence the overflow handling, the
new test cases and the extra fuzz target).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants