Skip to content

Instantly share code, notes, and snippets.

@mekkablue
Last active February 26, 2024 15:25
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 mekkablue/1aeebf592a8c47d303a10bbefd5b1078 to your computer and use it in GitHub Desktop.
Save mekkablue/1aeebf592a8c47d303a10bbefd5b1078 to your computer and use it in GitHub Desktop.
Macro window script for the current glyph, checks for point pairs getting twisted
# brings macro window to front and clears its log:
Glyphs.clearLog()
Glyphs.showMacroWindow()
from math import sqrt, acos, degrees
def dot_product(v1, v2):
return v1[0] * v2[0] + v1[1] * v2[1]
def magnitude(v):
return sqrt(v[0] ** 2 + v[1] ** 2)
def angle_between_vectors(vector1, vector2):
if vector1 == (0, 0) or vector2 == (0, 0):
return False
dot = dot_product(vector1, vector2)
mag1 = magnitude(vector1)
mag2 = magnitude(vector2)
cos_theta = dot / (mag1 * mag2)
theta = acos(max(-1, min(1, cos_theta))) # Ensure acos input is within [-1, 1] range
return degrees(theta)
def vectorForNodePair(n1, n2):
p1, p2 = n1.position, n2.position
return subtractPoints(p1, p2)
def distanceForNodePair(n1, n2):
p1, p2 = n1.position, n2.position
return distance(p1, p2)
def addToCurrentTab(layer):
font = layer.parent.parent
tab = font.currentTab
if not tab:
tab = font.newTab()
if not layer in tab.layers:
tab.layers.append(layer)
def checkGlyph(g):
print(f"{g.name}:")
print("PATH, NODES: ANGLE VECTOR LAYER")
for layerGroup in g.layerGroups():
layerGroup = tuple(layerGroup)
groupLayer = g.layerForId_(layerGroup[0])
groupLayer.clearSelection()
for pi, path in enumerate(groupLayer.paths):
for ni, node in enumerate(path.nodes):
nextNode = node.nextNode
nni = nextNode.nodeIndex()
vectorDict = {}
largestDist, largestID = 0, None
for layerID in layerGroup:
l = g.layerForId_(layerID)
nodes = (
l.paths[pi].nodes[ni],
l.paths[pi].nodes[nni],
)
vectorDict[layerID] = vectorForNodePair(*nodes)
if distanceForNodePair(*nodes) > largestDist:
largestDist = distanceForNodePair(*nodes)
largestID = layerID
largestVector = vectorDict[largestID]
largestLayer = g.layerForId_(largestID)
# addToCurrentTab(largestLayer)
# print("largestVector", *largestVector, largestLayer.name)
for layerID in vectorDict.keys():
if layerID == largestID:
continue
layerVector = vectorDict[layerID]
layer = g.layerForId_(layerID)
rotation = angle_between_vectors(largestVector, layerVector)
if rotation != False and abs(rotation) > 90:
print(f"{pi:3.0f}, {ni:3.0f}>{nni:3.0f}: {rotation:8.1f}° {layerVector.x:10.0f} {layerVector.y:6.0f} {l.name}")
layer.paths[pi].nodes[ni].selected = True
layer.paths[pi].nodes[nni].selected = True
addToCurrentTab(layer)
checkGlyph(Layer.parent)
print("✅ Done.")
@mekkablue
Copy link
Author

Installation

  1. Open Window > Macro Panel and open a new tab if necessary (with the plus button)
  2. Paste the code from above

Usage

  1. Select a glyph (in edit view or font view)
  2. Run the script (press the Run button in the Macro window or Cmd-Enter)

@mekkablue
Copy link
Author

To run on multiple glyphs, change the last two lines to:

for layer in Glyphs.font.selectedLayers:
	glyph = layer.parent
	checkGlyph(glyph)
	print()
print("✅ Done.")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment