Skip to content

Instantly share code, notes, and snippets.

@ryanbugden
Last active January 9, 2024 02:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ryanbugden/1b1efbbaff35e3a6b572fefb7c9719c4 to your computer and use it in GitHub Desktop.
Save ryanbugden/1b1efbbaff35e3a6b572fefb7c9719c4 to your computer and use it in GitHub Desktop.
Looks at selected points, attempts to pair them up, and consolidates each pair into one point (attempts to preserve curve)
# menuTitle: Consolidate Selected Point Pairs
# shortCut : shift + control + f
from fontTools.misc.fixedTools import otRound
MAKE_INTO_ONE_POINT = True
def average(lst):
return sum(lst) / len(lst)
def find_point_pairs_from_selection(g):
paired_points = []
for c in g.contours:
prev_next = None
for pt in c.points:
# If the point is the same as the second of the last pair, continue on...
if pt == prev_next:
continue
# If the point is in the selection, see if the next point is also, and pair them.
if pt in g.selectedPoints:
# Get next on-curve point
try:
next_point = c.points[pt.index + 1]
except IndexError:
next_point = c.points[0]
if next_point.type == 'offcurve':
next_point = c.points[pt.index + 3]
if next_point in g.selectedPoints:
pt_pair = (pt, next_point)
paired_points.append(pt_pair)
prev_next = next_point
return paired_points
def find_bpoint_pairs_from_selection(g):
paired_bpoints = []
for c in g.contours:
prev_next = None
for bpt in c.bPoints:
# If the point is the same as the second of the last pair, continue on...
if bpt == prev_next:
continue
# If the point is in the selection, see if the next point is also, and pair them.
if bpt in g.selectedBPoints:
# Get next on-curve point
try:
next_bpoint = c.bPoints[bpt.index + 1]
except IndexError:
next_bpoint = c.bPoints[0]
if next_bpoint in g.selectedBPoints:
bpt_pair = (bpt, next_bpoint)
paired_bpoints.append(bpt_pair)
prev_next = next_bpoint
return paired_bpoints
def consolidate_pair(two_pts, two_bpts):
point_coords = [(pt.x, pt.y) for pt in two_pts]
average_coords = (otRound(average([coord[0] for coord in point_coords])), otRound(average([coord[1] for coord in point_coords])))
# Snap all selected points to the same contour
for pt in two_pts:
pt.x, pt.y = average_coords
# Delete all but one point
if MAKE_INTO_ONE_POINT == True:
point_amount = len(two_bpts)
i = 0
for bpt in two_bpts:
i += 1
if i < point_amount: # Currently always going to be 2, but could use this on multiple
bpt.contour.removeBPoint(bpt, preserveCurve=True)
g = CurrentGlyph()
with g.undo("Consolidate selected points"):
with g.holdChanges():
paired_points = find_point_pairs_from_selection(g)
paired_bpoints = find_bpoint_pairs_from_selection(g)
# Put points and bPoints together
pts_and_bpts = []
i = 0
for i, pair in enumerate(paired_points):
pts_and_bpts.append((pair, paired_bpoints[i]))
# Consolidate those pairs into one point each
for (pt_pair, bpt_pair) in pts_and_bpts:
consolidate_pair(pt_pair, bpt_pair)
# Deselect points
for pt in g.selectedPoints:
pt.selected = False
g.changed()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment