Open ianmackenzie opened 6 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".
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.
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.
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.
Sounds good. I'll keep notes on that and get back to you when I've made more progress.
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/
Point2i
type was used for storing things like cursor position, view position, position of ascii chars, position of "cells" (16x16 blocks of ascii characters)Vector2i
type was used for size of cursor box selection, for cursor translateBy, storing window dimensionsBoundingBox2i
type was used for storing what region is visible to a userOperations 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.
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...
Especially if
elm-geometry
gains support forInt
-valued geometry as part of #66, it would likely be useful to add dedicated functionality to convert back and forth betweenInt
andFloat
-valued geometry. I think this would be done by providingGrid2d
andGrid3d
modules that provide functions for converting back and forth betweenFloat
coordinates andInt
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?