-
Notifications
You must be signed in to change notification settings - Fork 72
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
Figure out sign conventions #4
Comments
Call me crazy, but if the idea is to, basically, "be the best library" then it feels like there should be a way to bootstrap the coordinate system so that after calling the appropriate function, the library "just works" using whatever convention it should be using, given the user's need (and then I'd suggest the computer graphics convention as default, with (0,0) in the top left). And then whether that takes the form of a static call, or per-call option, or even literally a duplicated library with the two different y conventions should probably be informed by "what performs best". |
@Pomax I'm open to that, but want to make sure it doesn't come at a significant cost of compile time, usability, doc clarity, etc. Sketching this out, here are a couple ways this might look: One is to have a zero-size type for Y direction, then instead of Another possibility is to do this at the module level: instead of I see the value, it feels more correct, but I'm hesitating. It's really only a small handful of methods (essentially rotations) where you might really want both conventions, and for the other stuff, as long as it's documented, users will be adapt and be happy, I think. MathWorld defines positive area as counterclockwise, and then of course gives the shoelace formula assuming y-up. Looking at Cairo, which was designed by people I trust, they define a rectangle as clockwise (given width and height of the same sign). So this is where I'm leaning right now, though quite open to discussion: The convention of y-up or y-down is not specified. Positive area is widdershins in a y-up space, clockwise in y-down. I'm inclined to define theta the same way (a clockwise rotation in y-down), but I see Cairo's rotate and probably other things define it as anticlockwise, so in that case I'll have separate rotate_y_up and rotate_y_down methods. Curvature and winding number are consistent with area: the curvature of a smooth convex path of positive area is positive, and the winding number inside a positive area path is positive. The shoelace formula as conventionally written, in xy coordinates, has the correct sign. |
I suggest defining things in terms of X/Y sign and agnostic of Y direction: when traveling in the direction of the positive X axis, then turning towards positive Y axis is a positive angle, positive curvature, results in positive area, and positive winding number. |
@behdad This seems simplest and most intuitive to me. I also think I got Cairo wrong, it's consistent with that, and Web Canvas (which seems to follow Cairo really closely) is the same. I'll go with this unless something radical changes my mind. Now I think the main challenge is documentation and naming; you certainly don't want to call the maximal y coordinate of a bounding box rectangle "bottom," even though that would be most natural in a y-down world. |
Right. The most commonly type is done wrong in a y-up model is to model rectangle's y with it's "top", and positive y height towards y-down... Most other shapes are intuitive. |
Right. We have a couple of these naming issues in euclid, which doesn't matter for servo/gecko work but other users tend to be opinionated about these things and there's been complaints. |
A quarter-turn rotation I is a unit bivector. The most straight-forward convention when dealing with a designated orthonormal basis {ex, ey} is for positive to mean the bivector I = ex ∧ ey, irrespective of which way on screen ey is pointing. (Which is to say, behdad has it right here.) An angle measure or a winding number is inherently a bivector-valued quantity, θI, oriented like the plane of rotation. We exponentiate it to get a complex-valued rotation exp(θI) = cosh(θI) + sinh(θI) = cos(θ) + Isin(θ). This generalizes to quaternions, etc. (just replace I with an arbitrary bivector). It is only for historical reasons that we describe rotations and angles using a scalar angle measure, with the planar orientation stripped away.
In e.g. SVG it is very easy to apply a global flip to the y axis by attaching a transformation to a top-level group, whereupon everything should keep working as expected for mathy folks. When writing technical explanations of vector graphics concepts it is often a good idea IMO to work with a y-up coordinate system so that people familiar with math conventions won’t be confused, even though to achieve the same result a reader might need to apply a top-level transformation to their drawings. |
Add `as_coeffs` method to Affine so we can get the coefficients out (necessary for conversion to Direct2D types, etc). Also tweak documentation to explain the convention for rotation (partially addressing #4) and the augmented matrix representation of Affine, particularly that it's backwards from PostScript. This doesn't address all of #4, it doesn't change any behavior.
I do want to get this right, and think the above PR is a step in the right direction. Consensus on renaming |
This is the first time I'm seeing that terminology. Which is to say, it's probably not a good idea to use it. |
Reading more.. if you are returning a vector, I believe the |
@behdad agreed, also relevant - kvark/mint#32 |
MathWorld has this as cross product with the × notation. The sense I get is that the current naming is appropriate, but I could add proper bivector operations at some point. |
Correct. The two are related but separate concepts. |
Not a vector, a scalar https://github.com/linebender/kurbo/blob/master/src/vec2.rs#L29-L35 (You want to think about the sign of the value returned from It’s a purely conceptual thing. The scalar should be thought of as representing a plane-oriented magnitude instead of the magnitude of a vector perpendicular to the plane. In 2D, there’s not really such a thing as a “cross product” per se. Anyway, I don’t think it matters that particular projects use the concept of a “cross product”. There’s not even a problem with projects calling their 2-D wedge product “cross”, if they want. I’m just griping more generally because the whole STEM community would benefit from switching away from 3D cross products, which introduce the confusing notion of two “types” of vectors (“axial” and “polar”) which behave subtly differently where the differences must be minded and every vector operation means something different depending on which types of vectors it takes as input, &c. – and which don’t generalize at all to other dimensions. Once it is pointed out axial vectors are really just bivectors in disguise, a whole bunch of previously arbitrary seeming nonsense falls away. But this is all largely off topic in this context of a 2D project/discussion. |
I've opened a PR to the wiki with a file discussing this/giving an overview: linebender/wiki#2 |
Document sign conventions in Shape. Add tests to confirm this is indeed the behavior. Fix broken cases. Some doc touchup. Fixes #4
One of the current points of confusion is whether the coordinate system is y-up or y-down. The former is more conventional in math, the latter completely standard in graphics these days (though this was less true, PostScript was y-up, as are fonts to this day).
Related issues are conventions around whether a positive area shape is clockwise or withershins, and whether a clockwise circle has positive or negative signed curvature. The winding number should match area - it should be positive if the point is inside a positive area shape. Either convention will do, but it should be consistent.
Another thing that falls in the same category is the convention of
Vec2::cross
. The right hand rule says right ^ up = 1, but is up (0, 1) or (0, -1)? For this, I think we pick one and document it.I do think kurbo is interesting in both math and graphics contexts. For methods like
Vec2::from_angle
andAffine::rotate
it's hard to just pick a convention, so I'm inclined to split them into_y_up
and_y_down
variants, so that the user makes an explicit choice. For the signed area stuff, I'm not sure there's a strong convention; e^{iπt} is anticlockwise in a y-up coordinate space as t increases. Do people know of other conventions we can follow? Something along the lines of JuliaLang/julia#8750 which surveys rounding conventions of many languages might be a model.Tagging @behdad @jrus @Pomax @nical @Connicpu @kvark as they might have horses in this race.
The text was updated successfully, but these errors were encountered: