Clean curve/remove points



  • Hi there,
    my outlines have too many points due to how I generate the glyphs (via Phython/mathematical functions). Therefore I have to do some “cleaning” before interpolation.
    In FontLab7 I found the comment “Contour > clean up” which does what I want (see two screenshots below), but I would like to avoid jumping cross programs as I do everything else in RoboFont.
    However, I can't find anything in RF doing something similar. Any help appreciated!
    Thanks in advance,
    Petra

    Screenshot before:
    RemovePoints_1.jpg
    Screenshot after:
    RemovePoints_2.jpg


  • admin

    There is not such a feature in RoboFont, yet

    RoboFont does recurve as much as possible when deleting a point.

    So that means all the building blocks are there to make a clean up tool.

    the plan:

    • make a copy of the glyph
    • delete one point in the copy
    • check if curve is almost the same as the original, maybe with an controlled error value
    • continue doing this until you can not delete points anymore or when all points are required to draw the shape

    good luck!!!



  • Thank you Frederik.
    The thing is that we're talking about a few hundred glyphs – which get generated on one click. So deleting all the points in each glyph cannot be done over and over again. (My font file is basically a Pyhthon script which gets generated on need.)
    Worst case I have to import the file in FontLab, but I was hoping to be able to avoid that.


  • admin

    I was not talking about doing it manually :)

    Just wrote it down in pseudo code how to solve this. I dont have much time left. Not my intention to scare you, more to trigger you or someone else to write this little script :)



  • Ahh, ok, now I got it. But I suspect my coding skills are not good enough yet to translate this into code. As my question is not urgent, I will try it myself first anyway and see where I get. I will post whatever solution I come up with.
    Cheers,
    P



  • For what it’s worth, here’s how I tend to approach the problem in a non-code way:

    1. Copy the contour and then paste it to a background layer. Make sure this layer is visible, via the Inspector.
    2. Select a point that isn’t needed, and hit delete. Adjust the path as needed to match the background contour as much as you want, while considering whether you can improve upon that.
    3. Repeat!

    If you had a bunch of glyphs to clean up, this would obviously be a hassle. But, often, it is probably better to have precise control over exactly how you are simplifying paths. It will result in cleaner curves, and helps you be careful to consider interpolation compatibility as you go (if needed).



  • Thank you, ArrowType.
    Your described process is how I was approaching the issue until now – I have tried a few glyphs manually, to test. But now I am in the phase of my project where I have to switch to “mass production”, otherwise my whole approach wouldn't make sense. (It's a student project where I rebuild type which was defined by a mathematical system from a writing master of the 18th century.)



  • Apologies if I’m misunderstanding the question, but I was recently playing around with some code to something similar — in my case, to iterate over all the glyphs in a font, and then automatically delete any non-extrema points (and from your illustration, that looks like what you’re trying to do as well).

    It works in my case because the the non-extrema points are usually extraneous, so the RoboFont point-deletion recurve algorithm "solves" the deletion correctly.

    No error checking or anything like that, as Frederik suggested, but hopefully it might still help you.

    for glyph in CurrentFont():
        glyph.deselect()
        for contour in glyph.contours:
            for bp in contour.bPoints:
                ix, iy = bp.bcpIn
                ox, oy = bp.bcpOut
                if bp.type == "corner":
                    continue
                if abs(ox-ix) > 5 and abs(oy-iy) > 5:
                    bp.selected = True
    
        if True:
            glyph.removeSelection(
                removePoints=True,
                removeComponents=False,
                removeAnchors=False,
                removeImages=False)
    


  • Thank you Rob for all the effort you made (in the background) to help me and solve my problem. Highly appreciated!
    I will post the final code here:

    import math
    
    def calc_vector(point1, point2):
        x1, y1 = point1
        x2, y2 = point2
        dx = x2 - x1
        dy = y2 - y1
        return dx, dy
    
    def calc_angle(point1, point2):
        dx, dy = calc_vector(point1, point2)
        return math.degrees(math.atan2(dy, dx))
    
    def bp_angles(bp):
        ax, ay = bp.anchor
        ix, iy = bp.bcpIn
        ox, oy = bp.bcpOut
        in_angle = calc_angle((ix+ax, iy+ay), bp.anchor)
        out_angle = calc_angle(bp.anchor, (ox+ax, oy+ay))
        diff = abs(in_angle - out_angle)
        return in_angle, out_angle, diff
    
    def select_extra_points(glyph, remove=False):
        if False: # flip to True if you want to select a point and see it’s angles printed, useful for debugging
            for bp in glyph.selectedBPoints:
                in_angle, out_angle, diff = bp_angles(bp)
                print("----------", in_angle, out_angle, diff)
        else:
            glyph.deselect()
            for contour in glyph.contours:
                for bp in contour.bPoints:
                    in_angle, out_angle, diff = bp_angles(bp)
                    if diff < 10:
                        if abs(abs(in_angle / 90) - round(abs(in_angle / 90))) > 0.1:
                            bp.selected = True
    
            if remove:
                glyph.removeSelection(
                    removePoints=True,
                    removeComponents=False,
                    removeAnchors=False,
                    removeImages=False)
                    
    glyph = CurrentGlyph()
    #glyph.removeOverlap(round=1)
    select_extra_points(glyph, remove=False)
    
    # To run on the entire font
    #for glyph in CurrentFont():
    #    select_extra_points(glyph, remove=True)
    

  • admin

    super!!! thanks @robstenson @Manufraktur


Log in to reply