Skip to content

Instantly share code, notes, and snippets.

@connordavenport
Last active May 11, 2024 09:58
Show Gist options
  • Save connordavenport/1b37a509561c0f45bdbe937046713649 to your computer and use it in GitHub Desktop.
Save connordavenport/1b37a509561c0f45bdbe937046713649 to your computer and use it in GitHub Desktop.
custom measure tool to include guidelines
import math
import merz
from mojo.UI import PostBannerNotification, getDefault, setDefault, UpdateCurrentGlyphView, getGlyphViewDisplaySettings, setGlyphViewDisplaySettings, preferencesChanged, inDarkMode
from mojo.subscriber import Subscriber, registerGlyphEditorSubscriber, unregisterGlyphEditorSubscriber, listRegisteredSubscribers
from ufoProcessor.emptyPen import DecomposePointPen
class CustomMeasureTool(Subscriber):
debug = True
def started(self):
self.glyph = None
self.suffix = ".dark" if inDarkMode() else ""
self.foregroundLayer = self.getGlyphEditor().extensionContainer(
identifier="com.connordavenport.CustomMeasureTool",
location='foreground',
clear=True
)
self.beamLayer = self.foregroundLayer.appendPathSublayer(
fillColor=None,
strokeColor=None
)
self.valueLayer = self.foregroundLayer.appendTextLineSublayer(
backgroundColor=None,
text="",
fillColor=(1, 1, 1, 1),
horizontalAlignment="center"
)
self.glyphViewMeasurementsTextColor = getDefault(f"glyphViewMeasurementsTextColor{self.suffix}")
self.glyphViewMeasurementsForegroundColor = getDefault(f"glyphViewMeasurementsForegroundColor{self.suffix}")
self.glyphViewMeasurementsBackgroundColor = getDefault(f"glyphViewMeasurementsBackgroundColor{self.suffix}")
setDefault(f"glyphViewMeasurementsTextColor{self.suffix}", (0,0,0,0))
setDefault(f"glyphViewMeasurementsForegroundColor{self.suffix}", (0,0,0,0))
setDefault(f"glyphViewMeasurementsBackgroundColor{self.suffix}", (0,0,0,0))
setGlyphViewDisplaySettings({'MeasurementInfo': False})
self.draw_measurments()
def destroy(self):
self.foregroundLayer.clearSublayers()
setDefault(f"glyphViewMeasurementsTextColor{self.suffix}", self.glyphViewMeasurementsTextColor)
setDefault(f"glyphViewMeasurementsForegroundColor{self.suffix}", self.glyphViewMeasurementsForegroundColor)
setDefault(f"glyphViewMeasurementsBackgroundColor{self.suffix}", self.glyphViewMeasurementsBackgroundColor)
def glyphEditorDidMouseDown(self, info):
self.glyph = info['glyph']
if self.glyph is not None:
self.draw_measurments()
def glyphEditorDidSetGlyph(self, info):
self.glyph = info['glyph']
if self.glyph is not None:
self.draw_measurments()
def glyphEditorGlyphDidChange(self, info):
self.glyph = info['glyph']
if self.glyph is not None:
self.draw_measurments()
def glyphEditorDidMouseDrag(self, info):
self.glyph = info['glyph']
if self.glyph is not None:
self.draw_measurments()
def lerp(self, start, stop, factor):
"""
Return an interpolated value between `start` and `stop` using interpolation factor `factor`.
"""
return start + (stop - start) * factor
def draw_measurments(self):
if self.glyph is not None:
self.beamLayer.clearSublayers()
measurements = self.glyph.naked().measurements
temp = RGlyph()
temp.name = self.glyph.name
outPen = temp.getPointPen()
decomposePen = DecomposePointPen(self.glyph.layer, outPen)
self.glyph.drawPoints(decomposePen)
temp.width = self.glyph.width
if measurements:
for gl in self.glyph.guidelines:
self.drawGuide(temp.getPen(), *self.get_guideline_points(gl.getRepresentation("doodle.GuidelinePath")))
for fl in self.glyph.font.guidelines:
self.drawGuide(temp.getPen(), *self.get_guideline_points(fl.getRepresentation("doodle.GuidelinePath")))
for m in measurements:
if m.startPoint and m.endPoint:
#stroke(0,1,0,1)
#strokeWidth(3)
sx, sy = m.startPoint
ex, ey = m.endPoint
l = (sx, sy), (ex, ey)
pathLayer = self.beamLayer.appendPathSublayer(
strokeColor=self.glyphViewMeasurementsForegroundColor,
strokeWidth=1,
)
pen = pathLayer.getPen()
pen.moveTo(m.startPoint)
pen.lineTo(m.endPoint)
pen.closePath()
i = temp.getRepresentation("doodle.Beam", beam=l, italicAngle=self.glyph.font.info.italicAngle, canHaveComponent=True)
ti = sorted(i.intersects)
l,r = i.lefMarginIntersection, i.rightMarginIntersection
if l: ti.insert(0, l)
if r: ti.append(r)
inters = [ti[ii:ii+2] for ii in range(0, len(ti), 2-1)]
parsed = []
for coords in inters:
if len(coords) == 2:
front, back = coords
if front in parsed:
pass
else:
parsed.append(front)
pathLayer.appendOvalSublayer(
fillColor=self.glyphViewMeasurementsForegroundColor,
anchor=(.5,.5),
position=(front[0],front[1]),
size=(10,10),
)
if back in parsed:
pass
else:
parsed.append(back)
pathLayer.appendOvalSublayer(
fillColor=self.glyphViewMeasurementsForegroundColor,
anchor=(.5,.5),
position=(back[0],back[1]),
size=(10,10)
)
xM = self.lerp(front[0], back[0], .5)
yM = self.lerp(front[1], back[1], .5)
pathLayer.appendTextLineSublayer(
fillColor=self.glyphViewMeasurementsTextColor,
pointSize=12,
position=(xM, yM),
text=f"{self.distance(front,back)}",
horizontalAlignment="center",
verticalAlignment="center",
)
def get_guideline_points(self, NSBezierPath):
points = []
for index in range(NSBezierPath.elementCount()):
instruction, pts = NSBezierPath.elementAtIndex_associatedPoints_(index)
points.extend([(p.x, p.y) for p in pts])
return tuple(points)
def drawGuide(self, pen,p1,p2):
pen.moveTo(p1)
pen.lineTo(p2)
pen.endPath()
def distance(self, p1,p2):
return round(math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2), 2)
def dragSelection(self, point, delta):
if not self.segmentPoints:
super().dragSelection(point, delta)
def getToolbarTip(self):
return "Custom Measure"
def canSelectWithMarque(self):
return False
def shouldShowSelection(self):
return False
if __name__ == '__main__':
registered_subscribers = listRegisteredSubscribers(subscriberClassName='CustomMeasureTool')
if len(registered_subscribers) > 0:
for target_subscriber in registered_subscribers:
unregisterGlyphEditorSubscriber(target_subscriber)
else:
registerGlyphEditorSubscriber(CustomMeasureTool)
print('Glyph Edit View Metrics UI Installed')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment