Just noticed that in Glyphs, it’s possible to use cubic and quadratic Bézier curves in one path. Nice! Feels like FontLab in the late 90s. It takes a bit of trickery like cutting and re-joining paths but it seems to work.
Is it okay to do so? Or are there any risks? Is it too hacky?
I feel that for simple, shallow curves, this is the perfect solution, as two cubic handles give unnecessary (and unwanted) control. Retracted cubic BCPs – another long-time favourite of mine even though advised against by some – generally give shallower and more asymmetrical curves than a single-BCP quadratic curve.
I was not even thinking of mixing them in the final font file – of course, that is an interesting long-term goal. Currently, Glyphs simply converts them to one or the other on export anyway.
The question is, just as Georg mentioned, that there might be some code in Glyphs that malfunctions when it encounters mixed paths. This is what I had in mind when I wrote, “is it safe?”
Why is it easy to test, @GeorgSeifert? I tried exporting OTFs, which works perfectly fine, but there is some uncertainty left, some potential for errors that go unnoticed.
Anyway, I also wonder if that might affect quite a lot of plugins, which operates with paths. You could see with your own ones (Symmetrify, Harmonizer, RMX, …). I’d be curious about that too.
Just wrote a small script that helps play around with quadratic curves:
Two things I noticed:
On export, Glyphs’ overlap removal fails if cubic and quadratic curves are mixed. I tried all possible combinations for the path directions; none of them worked. Maybe this could be fixed by changing the order in which the paths are processed (curve conversion vs overlap removal), or skipping an incorrect change of path direction?
When using “Convert to Cubic” from the menu, Glyphs likes to duplicate the starting node. Seems to be a little bug.
The overlap removal code doesn’t handle quadratic curves at all. because intersecting quadratic curves is quite tricky. In the last few month I wrote some code that might help with this, but I didn’t integrate it with the remove overlap code, yet.
If the font is exported as OTF then all curves are converted to cubic in any case. If you do this before you apply the overlap removal this should fix the problem. No need to support overlap removal with quadratic curves.
I just managed to fix the overlap removal on OTF export by setting up a very simple PreFilter that calls path.convertToCubic() for all paths in the font.
This means this problem should be fairly easy to fix in Glyphs internally. Just convert the paths to cubic before you apply overlap removal.
Seems like using manual PS hints, and then exporting as CFF, messes up the node indices as the curves are converted to cubic. (Strictly speaking, this is not due to the mixing of cubic and quadratic, of course).
Another bug: Selecting part of the contour, then Cmd+X, flattens all quadratic curves. Interestingly, the knife tool works flawlessly, but deleting points from open contours will also flatten the quadratic curves. Deleting nodes from closed contours works as expected, though.
What’s the current state of mixing types in one path in a font by now?
I need to work with some quadratics due to the design space I got and wonder if I can turn to just using quads in certain places where I need them, instead of converting the entire glyph into quads.
Once you start getting used to it, using superfluous BCPs feels really silly.
As a general rule, as we all know, one would always try to use as many points (on-curve and off-curve) as necessary, but not more. To me, deleting an unnecessary BCP now feels as natural as deleting an unnecessary on-curve point. I have a shortcut, of course, and it has become an everyday part of working on the contours.
It gets tricky when working with overlapping paths. Glyphs struggles with this, as does fonttools. Overlap removal doesn’t work when two paths overlap of which one is quadratic and one is cubic.
Sorry, I forgot to tell you about my custom filters.
(1) For Variable fonts, you can keep the overlaps.
(2) For static OTFs, I have a simple filter that does this:
def filter(self, layer, inEditView, customParameters):
layer.saveHints()
for path in layer.paths:
path.convertToCubic()
layer.restoreHints()
It needs to be set as a PreFilter, then the automatic overlap removal works.
(3) For static TTFs, I have a filter like this:
def filter(self, layer, inEditView, customParameters):
if not anyCurves(layer):
return
layer.parent.parent.grid = 0
for path in layer.paths:
path.convertToCubic()
layer.removeOverlap()
for path in layer.paths:
path.convertToQuadratic()
for path in layer.paths:
for i in range(len(path.nodes),-1,-1):
node = path.nodes[i]
if isDeletableQuadNode(node):
path.nodes.remove(node)
layer.parent.parent.grid = 1
# seems like we need to clean up paths twice
# to remove all double nodes:
layer.cleanUpPaths()
layer.roundCoordinates()
layer.cleanUpPaths()
I use this as a filter (not PreFilter). Many of the quadratic curves will simply be converted to cubic and back to quadratic, without any change (i.e. when there is no overlap). This is a bit unelegant but at least it is really 100% reliable at restoring the original points. Btw, this filter includes removal of unnecessary on-curve points (some code not shown here).
Don’t ask me why one of them needs saveHints() and restoreHints() while the other one doesn’t. This is the result of lots of tedious trial and error.