While working on my type family Elemaints, I have written several scripts that enhance the capabilities of FontForge. On https://github.com/linusromer/harmonize-tunnify-inflection I have published some of these tools, as you might find them useful too, if you are using FontForge.
it’s exciting to see another person coding plugins for FontForge! Honestly, I thought I was the only one for a while. None of mine are publicly available yet, though, so you’ve 1-upped me there ;-)
@Grzegorz Luk (gluk) Indeed, this is a cool trick, it makes the code much more compact. I have implemented the trick in the new version. Thank you for the hint!
If needed I could have placed those additional points of inflection without the need of this script.
In automated processes one will still need a script in order to add points of inflection.
That point on the right side of the shape really should be at the
extreme and not where it is at the moment of being "tunnified". Use your
eyes!
I have never claimed any of these contours to be perfect. The non-horizontal-or-vertical node on the dot contour should demonstrate that the tools will work for such nodes, too.
@ivan louette I think I have figured out what probably happend: You inserted an inflection point at the end of a contour. This led to a problem because the end node is identical with the start node. I have fixed this bug now. I hope it will work now for you.
@AbrahamLee Unfortunately, there are hardly any plugins of FontForge available. I have only found the plugin in the FontForge documentation and those of David Crossland (which were worth a lot for me because the documentation is so sparse). I am looking forward to the publication of your scripts.
@ivan louette According to the error message I assume you have downloaded the html-file instead of the raw file. The direct link to the file (without html view) is:
@AbrahamLee Unfortunately, there are hardly any plugins of FontForge available. I have only found the plugin in the FontForge documentation and those of David Crossland (which were worth a lot for me because the documentation is so sparse).
Absolutely true. This was a really frustrating realization I came to as well when there are so many awesome ones for Glyphs, Robofont, Fontlab, etc. Once I was able to really figure out how to do it (and it was very much NOT easy or obvious), I have had way too much fun creating useful plugins for all levels of font production/deveopment (i.e., font level, glyph level, contour level, etc.).
I am looking forward to the publication of your scripts.
Me too ;-) I don't have a specific plan for this, but I really do hope to some day.
@Stefan Peev Thanks for the note. This bug was due to the windows path "C:\Users..." in the docstring. Under Linux there were no complaints but under Windows "\U" obviously must not be contained in the docstring. I have fixed the bug and tested the new version with Windows (and it worked):
I was asked if my plugin could be part of the (future) contrib scripts of FontForge. Therefore, I had to change a few things and I will release the updated plugin under a new name (because the compatibility to FontForge2017 will be broken). However, I am not sure what name will fit the plugin. At the moment, I have chosen "Curvatura" because my tools are related to curvature and "Curvatura" is understandable in many languages and "Curvatura.py" is not as overused as "Curvature.py":
If you have a better name for the plugin, I would be glad to read it here.
@Jeremy Dooley Unfortunately, I do not own a copy of Fontlab 5, which is a bad precondition for testing and supporting. However, if someone wants to adapt the script: Feel free to use my script (under the conditions of the GPL). For all the actions in my plugin there are low level methods that can be used 1:1 for FontLab.
I created both balance and harmonize macros for Windows FontLab 5.2 a few months ago and just now published them to github. The functions for the macros are contained in an extension module which I wrote in Cython C++, which makes the macros perform much faster than if they were run as plain Python.
They were created out of the need to harmonize individual curve segments without changing anything not selected, something RMX has difficulties with.
The balance macro works the same way as the harmonize macro. This is a feature sadly missing from RMX.
So far this particular project is still unfinished, but outside of a some edge cases, it mostly works as expected.
@Jameson R Spires Laudable that you share your macros! I hope you do not mind, if I give you a hint: The harmonize algorithm of Simon Cozens works in most of the cases but depends on finding an intersection point, which may not exist. Therefore, I use a different algorithm (with the same results in generic cases) in the latest version of curvatura: (the code environment does not work on typedrawers.com, sorry)
# Returns the signed distance of the point p from the line # starting in q and going to r. The value is positive, iff # p is right from the line. def side(px,py,qx,qy,rx,ry): a, b = rx-qx, ry-qy return ((py-qy)*a-(px-qx)*b)/(a**2+b**2)**.5
# Given two adjacent cubic bezier curves (a,b), (c,d), (e,f), (g,h) # and (g,h), (i,j), (k,l), (m,n) that are smooth at (g,h) # this method calculates a new point (g,h) such that # the curves are G2-continuous in (g,h). def harmonize_cubic(a,b,c,d,e,f,g,h,i,j,k,l,m,n): if e==i and f==j: return g, h # no changes d2 = abs(side(c,d,e,f,i,j)) l2 = abs(side(k,l,e,f,i,j)) if d2 == l2: # then (g,h) is in mid between handles return .5*(e+i), .5*(f+j) t = (d2-(d2*l2)**.5)/(d2-l2) return (1-t)*e+t*i, (1-t)*f+t*j
<br>
This algorithm may be faster as well (but please check yourself). The mathematics behind are explained in https://github.com/linusromer/curvatura/blob/master/curvatura-doc.pdf. The idea behind the function `side()` is: length of the crossproduct over the length of the one vector equals the height of the spanned parallelogram.
I adapted your method above to the harmonize_curves() function in the extension module. It should be quicker due to considerably fewer calculations, but I did not measure it. Thanks for your help.
My pleasure! I see, that you have optimized abs(), which is absolutely correct in this case. I use side() for other functions as well and therefore side() does not include abs(). But after looking at your code, I think I should split side() in two functions harmonize_distance() and sideness() just as you.
Comments
In automated processes one will still need a script in order to add points of inflection.
I have never claimed any of these contours to be perfect. The non-horizontal-or-vertical node on the dot contour should demonstrate that the tools will work for such nodes, too.
@ivan louette I think I have figured out what probably happend: You inserted an inflection point at the end of a contour. This led to a problem because the end node is identical with the start node. I have fixed this bug now. I hope it will work now for you.
I use Windows 8.1 and the latest an official version of FontForge.
https://www.upwork.com/ab/applicants/1219390351307173888/job-details
https://typedrawers.com/discussion/comment/15766/#Comment_15766
Is this script still around? I've found your github repository, but I can't identify it. Much appreciated.
# starting in q and going to r. The value is positive, iff
# p is right from the line.
def side(px,py,qx,qy,rx,ry):
a, b = rx-qx, ry-qy
return ((py-qy)*a-(px-qx)*b)/(a**2+b**2)**.5
# Given two adjacent cubic bezier curves (a,b), (c,d), (e,f), (g,h)
# and (g,h), (i,j), (k,l), (m,n) that are smooth at (g,h)
# this method calculates a new point (g,h) such that
# the curves are G2-continuous in (g,h).
def harmonize_cubic(a,b,c,d,e,f,g,h,i,j,k,l,m,n):
if e==i and f==j:
return g, h # no changes
d2 = abs(side(c,d,e,f,i,j))
l2 = abs(side(k,l,e,f,i,j))
if d2 == l2: # then (g,h) is in mid between handles
return .5*(e+i), .5*(f+j)
t = (d2-(d2*l2)**.5)/(d2-l2)
return (1-t)*e+t*i, (1-t)*f+t*j
<br>
T
his algorithm may be faster as well (but please check yourself). The mathematics behind are explained in https://github.com/linusromer/curvatura/blob/master/curvatura-doc.pdf. The idea behind the function `side()` is: length of the crossproduct over the length of the one vector equals the height of the spanned parallelogram.