Last active
October 6, 2021 23:16
-
-
Save mathieureguer/6f98645003ab0d02fc72e6412f9eb6af to your computer and use it in GitHub Desktop.
This file contains hidden or 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
from mojo.UI import SetCurrentLayerByName | |
from mojo.subscriber import Subscriber, registerGlyphEditorSubscriber | |
from fontTools.ufoLib.pointPen import AbstractPointPen | |
import math | |
""" | |
This is *largely* based on the great work of Jackson Cavanaugh on AddOverlapp | |
and the awesome drag offset update by Ryan Bugden | |
It is *extremly* likely not the best way to use merz and subscriber :) | |
""" | |
# ---------------------------------------- | |
DEFAULT_OFFSET = 30 | |
# ---------------------------------------- | |
def getLength(pt1, pt2): | |
x1, y1 = pt1 | |
x2, y2 = pt2 | |
return math.sqrt((x2 - x1)**2 + (y2 - y1)**2) | |
def pointOnACurve(curve, value): | |
(x1, y1), (cx1, cy1), (cx2, cy2), (x2, y2) = curve | |
dx = x1 | |
cx = (cx1 - dx) * 3.0 | |
bx = (cx2 - cx1) * 3.0 - cx | |
ax = x2 - dx - cx - bx | |
dy = y1 | |
cy = (cy1 - dy) * 3.0 | |
by = (cy2 - cy1) * 3.0 - cy | |
ay = y2 - dy - cy - by | |
mx = ax * (value)**3 + bx * (value)**2 + cx * (value) + dx | |
my = ay * (value)**3 + by * (value)**2 + cy * (value) + dy | |
return mx, my | |
class AddOverlapPointPen(AbstractPointPen): | |
def __init__(self, selectedPoints=[], offset=30): | |
self.offset = int(offset) | |
self.selectedPoints = selectedPoints | |
self._contours = [] | |
self._components = [] | |
def beginPath(self): | |
self._contours.append([]) | |
self.firstSegment = None | |
self.prevOncurve = None | |
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): | |
data = dict(point=pt, segmentType=segmentType, smooth=smooth, name=name, kwargs=kwargs) | |
self._contours[-1].append(data) | |
def endPath(self): | |
pass | |
def addComponent(self, baseGlyphName, transformation): | |
pass | |
def _offset(self, pt1, pt2): | |
x1, y1 = pt1 | |
x2, y2 = pt2 | |
length = getLength((x1, y1), (x2, y2)) | |
if length == 0: | |
return 0, 0 | |
ox = (x2 - x1) / length * self.offset | |
oy = (y2 - y1) / length * self.offset | |
return int(round(ox)), int(round(oy)) | |
def drawPoints(self, outpen): | |
for pointsData in self._contours: | |
if len(pointsData) == 1: | |
# ignore single movetos and anchors | |
continue | |
outpen.beginPath() | |
lenPointsData = len(pointsData) | |
for i, pointData in enumerate(pointsData): | |
currentPoint = pointData["point"] | |
addExtraPoint = None | |
if pointData["segmentType"] and pointData["point"] in self.selectedPoints: | |
prevPointData = pointsData[i - 1] | |
nextPointData = pointsData[(i + 1) % lenPointsData] | |
prevOffsetX, prevOffsetY = self._offset(prevPointData["point"], pointData["point"]) | |
nextOffsetX, nextOffsetY = self._offset(pointData["point"], nextPointData["point"]) | |
if (nextOffsetX, nextOffsetY) == (0, 0) and nextPointData["segmentType"] is None: | |
nextSegment = [ | |
pointsData[(i + 3) % lenPointsData]["point"], | |
pointsData[(i + 2) % lenPointsData]["point"], | |
nextPointData["point"], | |
pointData["point"] | |
] | |
newPoint = pointOnACurve(nextSegment, 0.9) | |
nextOffsetX, nextOffsetY = self._offset(pointData["point"], newPoint) | |
addExtraPoint = currentPoint[0] - nextOffsetX, currentPoint[1] - nextOffsetY | |
if (prevOffsetX, prevOffsetY) == (0, 0) and prevPointData["segmentType"] is None: | |
prevSegment = [ | |
pointsData[i - 3]["point"], | |
pointsData[i - 2]["point"], | |
prevPointData["point"], | |
pointData["point"] | |
] | |
newPoint = pointOnACurve(prevSegment, 0.9) | |
prevOffsetX, prevOffsetY = self._offset(newPoint, pointData["point"]) | |
currentPoint = currentPoint[0] + prevOffsetX, currentPoint[1] + prevOffsetY | |
outpen.addPoint(currentPoint, | |
pointData["segmentType"], | |
pointData["smooth"], | |
pointData["name"], | |
**pointData["kwargs"] | |
) | |
if addExtraPoint: | |
outpen.addPoint(addExtraPoint, "line") | |
outpen.endPath() | |
for baseGlyphName, transformation in self._components: | |
outpen.addComponent(baseGlyphName, transformation) | |
# ---------------------------------------- | |
class AddOverlap(Subscriber): | |
debug = True | |
def build(self): | |
self.initial_drag_x = None | |
self.glyph = CurrentGlyph() | |
glyphEditor = self.getGlyphEditor() | |
self.container = glyphEditor.extensionContainer( | |
identifier="com.jackson.ryan.mathieu.addoverlap", | |
location="background", | |
clear=True) | |
self.overlap_layer = self.container.appendPathSublayer( | |
fillColor=None, | |
strokeColor=(1, 0, 1, 1), | |
strokeWidth=2, | |
name="overlap_layer") | |
def destroy(self): | |
self.container.clearSublayers() | |
def glyphEditorDidKeyDown(self, info): | |
if info['deviceState']['keyDownWithoutModifiers'] == "v": | |
offset_value = self._calculate_drag_offset(info) | |
self.overlap_glyph = self._make_overlap_glyph(offset_value) | |
path = self.overlap_glyph.getRepresentation("merz.CGPath") | |
self.overlap_layer.setPath(path) | |
def glyphEditorDidKeyUp(self, info): | |
# !! there must be a better way here | |
if info['deviceState']['keyDownWithoutModifiers'] == "v" \ | |
and info['deviceState']['optionDown'] == 0 \ | |
and info['deviceState']['controlDown'] == 0 \ | |
and info['deviceState']['commandDown'] == 0: | |
self.initial_drag_x = None | |
self.overlap_layer.setPath(None) | |
self.glyph.prepareUndo('Add Overlap') | |
self.glyph.clearContours() | |
self.overlap_glyph.draw(self.glyph.getPen()) | |
self.glyph.performUndo() | |
def _calculate_drag_offset(self, info): | |
if self.initial_drag_x == None: | |
self.initial_drag_x = int(info['deviceState']['locationInWindow'].x) | |
current_drag_x = int(info['deviceState']['locationInWindow'].x) | |
return int((current_drag_x - self.initial_drag_x) / 2 + DEFAULT_OFFSET) | |
def _make_overlap_glyph(self, offset): | |
selected_pts = [(p.x, p.y) for p in self.glyph.selectedPoints] | |
overlap_pen = AddOverlapPointPen(selected_pts, offset) | |
self.glyph.drawPoints(overlap_pen) | |
# draw result into a ghost glyph | |
# !! there probably a smarter way to do this than creating a glyph object :) | |
ghost_glyph = RGlyph() | |
ghost_glyph_pen = ghost_glyph.getPointPen() | |
overlap_pen.drawPoints(ghost_glyph_pen) | |
return ghost_glyph | |
registerGlyphEditorSubscriber(AddOverlap) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment