A (new?) algorithm to make two G¹–continuous cubic Bézier curves G³–continuous

Linus Romer
Linus Romer Posts: 187
edited February 2020 in Type Design Software
In the first section of this draft, I describe a formula to make two G¹–continuous cubic Bézier curves G³–continuous. Though the formula actually works for two G¹–continuous cubic Bézier segments only, it seems to work for more segments as well, if being iterated.
The formula (and its iteration) is implemented in the Curvatura script for FontForge. Feel free to try it.

Is this algorithm already known?
(As far as I can see, the RMX tools have an algorithm to make curves G³–continuous as well (called supersmooth), but my algorithm is probably different: "supersmooth" seems to move the node perpendicular to its handles, my algorithm scales the handles.)
«1

Comments

  • Linus Romer
    Linus Romer Posts: 187
    edited February 2020
    The following image tries to visualize the algorithm "Make G³–continuous" by a comparison with "Harmonize". To the left the glyphs are harmonized, to the right the glyphs are additionally made G³-continuous:

    Hence, the handles have been scaled in order to make the derivative of the curvature continuous, too. The last example also shows a case where the algorithm does not work as intended: The tunni ratio of some handles may become more than 1 and additional inflection points may occur.
  • I confess I don't get the maths, so this may be a silly question, but can you explain, in as non-mathsy terms as possible, why G3 continuity matters?

    I always thought harmonized is harmonized (of course, how harmony is achieved may differ), but now it turns out there are all sorts of harmonized.
  • Chris Lozos
    Chris Lozos Posts: 1,458
    I use RMX to check my curves with a copy on the mask layer to see changes.  I often concur with what RMX does but not always.  Often, RMX will make no change at all.  This gives me visual control.  What is the benefit of using your formula above my method? I hastily admit that I am a visual person and not a math person.
  • To my eye, the overall shape in your illustrations above "harmonized=G2-continuous" appears more pleasing then "G3-continuous". Not sure what advantage your new algorithm achieves. I agree with Chris Lozos.
  • Linus Romer
    Linus Romer Posts: 187
    edited February 2020
    @Chris Lozos Just as you work with RMX you can work with "Make G3 continuous": Look if the curve is more pleasing after applying it; if not then undo it.

    @Alex Kaczun The examples have been chosen to show how the algorithm works. They are not particulary aesthetic examples. I will try to show how "Make G3 continuous" could be part of your workflow:

    1. Given a sketch of a hook (left), you apply "Harmonize" (mid) and you think that this will move the node too much to the right. So you actually you do not want to move the node but the handles and therefore you undo "Harmonize" and apply "Make G3 continuous" (right):



    2. You have a sketch of an "n":
    Now you apply "Make G3 continuous": (the gif shows original to G3)

    As expected, only the handles moved and not the nodes. Some parts seem to be more pleasing, some are worse... So you undo it and apply "Harmonize": (the gif shows original to harmonized)
    As expected, only the nodes moved and not the handles. Curiously you additionally apply "Make G3 continuous": (the gif shows original to harmonized and made g3)

    Now you have many possibilities to choose from. You will take some parts from the one variant, some from the other and you let some parts untouched.
  • Could you show an example that is very clearly not in 'harmony' (basically just poorly drawn), and then apply (1) your algorithm vs (2) RMX Harmonizer?

    I have a feeling that moving the nodes rather than the handles preserves more of the intended shape.


  • The problem is that if your node is well-positioned - making a stem width or overshoot point - you don’t want the node moved. I would say that nodes are “more designed” than handles.
  • @Jasper de Waard: The left arch is clearly not in 'harmony' (basically just poorly drawn), and then I have applied "Harmonize" (mid) vs "Make G3 continuous" (right):


    Of course, you may prefer to keep the position of a3 because it's the on-curve point. Fine. Instead, compute where a3 should go according to this algorithm, work out the delta between the new position and the current position, and apply that delta to the handles a2 and b1 instead.
    This quote from https://gist.github.com/simoncozens/3c5d304ae2c14894393c6284df91be5b was one of the "seeds" for my algorithm.
  • Thanks for the example! I still think it could go a lot more extreme, though. I can see, visually, that G3 continuous is indeed very 'continuous', which is cool. I just wonder how strongly it changes the outlines in comparison to G2 continuity.

    Some more silly questions: do you think it would be difficult/possible to
    1. Port this functionality to Glyphs app?
    2. Give users a choice: adjust handles, adjust nodes, or say 50/50?
  • @Jasper de Waard I see "Make G3 continuous" as a complement to "Harmonize": In some situations it may be better to move the node and in others better to move the handles. However, moving the handles is much more problematic, because new inflection points may occur or the curvature could become infinitely large. Hence, "Harmonize" can be applied without thinking too much, but "Make G3 continuous" should be applied with more care.

    1. Yes, it should not be too problematic to write a Glyphs plugin with the same functionality. The math is open, the implementation under GPL. As I don't have Glyphs, someone else would have to do this.

    2. You already have the choice: "Harmonize" will move the node, "Make G3 continuous" will move the handles. Mathematically speaking, going from G1 to G2 means 1 additional constraint (like moving the node), going from G1 to G3 means 2 additional constraints (like moving the two handles). Therefore, in generic cases the best you can get by moving the node ist G2. 50/50 does not make sense in a mathematical point of view, but one can easily implement a mixture of "Harmonize" and "Make G3 continuous".

  • I see, that makes sense. Thanks for taking the time to explain this :)
  • So, why not create the best possible shape as indicated on the extreme right side of your example—visually by eye...


    This was created with 2 vector points like this...


    Then, using "Nodes at Extremes" to create my final example above. Like this...


    Tools and advanced algorithms are a help but no substitute for skill and developing a good eye. But, everyone is entitled to their opinion and methods. Your "G3-continuous" algorithm is an improvement. I commend you on your efforts. Someday, sorry to say, machines and automated programs will indeed replace the artist and the craftsman.
  • Chris Lozos
    Chris Lozos Posts: 1,458
    I also do a lot of curves using this technique.
  • So do I. However, in many cases you are given a tangent constraint (like reaching a certain height) and fiddling the handles to meet this constraint is difficult:
    Another aspect: Your method has exactely one solution for a given tangent point and given directions of the handles. "Make G3 continuous" allows more (actually infinitely many) solutions for a given tangent point and given directions of the handles:

    Therefore, "Make G3 continuous" gives you more freedom.
  • Adam Twardoch
    Adam Twardoch Posts: 515
    edited February 2020
    @Linus Romer 

    thank you very much for publishing openly available math alongside your Curvatura code. At FontLab, we’ve made a successful test implementation of your “Make G3 continuous” method in FontLab 7. The app already has included Harmonize, Balance aka Tunnify, and adding inflections, so G3 was a useful addition. 

    Since our Harmonize (G2) implementation only moves the node and keeps the handles in place (which allows us to have the dynamic G2 node aka Genius node), I realized that we can add specificity to Harmonize by calling it “Harmonize Nodes”, and then add the second operation (your Make G3 continuous), which we called “Harmonize Handles”, since it actually keeps the node position and only moves the handles. 

    - As previously, you can Harmonize a Smooth node with Alt+2xclick to make it G2, or convert it to Genius (dynamic G2) with Shift+Alt+2xclick
    - You can now Harmonize Handles with Ctrl+Alt+2xclicking a Smooth or Genius node to make it G3

    The new Harmonize Handles operation is available in the beta version of FontLab 7.1.0 that we just released. You can download the beta at https://download.fontlab.com/ — it’s available as a 30-day trial or with your FontLab 7 license, for macOS, Windows (32- and 64-bit). The 32-bit Windows version is said to work well under Linux with Wine. 

    I’ve described Harmonize Handles in the FontLab 7.1.0 beta release notes: 
    https://help.fontlab.com/fontlab/7/manual/Release-Notes-Beta/#harmonize-handles

    I have found that Harmonize Node is a very neat way to get “some sort of reasonable defaults” for the handle configuration of a smooth node. Not universal and good for everything, but decent as quick defaults. This actually plays nicely with the Alt+2xclicking a Sharp node operation, which in FontLab 7 “normalizes a corner” i.e. makes reasonable defaults for a sharp node. 

    I can’t wait what others will think of it! Many thanks :smile:

    Adam
  • Christian Thalmann
    Christian Thalmann Posts: 1,988
    edited February 2020
    I agree with Alex that the G2 looks better in this example, whereas G3 looks better in this example.
    I suspect that's because, as Alex pointed out, the second example is «trying» to be a single Bézier arc, whereas the first clearly has substructure. G3 will try to pave out the substructure to make a single motion, which can be counterproductive.
    (I actually sometimes delete the intermediate point to get a smoother result in the case where I want to have a single motion across two Bézier segments; that seems to fit this realization.)
  • Adam Twardoch
    Adam Twardoch Posts: 515
    edited February 2020
    I've made some examples using the new FL7.1 beta. Of course we know that in those situations, there are other ways to solve this, but it shows it well, with the visualization of the Curvature comb turned on.

    G0 continuity (Sharp nodes):



    G1 continuity (Smooth nodes):


    G2 continuity (Smooth nodes with Harmonize Nodes applied, or Genius nodes):


    G3 continuity (Smooth nodes with Harmonize Handles applied):


    This gives you the idea. 

    In my view, Harmonize Handles is useful, but as Linus said, you need to be more aware of its limitations, so in FontLab 7, we’re only exposing it in the UI in a limited fashion (for example, there is no way to apply this to all glyphs, as it would not be advisable to do this thing "blind").
  • Adam Twardoch
    Adam Twardoch Posts: 515
    edited February 2020
    Christian, 

    I agree, G3 "removes" any "visual structure" along a longer Bezier spline. In a single-master design, you may just as well remove that kind of node. 

    Harmonize Handles (G3) may be useful if you decide to keep a node which you want to be super-smooth (“silent” or “super-harmonic”) in one master, but then you need it to be "functional" (“have an individual voice”) in another master for whatever reason. 
  • Linus Romer
    Linus Romer Posts: 187
    edited June 2020
    In my FontForge plugin Curvatura, I have ditched the «Make G³-Continuous» algorithm in favour of a «Harmonize Handles» algorithm which ensures G²-continuity only but is more flexible (i.e. it has solutions for nearly any reasonable case).

    (The joint node remains fixed and its attached handles are scaled such that the curvature in the joint becomes the average of the previous curvatures. The curvatures of the neighbour nodes are maintained. See the documentation for further details.)

    However, if G³-continuity can be gained without exceeding the tangent triangle, it may still be the superior solution.


  • Is it possible to make a quadratic version of it?
  • Linus Romer
    Linus Romer Posts: 187
    @Piotr Grochowski There is no quadratic version of this specific algorithm because scaling handles has no meaning in quadratic Béziers. However, the «normal» harmonize tool, which moves the joint nodes, supports quadratic Béziers:

  • @Piotr Grochowski There is no quadratic version of this specific algorithm because scaling handles has no meaning in quadratic Béziers. However, the «normal» harmonize tool, which moves the joint nodes, supports quadratic Béziers:

    But how would it work if the on-curve point isn't defined but is implied by interpolation?
  • Is it possible to make a version of a harmonize tool that works on bitmap fonts?
  • Simon Cozens
    Simon Cozens Posts: 752
    I don't think it is. What would it harmonize?

    I suppose you could trace the bitmaps to curves, then harmonize them, then re-rasterize them. But my suspicion is that in general the autotraced curves would be very yucky and harmonizing wouldn't work on them.
  • Again, how would a quadratic harmonizer work if the on-curve point isn't defined but is implied by interpolation?
  • Thomas Phinney
    Thomas Phinney Posts: 2,901
    It couldn’t make any effective change without adding an on-curve point.

    I haven’t looked at this aspect of TT quadratics to know, but it is at least possible that TrueType is (usually? always?) G³-continuous already, in those cases when the on-curve point is merely implied. Probably not, though, as it would involve a bit more processing from the rasterizer to figure out the implied point position. The design of TrueType is all about saving processor power, even when at the cost of needing more data.
  • It couldn’t make any effective change without adding an on-curve point.

    I haven’t looked at this aspect of TT quadratics to know, but it is at least possible that TrueType is (usually? always?) G³-continuous already, in those cases when the on-curve point is merely implied. Probably not, though, as it would involve a bit more processing from the rasterizer to figure out the implied point position. The design of TrueType is all about saving processor power, even when at the cost of needing more data.
    When there isn't a defined on-curve point between two off-curve points, it is implied to be the linear average of the two.
  • @Adam Twardoch, I am using FL 7.1.4.7515 on a Mac and can't seem to get "Harmonize Handles" to work. The release notes only make mention of the functionality, but don't give instructions. Could you enlighten me, please? I'm eager to try this out. 

    Thanks! 

  • Got it: Shift + Option + Doubleclick