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

Add a transform() method to Path and segments #101

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

regebro
Copy link
Owner

@regebro regebro commented May 1, 2023

No description provided.

@regebro regebro force-pushed the transform-support branch 2 times, most recently from 8920910 to c119d0f Compare May 1, 2023 19:06
@regebro
Copy link
Owner Author

regebro commented May 2, 2023

Skews don't work right on curves yet.

@tatarize
Copy link
Contributor

tatarize commented May 2, 2023

This won't work on arcs. It's simply not enough to convert the arc start and end points. If you, for example apply a scale by -1 you must invert the sweep flag, a bit of a rotation would require that you actually change the rotation bit but only if the arc is sort of non-circular.

I don't even think I got arcs that work correctly with skews. Since I opted for an internal parameterization of start-point, end-point, x-radius point, y-radius point and center point. SVG arcs are sort of terrible. It's why when I make my own geometric classes the arc is circular and the parameterization is start, end and control. All the other segments are parameterized entirely with just points so they'd convert fairly easily.

@regebro
Copy link
Owner Author

regebro commented May 2, 2023

Good point about the sweep flag, I hadn't thought about that. I think arcs might have to be beziers when skewing, but I haven't looked at getting beziers right yet.

@tatarize
Copy link
Contributor

tatarize commented May 3, 2023

I don't really see how you re-parameterize it afterwards. You could certainly replace the arcs with cubic or quads and those would skew perfectly fine. I think you actually need to solve it as a pretty large and complex equation. Given these values what does this matrix do to each of these values. My solution of just turning rx, ry center, start and end into points doesn't work. I think because skews change the shape differently than the points. (though it works for everything else, translate, scalex, scaley, rotation, and lets you calculate the rotation, large-arc, sweep, rx, ry, from the points)
It's fueled my hate for SVG arcs ever since meerk40t/svgelements#47.

(https://www.w3.org/TR/SVG2/implnote.html#ArcParameterizationAlternatives)

I think you might need to basically convert the formula with regards to t for the entire svg arc. Multiply it by your particular matrix and them simplify it back to arc values perhaps with values a-i inserted in there. For example, the sweep is the old sweep times the sign of the determinant of the matrix, so like sign(a * d - c * b) (Using an SVG 2x3 style matrix). The start and start and end points should be 1:1 with their multiplied variations, but rotations very well may be another giant wrench in the whole affair.

I believe it may actually be akin to:
https://math.stackexchange.com/a/3884655/217855

Which is a very useful set of equations to do 3x3 matrix perspective warping mapping 4 points to another 4 points. Which was done using lean. But, in theory you need to know how the matrix affects each value of the parameterization.

@regebro
Copy link
Owner Author

regebro commented May 3, 2023

Yeah, you are right, I drew out some skewed shapes yesterday and I thought the beziers looked bad, but now I have written tests that confirm them point by point, and they are actually fine, it's only the arcs that are the problem.

I guess the nicest would be to somehow apply the matrix in a way that changes the ellipticity (is that a word) and rotates them to make a skew. But I'm not 100% sure that would work, and yeah, the easiest is probably just to convert the arcs to beziers and skew later. I'll think about that after some more coffee.

@regebro
Copy link
Owner Author

regebro commented May 3, 2023

@regebro
Copy link
Owner Author

regebro commented May 3, 2023

image

Going forward

@tatarize
Copy link
Contributor

tatarize commented May 4, 2023

Yeah, I would prefer source code in Klingon to the answer in mathy-english. What's annoying is the person answering that question could probably write the five lines of code it would take. But, convincing them to help make a computer do a practical thing takes a distant 2nd fiddle to explaining the obviousness of the conjectures involved. I've gone pretty deep into learning some leanprover code since I've seen it actually come up with useful and implementable equations. But, I daresay there might not be a full set of solutions to this with a 9x9 matrix since you'll have g and h which convey perspective if non-zero.

perspective

Here's an ellipse with a skew applied and a perspective applied to that (inkscape, skew, object to path, path effects, perspective). As you obviously guess there's no reified parameterizations that could give you a 1:1 result with that now fairly warped object. You could also clearly get some sorts of teardrop shapes with fatter bottoms and and narrower tops. I guess the perspective of that would be like an ellipse viewed converging into the horizon. Space gets weird without parallel lines. And while I strongly prefer your matrix type (I'm making my own geometry classes and will certainly be using 3x3 matrixes for them, but I certainly won't be using SVG's arcs).

There should entirely be a set of equations that explains what happens to each of the arc parameters when you feed the arc into an affine matrix.

Start: transformed.
RX:
RY:
Rotation:
Sweep: bool(sign(matrix.determinate))
Large-Arc: abs(sweep_amount) > tau / 2
End: transformed

rx and ry are also made a bit strange here. Obviously if you applied this transformation they could be (in theory) the distance from the center to the point on the circle at angle of rotation. And same for ry being the position of the point on the ellipse at the angle of rotation + tau / 4. But, I could easily see this definition being generally wrong and leading to errors. Afterall, I did this for svgelements and it deals with sheers wrongly.

@regebro
Copy link
Owner Author

regebro commented May 4, 2023

There's no g and h in SVG, though, so I don't need to implement them, luckily. If the interface doesn't return a new PathSegment, but a list of new PathSegments, that's at least theoretically doable. But I doubt very many people are going to use this transform function at all. I basically just want to close the two last tickets. :-D

I'm not sure if there is an order defined for the functions, when you implement a matrix, though. A shear and then rotate would be different from a rotate and then a shear... I'll have to read through the SVG spec to see if there's an order defined.

@regebro regebro force-pushed the transform-support branch 2 times, most recently from 8704615 to c8e4a18 Compare May 5, 2023 07:16
@tatarize
Copy link
Contributor

tatarize commented May 7, 2023

Okay, yes. If somebody calls shear/rotate for your arc, return some bezier curves for that. That actually sounds approximately great.

The order is pre-cat in the list. So "translate(20, 20) rotate(5)". Though I guess equals "Matrix(0.996194698092, 0.087155742748, -0.087155742748, 0.996194698092, 20, 20)" and not "Matrix(0.996194698092, 0.087155742748, -0.087155742748, 0.996194698092, 18.180779106882, 21.667008816788)" is the safest way to put that.

> from svgelements import *
> Matrix("rotate(5)") * Matrix("translate(20,20)") == Matrix("translate(20, 20) rotate(5)")
True
> m = Matrix("translate(20, 20) rotate(5)")
> m = Matrix()
> m.pre_translate(20,20)
> m.pre_rotate(Angle.degrees(5))
> m == "translate(20, 20) rotate(5)"
True
> Matrix() * "translate(20,20)" * "rotate(5)" == "translate(20,20) rotate(5)"
False

Or at least that's what was needed to pass the test my

Basically <transform> * Matrix each time you get a new thing applied. It goes to the end rather than the beginning. There's a bunch of SVG tests for this.

But you're probably good if you do matrix parsing tests. And ensure that the orders match.

WC3:

coords-trans-01-b

coords-trans-01-b

@regebro
Copy link
Owner Author

regebro commented May 25, 2023

Well, suddenly while testing an x-skew and a y-skew created a rotation, but the wrong direction, so yeah, mine brain is definitely struggling. But it's going forward.

@regebro regebro force-pushed the transform-support branch 2 times, most recently from ac7aa53 to c34d5c9 Compare May 25, 2023 18:32
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

Successfully merging this pull request may close these issues.

2 participants