elm-geometry icon indicating copy to clipboard operation
elm-geometry copied to clipboard

Add Grid2d, Grid3d modules?

Open ianmackenzie opened this issue 5 years ago • 7 comments

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:

  • 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).
  • Similarly, should there be separate operations for simply 'convert to float' and 'convert to cell midpoint'? In one case (2, 1) would simply become (2.0, 1.0) but in the other it would become (2.5, 1.5).
  • How should the size of grid cells be defined (and should grids with non-square cells be supported)? For some unit types like pixels a 1x1 unit grid makes sense, but for real-world lengths there is no single natural grid size (1x1 meters? 1x1 feet?).

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

ianmackenzie avatar Sep 10 '18 15:09 ianmackenzie

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".

MartinSStewart avatar Aug 10 '19 15:08 MartinSStewart

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.

ianmackenzie avatar Aug 11 '19 03:08 ianmackenzie

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.

MartinSStewart avatar Aug 15 '20 16:08 MartinSStewart

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.

ianmackenzie avatar Aug 15 '20 20:08 ianmackenzie

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

MartinSStewart avatar Aug 16 '20 08:08 MartinSStewart

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/

  • A Point2i type was used for storing things like cursor position, view position, position of ascii chars, position of "cells" (16x16 blocks of ascii characters)
  • A Vector2i type was used for size of cursor box selection, for cursor translateBy, storing window dimensions
  • A BoundingBox2i type was used for storing what region is visible to a user

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.

MartinSStewart avatar Oct 03 '20 21:10 MartinSStewart

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...

ianmackenzie avatar Oct 04 '20 01:10 ianmackenzie