Last active
February 26, 2024 15:25
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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.") |
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
Installation
Usage