ianmackenzie / elm-geometry

2D/3D geometry package for Elm
Mozilla Public License 2.0
183 stars 26 forks source link

Add Grid2d, Grid3d modules? #71

Open ianmackenzie opened 6 years ago

ianmackenzie commented 6 years ago

Especially if elm-geometry gains support for Int-valued geometry as part of #66, it would likely be useful to add dedicated functionality to convert back and forth between Int and Float-valued geometry. I think this would be done by providing Grid2d and Grid3d modules that provide functions for converting back and forth between Float coordinates and Int grid indices. However, there are some subtleties here that will be important to get right:

What sorts of use cases do people have for converting back and forth between continuous and discrete geometry?

MartinSStewart commented 5 years ago

This doesn't answer all your questions but I've worked a bit with this now so I figured I'd give some input.

I had a need for a Grid2d type in a game I'm making. I went ahead and implemented my own by copying from the Point2d module and swapping out the floats for ints (except in cases like distanceFrom which still returns a float). What I quickly realized was that, once I had a Grid2d, I also needed a grid version of Vector2d for functions like translateBy. Then later I also needed grid version of BoundingBox2d. Instead of coming up with new names for these I just named them Point2i, Vector2i, and BoundingBox2i*.

Currently I also have a Cardinal module. This represents cardinal directions, North, South, East, West though I've named them Top, Bottom, Left, Right because they are sometimes used for local coordinates systems. This is sort of like an integer version of Direction2d.

Should there be separate operations for 'round to nearest integer' and 'find containing cell index'? For example, the point (1.6, 0.7) would naively round to (2, 1) but is in the cell with indices (1, 0).

In my case all I have needed to implement is roundFrom : Point2d -> Point2i and a toPoint2d : Point2i -> Point2d.

*I suspect the 2d in elm-geometry means "2 dimensions" but in this case I've retroactively reinterpreted it to mean "2 doubles" so that 2i can be "2 integers".

ianmackenzie commented 5 years ago

Cool, thanks for the comments, very useful! And yeah the dual interpretation of "2d" is handy, the Eigen C++ math library has Vector3d, Vector3f, Vector3i etc. and was definitely an early inspiration for elm-geometry =)

Things will also probably have to be rethought a bit when the coordinate-systems branch is merged and released as the next major version...the addition of units will complicate things slightly, although I think it also help make things more explicit.

MartinSStewart commented 4 years ago

I'm currently working on a project that involves a lot of grid related math (sometimes even on non-square grids) mixed in with normal Point2d and Vector2d stuff. I think this could be a good usecase if you have a rough draft for an API that needs testing.

ianmackenzie commented 4 years ago

I haven't been looking at this recently, but it would be great if you could list out a few of the key operations you find yourself needing as you go - that would be really useful when eventually designing an API.

MartinSStewart commented 4 years ago

Sounds good. I'll keep notes on that and get back to you when I've made more progress.

MartinSStewart commented 4 years ago

Saw your comment on Slack and it reminded me to get around to writing up what key operations I needed for grid operations.

I'll list some notes here as I don't know of a good way to structure this. Let me know if you want more information about a specific part or if something is unclear.

For reference, this is the app I needed grid operations for https://ascii-collab.lamdera.app/

Operations I needed were mostly just simple things like translateBy, scale, and various functions to convert between Point2i's with different units (single ascii characters and 16x16 cells), and Point2i to Point2d (and back).

There was some complexity in conversions though because the ascii characters are 10x18 pixels in size so converting between Point2d and Point2i meant scaling the x and y by different amounts. For this reason, my scale function let me scale x and y independently.

Also going from Point2d to Point2i or Point2i with ascii units to Point2i with 16x16 cells meant I needed to decide how to round. For the min point and max point representing a BoundingBox2i, I'd want the min point to round down, and the max point to round up. Determining which ascii coordinate the mouse cursor is over involved always rounding down (once I had converted the mouse position from ScreenPixel to WorldPixel coordinates)

I often used Point2i values as keys in a Dict. For this reason, I defined Point2i as (Int, Int) so it would be comparable. This meant less type safety since I could potentially mix up units or coordinate systems. It's probably better to use an opaque type for elm-geometry.

ianmackenzie commented 4 years ago

That's really helpful @MartinSStewart, thanks for writing that up! Being able to think about the ASCII collab app as a use case when trying to draft an API will I think be really useful. And that issue with bounding box rounding is I think a great example of the general issues about how to convert between integer and float coordinates in different situations...