-
Notifications
You must be signed in to change notification settings - Fork 25
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
Output Bézier splines not deterministic enough #26
Comments
Hi Fredrick, Likewise, we may think of 0,0 as point of origin, but in terms of variable fonts, you really should think of the center of the char as 0,0 and everything stretches proportionally from the centre of the char (towards the left and towards the right). In terms of maintaining the same number of curves when drawing with libspiro, you should consider having 2 grids. Let's say 1000x1000 as a "fixed" value for libspiro calculations, and a second grid, which is "variable" so the user can see the changes as X slides in or out....so if you shrink to 800x x 1000y, then you multiply all x values by 4/5 from the center of the char, or stretch to 1200x x 1000y, then you recalc display values by 6/5 from the center of the char. This is not important to the question, but more of an FYI, the older libspiro calculations are based on 0,0, while the "2" functions are based on Xcenter/Ycenter ....but this should have nothing to do with the problem. Just keep a fixed grid for calculating spiros, and translate to a variable grid for displaying your variable font. Hope this helps |
@JoesCat When I say "variable fonts", I don't mean a custom rasterizer which would support Spiro, or which would interpolate Spiro points, but rather OpenType 1.8 Font Variations, which only can be drawn using quadratic or cubic Bézier splines. So, we would have two slightly different Spiro splines, and the hope is, we get quadratic/cubic splines that are interpolation-compatible. |
Having thought about related issues I continue to think that there's no need for a tool specific to Spiro or Expand Stroke to support variable fonts. What is needed is a tool that can take two or more smooth (sub-)contours (occasionally closed (as in the middle of an "o") but usually open (as in the outside of an "e")) as input and outputs corresponding (sub-)contours with the same endpoints but having interpolation-compatible points and close to the same geometry. If you have that you can post-process the output of other tools to generate interpolable masters. Given that in practice a (sub-)contour will be G1 smooth rather than C1 smooth (and often won't be G2 smooth) you can't just reparameterize to get the desired number of points. Therefore a sophisticated fitter is what is needed; one where you can influence the number of points fit and where some of them go. Ideally such a tool could also do something at least reasonable about geometric matches (such as when both (sub-)contours have an inflection point) and mis-matches (such as when only one does). I had thought a bit about this before starting the stroke code and went down a large and ultimately unnecessary rabbit hole during its implementation trying to determine the best fit algorithm for these purposes. It doesn't appear that there is a best one -- it might be better to have a "toolbox" and try multiple approaches with multiple starting parameters, sometimes selected randomly. It would also be nice if such a tool could do a better job at the Bezier->Spiro direction, so there can be fewer points when transitioning back. |
Thanks @skef. If you say it's impossible I trust your judgment, you're the math wiz around here. 🧮 (By the way, I might not be pushing round 2 of fontforge/fontforge#4321 tonight, sorry about that. I have been working on it but have gotten bogged down in some part of it. Hopefully @davelab6 doesn't kill me.) |
Although, maybe, for my sake, if you feel like educating the simple, you can explain why it's impossible to go segment-by-segment in a Spiro spline and come up with an approximation using n curve points of the normal Bézier conversion of that segment? |
The argument isn't that it's impossible it's that I don't see what a Spiro-specific tool would add.
Here is a simple curve-fitting heuristic that could be applied to the variable font problem: Take each corresponding sub-contour and fit it with successive point counts until you're under an error threshold. Then take the max needed point count and refit the other ones to get the same number of points. This heuristic will fail in any number of circumstances but it's not a bad starting point. Now:
|
Yes @skef you're right, I was already taking it for granted that we'd need a new Spiro conversion function which takes two Spiros, which would need to be checked for "Spiro compatibility" (essentially the same thing as cubic/quadratic compatibility, but marginally simpler with Spiro as all points are on-curve). Then, we'd call this function in e.g. Definitely we cannot just go with a high n. So really the only difference between the "raw" curve fitting and the "double" curve fitting is that whatever segment needs the most points, the other segment will get that many too. |
Except that in either case you're not fitting to FontForge points but to either the geometry of the curve as a whole (less likely) or to points sampled along it. Sampling with "native Spiro" vs Bezier isn't likely to make a substantial difference. Maybe Spiro makes uniform sampling easier (I haven't looked into it) but you probably only need approximately uniform sampling anyway. I looked into direct geometric fitting vs sampled point fitting and the former seems really tricky and potentially expensive because you need to compute the shortest distance between to curves while still maintaining some proportionality along the curves. Sampling the source curve with points brings you back into the ordinary world of curve fitting. |
Incidentally Spiro point interpolation is easy: Just do it the same way as you would a normal transformation. However, given that linear transformations on Spiro points aren't affine there is no clear relation between Spiro point interpolation and true variable fonts. One could interpolate between Spiro masters for inspiration or as a potential source for more masters but the "end user" can't do so. |
Thanks for the external links - weight and width. |
@JoesCat As @ctrlcctrlv has noted, although variable fonts often support width and weight changes at the high level that goal doesn't have any direct analogue in the implementation. The nuts and bolts of variable fonts are implemented by linearly interpolating the corresponding Bezier points (on-curve and control) of "master" contours. When there is a "weight" knob it is because the designer has included "thick" and "thin" masters that interpolate cleanly. The knob setting determines the degree of interpolation. |
ok - going back-n-forth between all the info in this thread... In terms of reliably maintaining 'n' bezier curves, you can also break a curve into segments using '[]' or 'ah', but then this sort of defeats libspiro calculating best fit for you because now you force angles. okay, back to interpolation....walking along the curve...see if this thought helps much...
...now, going back to @ctrlcctrlv reference at OP... for 2 curves, and @skef 's toolbox
QUAD0 is pretty-much a first attempt. If you got plenty of bend, a quad can sort of be close to a cubic, but the flatter the cubic gets, the easier it is to see that you need more quads to represent the cubic (2 won't be enough unless you're willing to live with the error). If there is going to be a kink in the curve, I would expect it to show itself between these two Quads since it's a midpoint of the cubic, and angles weren't calculated here, a better compromise is going to have to be something like a straight line going through this point, or breaking-up the curve further. |
@JoesCat Just to clarify the term (linear) "interpolation" is used in its ordinary sense. Say you have two reference numbers: 20 and 40. 30 is 50% of the way from the former to the latter, 35 is 75%, and so on. If you have points (20,20) and (40,40) then it's similarly (30,30) and (35,35). As you turn the knob on a variable font all that's going on underneath is that the corresponding points in the corresponding contours of each master are being varied in this way. The problem of making a variable font is therefore the problem of having a good result under this simple operation. For corresponding "smooth curvature" portions of a contour that typically means having the same number of points in that section of the contour spaced roughly the same way. |
Thanks @skef for the interpolation clarifications. I looked at this https://docs.microsoft.com/en-us/typography/opentype/font-variations which references a summary here https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369 What I think may seem to be confusion between us, is @ctrlcctrlv started with spiro points. Where you came into this conversation seems to be a couple steps into sampling and curve fitting. The toolbox is good - you really don't want to be importing all this complexity into spiro - it doesn't belong in that library, but I think the question was more of spiro->n_beziers->N_points. The solution was, keep spiro X and Y dimensions fixed, and use a different grid to work on variances. The curve fitting is good, but it's more in the direction of curve->n_beziers->N_points. Toolbox location....probably would fit best somewhere around the n_beziers point. |
Fascinating discussion. I'm also interested in using spiro for variable fonts, but had basically given up: matching bezier output points sounds like a great solution tho'. |
Hello @JoesCat, good day.
A problem I've been thinking about for a while is how we can increase the determinism† of the output Béziers from libspiro, at the cost of increased path complexity, and, perhaps, even some mathematical accuracy.
This would make it much easier for Spiro splines to be used in variable fonts, or in other cases where we'd want interpolation. Right now, Spiro users such as me usually have to redraw one or both splines to create an interpolation-compatible spline.
It's clearly quite the challenge. Before I give you my thoughts, what are yours?
† The type of determinism I'm specifically referring to is determinism in the number of points. So, for two Spiro splines with n points, assuming all point types are equal, and both splines have the same openness/closedness, we always get back a Bézier spline with N points, no matter the configuration of the Spiro points.
The text was updated successfully, but these errors were encountered: