Last active
May 10, 2022 15:05
-
-
Save okay-type/73637a66de2772a0d18c68efa404a1e7 to your computer and use it in GitHub Desktop.
start of a rf4 anchor tool using merz and subscriber
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
from AppKit import NSScreen | |
from vanilla import * | |
import merz | |
import mojo.subscriber as subs | |
from mojo.subscriber import Subscriber, WindowController, registerCurrentGlyphSubscriber, registerGlyphEditorSubscriber, registerSubscriberEvent, getRegisteredSubscriberEvents, unregisterGlyphEditorSubscriber, listRegisteredSubscribers | |
from mojo.extensions import registerExtensionDefaults, setExtensionDefault, getExtensionDefault, removeExtensionDefault | |
from mojo.drawingTools import * | |
from math import radians, tan, pi | |
from AppKit import NSColor | |
from fontTools.pens.basePen import BasePen | |
from mojo.UI import getDefault, setDefault, SetCurrentGlyphByName | |
import fnmatch | |
# color settings | |
uiBackgroundColor = (.5, 1, .5, .25) | |
uiBackgroundColorOff = (.8, 1, .8, .2) | |
fillColor = (.1, 0, 1, .25) | |
selectedFillColor = (.5, 0, 1, .666) # also use for path-to-point in positionHelper | |
boundsColor = (0, 1, 0, .333) | |
positionHelperColor = (0, 1, 1, 1) | |
# position helper settings | |
fuzz = 25 | |
glyphRelationships = { | |
# glyph: (referenceGlyph, name transformation) | |
# * wildcard | |
# transformations can be 'upper', 'lower', 'title' | |
'*.sc' : ('*', 'title'), | |
'ae*.sc' : ('AE*', None), | |
'oe*.sc' : ('OE*', None), | |
'oslash.sc': ('o.sc', None), | |
'ohorn.sc': ('o.sc', None), | |
'uhorn.sc': ('u.sc', None), | |
'Oslash': ('O', None), | |
'Ohorn': ('O', None), | |
'Uhorn': ('U', None), | |
'oslash': ('o', None), | |
'ohorn': ('o', None), | |
'uhorn': ('u', None), | |
} | |
''' | |
recent updates: | |
differentiates between on-but-no-anchors and off (shows options when on) | |
now a dropdown menu in the glyph edit window | |
stores checked components as a preference | |
expand/collapse state persists when switching glyphs | |
close button turns off the view (still need to figure out how to remove the tool) | |
draws glyph guidelines (left, middle, right) | |
updates when anchors are added or deleted | |
draw accents that have components, thanks conner! | |
fixed(?): error when matching 'anchor' and '_anchor' don't exist | |
draws suggested anchor positions for dependent glyphs (smallcaps, oslash, etc) | |
ui options for view modes | |
save ui options in preferences | |
improved glyphRelationships dict for position helper | |
has wildcards and case tranformations | |
when anchors are selected, the preview color darkens | |
triple click on component changes the edit window to that component, thanks again conner! | |
waiting for beta updates to do: | |
draw in preview mode | |
make it a real tool that you can turn on and off for real | |
to do: | |
on off - seperate from show/hide | |
don't update data if it's not visible | |
make anchor list scrollable for small windows | |
draw ui to fix selected anchors suggest postions | |
load colors from preferences - use component color pref as start | |
when nothing selected | |
change anchor colors to color-coded, maybe with a message or icon | |
show fix [add] [remove] [move all x] [move all y] [move all xy] buttons | |
when anchors are selected | |
hide components unrelated to the selected anchor | |
show the components' bounds as guides | |
show fix [move x] [move y] [move xy] buttons | |
''' | |
class anchorHelper(Subscriber): | |
debug = False | |
def build(self): | |
print('building anchorHelper') | |
# preferences | |
self.prefKey = 'com.okay.anchorHelper' | |
initialDefaults = { | |
self.prefKey + 'anchorOn': [], | |
self.prefKey + 'showUI': 1, | |
self.prefKey + 'toggleComponents': 1, | |
self.prefKey + 'toggleGuidelines': 0, | |
self.prefKey + 'togglePositions': 1, | |
self.prefKey + 'toggleTool': 1, | |
} | |
# for v, p in initialDefaults.items(): | |
# removeExtensionDefault(v) | |
registerExtensionDefaults(initialDefaults) | |
# initial data | |
self.anchorOn = getExtensionDefault(self.prefKey + 'anchorOn') | |
self.showUI = getExtensionDefault(self.prefKey + 'showUI') | |
self.toggleComponents = getExtensionDefault(self.prefKey + 'toggleComponents') | |
self.toggleGuidelines = getExtensionDefault(self.prefKey + 'toggleGuidelines') | |
self.togglePositions = getExtensionDefault(self.prefKey + 'togglePositions') | |
self.toggleTool = getExtensionDefault(self.prefKey + 'toggleTool') | |
self.getAnchorData() | |
self.added = [] | |
self.anchorCount = None | |
self.selectedAnchors = [] | |
self.anchorsToRemove = [] | |
self.anchorsToAdd = {} # dict of name: position | |
self.anchorsToMove = {} # dict of name: position | |
self.anchorColor = tuple(float(a) for a in getDefault('glyphViewAnchorColor')) | |
self.anchorSize = getDefault('glyphViewOffCurvePointsSize') | |
self.anchorTextSize = getDefault('textFontSize') | |
# for mousedown to component | |
self.clickableComponents = {} | |
# glyph view layer | |
self.glyphEditor = self.getGlyphEditor() | |
self.anchorPreview = self.glyphEditor.extensionContainer( | |
identifier='com.okay.anchorHelper.background', | |
location='background', | |
clear=True, | |
) | |
self.previewContainer = self.glyphEditor.extensionContainer( | |
identifier='com.robofont.anchorHelper.preview', | |
location='preview', | |
clear=True | |
) | |
# ui | |
self.x = 0 | |
self.w = 180 | |
self.u = 22 | |
self.controls = Group((self.x, 0, self.w, self.u)) | |
self.controls.showTool = CheckBox( | |
(8, 0, -30, -0), | |
'⚓ Helper', | |
sizeStyle='small', | |
value=self.toggleTool, | |
callback=self.toggleViews, | |
) | |
self.controls.viewExpand = CheckBox( | |
(-30, 0, -15, -0), | |
'', | |
sizeStyle='small', | |
value=self.showUI, | |
callback=self.toggleViews, | |
) | |
self.controls.closeTool = Button( | |
(-15, 0, 0, -0), | |
'X', | |
sizeStyle='mini', | |
callback=self.removeTool, | |
) | |
self.glyphEditor.addGlyphEditorSubview(self.controls) | |
self.main = Group((self.x, self.u, self.w, -0)) | |
self.anchorController= Group((0, 0, -0, -0)) | |
y = 0 | |
h = 22 | |
self.anchorController.line = HorizontalLine((8, y+h+2, -8, 1)) | |
self.anchorController.line.setBorderWidth(1) | |
y += h + 5 | |
self.anchorController.viewComponent = CheckBox( | |
(8, y, -8, h), | |
'👀 Components', | |
sizeStyle='small', | |
value=self.toggleComponents, | |
callback=self.toggleViews, | |
) | |
y += h | |
self.anchorController.viewGuidelines = CheckBox( | |
(8, y, -8, h), | |
'👀 Bounds Lines', | |
sizeStyle='small', | |
value=self.toggleGuidelines, | |
callback=self.toggleViews, | |
) | |
y += h | |
self.anchorController.viewPositions = CheckBox( | |
(8, y, -8, h), | |
'👀 Position Helper', | |
sizeStyle='small', | |
value=self.togglePositions, | |
callback=self.toggleViews, | |
) | |
y += h | |
self.main.scrollView = ScrollView((0, 0, -0, -0), self.anchorController.getNSView()) | |
self.glyphEditor.addGlyphEditorSubview(self.main) | |
self.anchorController.setPosSize((0, 0, self.w, y), False) | |
# get blues (for position helper) | |
font = CurrentFont() | |
self.blues = [] | |
self.blues.extend(font.info.postscriptBlueValues) | |
self.blues.extend(font.info.postscriptOtherBlues) | |
self.blues = [(self.blues[i], self.blues[i+1]) for i in range(0, len(self.blues), 2)] | |
def toggleViews(self, sender): | |
self.showUI = self.controls.viewExpand.get() | |
testToggleTool = self.controls.showTool.get() | |
if testToggleTool != self.toggleTool: | |
if testToggleTool == False: | |
self.showUI = False | |
self.toggleTool = testToggleTool | |
self.toggleComponents = self.anchorController.viewComponent.get() | |
self.toggleGuidelines = self.anchorController.viewGuidelines.get() | |
self.togglePositions = self.anchorController.viewPositions.get() | |
self.drawAnchorControls() | |
self.positionHelper() | |
self.drawAnchors() | |
def destroy(self): | |
print('destroying anchorHelper') | |
setExtensionDefault(self.prefKey + 'anchorOn', self.anchorOn) | |
setExtensionDefault(self.prefKey + 'showUI', self.showUI) | |
setExtensionDefault(self.prefKey + 'toggleComponents', self.toggleComponents) | |
setExtensionDefault(self.prefKey + 'toggleGuidelines', self.toggleGuidelines) | |
setExtensionDefault(self.prefKey + 'togglePositions', self.togglePositions) | |
setExtensionDefault(self.prefKey + 'toggleTool', self.toggleTool) | |
self.anchorPreview.clearSublayers() | |
self.previewContainer.clearSublayers() | |
self.getGlyphEditor().removeGlyphEditorSubview(self.main) | |
self.getGlyphEditor().removeGlyphEditorSubview(self.controls) | |
def removeTool(self, sender): | |
self.destroy() | |
registered_subscribers = listRegisteredSubscribers(subscriberClassName='anchorHelper') | |
if len(registered_subscribers) > 0: | |
for target_subscriber in registered_subscribers: | |
unregisterGlyphEditorSubscriber(target_subscriber) | |
# | |
# | |
# events | |
# | |
def glyphEditorDidSetGlyph(self, info): | |
## shouldn't have to do this in next beta | |
self.previewContainer = self.glyphEditor.extensionContainer( | |
identifier='com.robofont.anchorHelper.preview', | |
location='preview', | |
clear=True | |
) | |
# | |
self.glyph = info['glyph'] | |
if self.glyph is None: | |
return | |
self.componentsToDraw = self.getGlyphComponentData(self.glyph) | |
self.drawAnchorControls() | |
self.positionHelper() | |
self.drawAnchors() | |
def glyphEditorGlyphDidChangeAnchors(self, info): | |
self.glyph = info['glyph'] | |
if self.glyph is None: | |
return | |
if self.anchorCount != len(self.glyph.anchors): | |
self.anchorCount = len(self.glyph.anchors) | |
self.componentsToDraw = self.getGlyphComponentData(self.glyph) | |
self.drawAnchorControls() | |
self.positionHelper() | |
self.drawAnchors() | |
def glyphSelectionChanged(self, notification): | |
glyph = notification['lowLevelEvents'][0].object | |
if glyph is None: | |
return | |
selectedAnchors = [] | |
for a in glyph.anchors: | |
if a.selected: | |
selectedAnchors.append(a.name) | |
if selectedAnchors != self.selectedAnchors: | |
self.selectedAnchors = selectedAnchors | |
self.drawAnchors() | |
self.drawHelperUI() | |
def glyphEditorDidMouseDown(self, info): | |
lle = info['lowLevelEvents'][0] | |
point = lle['point'] | |
cc = lle['clickCount'] | |
if cc == 3: | |
for componentKey in self.clickableComponents: | |
componentObject = self.clickableComponents[componentKey] | |
path = componentObject.naked().getRepresentation('defconAppKit.NSBezierPath') | |
if path.containsPoint_(point): | |
SetCurrentGlyphByName(componentKey) | |
break | |
# | |
# | |
# build data | |
# | |
def getAnchorData(self): | |
# family-level data | |
# anchor.name: (list of glyph.names) | |
self.anchorData = {} | |
f = CurrentFont() | |
if f is not None: | |
glyphOrder = f.glyphOrder | |
for g in glyphOrder: | |
glyph = f[g] | |
if len(glyph.anchors) > 0: | |
for anchor in glyph.anchors: | |
if anchor.name not in self.anchorData: | |
self.anchorData[anchor.name] = [] | |
self.anchorData[anchor.name].append(glyph.name) | |
def getGlyphComponentData(self, glyph): | |
componentsToDraw = {} | |
if len(glyph.anchors) > 0: | |
for anchor in glyph.anchors: | |
mateAnchor = self.mateAnchor(anchor.name) | |
if mateAnchor not in componentsToDraw: | |
componentsToDraw[mateAnchor] = [] | |
if mateAnchor in self.anchorData: | |
for component in self.anchorData[mateAnchor]: | |
componentsToDraw[mateAnchor].append(component) | |
return componentsToDraw | |
# | |
# | |
# helper functions | |
# | |
def mateAnchor(self, anchorName): | |
if '_' == anchorName[0]: | |
mateAnchor = anchorName.replace('_', '') | |
else: | |
mateAnchor = '_' + anchorName | |
return mateAnchor | |
def getAnchorByName(self, glyph, anchorName): | |
return next(( | |
anchor for anchor in glyph.anchors if | |
anchor.name == anchorName), None) | |
def italicShiftedX(self, x, y, italicAngle): | |
if italicAngle != 0: | |
return x + (tan(-italicAngle * pi / 180) * y) | |
else: | |
return x | |
# | |
# | |
# build UI | |
# | |
def drawAnchorControls(self): | |
unit = self.u | |
offset = 92 | |
length = len(self.componentsToDraw) | |
for i in self.componentsToDraw.values(): | |
length += len(i)+1 | |
height = length * unit + offset - unit/2 | |
if self.showUI == False: | |
self.main.show(False) | |
self.anchorController.show(False) | |
else: | |
self.main.show(True) | |
self.anchorController.show(True) | |
self.anchorController.setPosSize((0, 0, self.w, height), False) | |
# remove old stuff | |
for added in self.added: | |
if hasattr(self.anchorController, added): | |
delattr(self.anchorController, added) | |
# build new stuff | |
count = 0 | |
y = offset | |
self.added = [] | |
for anchor in self.componentsToDraw: | |
count += 1 | |
this = TextBox((8, y+unit*.5, -8, unit), '⚓︎ '+anchor, sizeStyle='small') | |
name = 'Label'+anchor | |
if hasattr(self.anchorController, name): | |
delattr(self.anchorController, name) | |
self.added.append(name) | |
setattr(self.anchorController, name, this) | |
this = HorizontalLine((8, y+19+unit*.5, -8, 1)) | |
this.setBorderWidth(1) | |
name = 'Line'+anchor | |
if hasattr(self.anchorController, name): | |
delattr(self.anchorController, name) | |
self.added.append(name) | |
setattr(self.anchorController, name, this) | |
y += unit*1.5 | |
for component in self.componentsToDraw[anchor]: | |
count += 1 | |
check = False | |
if [component, anchor] in self.anchorOn: | |
check = True | |
this = okCheckBox( | |
(8, y, -8, unit), | |
component, | |
component=component, | |
anchor=anchor, | |
value=check, | |
sizeStyle='small', | |
callback=self.checkAnchorData, | |
) | |
name = 'CheckBox'+component+anchor | |
if hasattr(self.anchorController, name): | |
delattr(self.anchorController, name) | |
setattr(self.anchorController, name, this) | |
self.added.append(name) | |
y += unit | |
def checkAnchorData(self, sender): | |
component = sender.component | |
anchor = sender.anchor | |
if sender.get() == 1 and [component, anchor] not in self.anchorOn: | |
self.anchorOn.append([component, anchor]) | |
if sender.get() == 0 and [component, anchor] in self.anchorOn: | |
self.anchorOn.remove([component, anchor]) | |
self.drawAnchors() | |
# | |
# | |
# draw anchor preview | |
# | |
def drawAnchors(self): | |
self.anchorPreview.clearSublayers() | |
self.previewContainer.clearSublayers() | |
if self.toggleTool == False: | |
return | |
if self.glyph is None: | |
return | |
font = self.glyph.font | |
self.clickableComponents = {} | |
# prep drawing | |
self.anchorPreview.clearSublayers() | |
self.previewContainer.clearSublayers() | |
anchorLayer = self.anchorPreview.getSublayer('anchorLayer') | |
if anchorLayer is None: | |
anchorLayer = self.anchorPreview.appendPathSublayer(name='anchorLayer') | |
previewLayer = self.previewContainer.getSublayer('previewLayer') | |
if previewLayer is None: | |
previewLayer = self.previewContainer.appendPathSublayer( | |
name='previewLayer', | |
) | |
# draw | |
with anchorLayer.drawingTools() as bot: | |
bot.stroke(None) | |
if self.toggleComponents == True: | |
with previewLayer.drawingTools() as previewbot: | |
previewbot.fill(0, 0, 0, 1) | |
for anchor in self.componentsToDraw: | |
for component in self.componentsToDraw[anchor]: | |
c = font[component] | |
mateAnchor = self.mateAnchor(anchor) | |
cAnchor = self.getAnchorByName(c, anchor) | |
bAnchor = self.getAnchorByName(self.glyph, mateAnchor) | |
shift = (bAnchor.x-cAnchor.x, bAnchor.y-cAnchor.y) | |
name = 'CheckBox'+component+anchor | |
componentVisibility = False | |
if hasattr(self.anchorController, name): | |
componentVisibility = getattr(self.anchorController, name).get() | |
if componentVisibility == True: | |
bot.fill(*fillColor,) | |
if mateAnchor in self.selectedAnchors: | |
bot.fill(*selectedFillColor,) | |
componentCopy = RGlyph() | |
pen = DecompPen(font, componentCopy.getPen()) | |
font[component].draw(pen) | |
componentCopy.moveBy(shift) | |
bot.drawGlyph(componentCopy) | |
if component not in self.clickableComponents: | |
self.clickableComponents[component] = componentCopy | |
previewbot.drawGlyph(componentCopy) | |
if self.toggleGuidelines == True: | |
self.drawGuide((0, 0), bot) | |
if self.togglePositions == True: | |
self.drawPositionHelper(bot) | |
def drawGuide(self, shift, bot): | |
font = self.glyph.font | |
italicAngle = font.info.italicAngle or 0 | |
italicOffset = font.lib.get('com.typemytype.robofont.italicSlantOffset') or 0 | |
shiftx, shifty = shift | |
bot.fill(None) | |
bot.strokeWidth((1)) | |
bot.stroke(*boundsColor,) | |
Ytop = font.info.ascender + 500 | |
Ybot = font.info.descender - 500 | |
# leftz | |
xL = self.glyph.angledLeftMargin + italicOffset + shiftx | |
Xtop = self.italicShiftedX(xL, Ytop, italicAngle) | |
Xbot = self.italicShiftedX(xL, Ybot, italicAngle) | |
bot.newPath() | |
bot.moveTo((Xbot, Ybot)) | |
bot.lineTo((Xtop, Ytop)) | |
bot.closePath() | |
bot.drawPath() | |
# right | |
xR = self.glyph.width - self.glyph.angledRightMargin + italicOffset + shiftx | |
Xtop = self.italicShiftedX(xR, Ytop, italicAngle) | |
Xbot = self.italicShiftedX(xR, Ybot, italicAngle) | |
bot.newPath() | |
bot.moveTo((Xbot, Ybot)) | |
bot.lineTo((Xtop, Ytop)) | |
bot.closePath() | |
bot.drawPath() | |
# middle | |
xC = (xL + xR)/2 | |
Xtop = self.italicShiftedX(xC, Ytop, italicAngle) | |
Xbot = self.italicShiftedX(xC, Ybot, italicAngle) | |
bot.newPath() | |
bot.moveTo((Xbot, Ybot)) | |
bot.lineTo((Xtop, Ytop)) | |
bot.closePath() | |
bot.drawPath() | |
# | |
# | |
# anchor position | |
# | |
def drawPositionHelper(self, bot): | |
# draw a red x over anchors to remove | |
if self.anchorsToRemove: | |
for remove in self.anchorsToRemove: | |
removeAnchor = self.getAnchorByName(self.glyph, remove) | |
x, y = removeAnchor.x, removeAnchor.y | |
bot.fill(None) | |
bot.strokeWidth((self.anchorSize/2)) | |
bot.stroke(*positionHelperColor,) | |
size = self.anchorSize * 2 | |
bot.newPath() | |
bot.moveTo((x-size, y-size)) | |
bot.lineTo((x+size, y+size)) | |
bot.closePath() | |
bot.drawPath() | |
bot.newPath() | |
bot.moveTo((x+size, y-size)) | |
bot.lineTo((x-size, y+size)) | |
bot.closePath() | |
bot.drawPath() | |
# draw a red circle around missing anchor | |
if self.anchorsToAdd: | |
for add in self.anchorsToAdd: | |
x, y = self.anchorsToAdd[add] | |
bot.fill(None) | |
bot.strokeWidth((self.anchorSize/2)) | |
bot.stroke(*positionHelperColor,) | |
size = self.anchorSize * 2 | |
bot.newPath() | |
bot.moveTo((x-size, y)) | |
bot.lineTo((x+size, y)) | |
bot.closePath() | |
bot.drawPath() | |
bot.newPath() | |
bot.moveTo((x, y-size)) | |
bot.lineTo((x, y+size)) | |
bot.closePath() | |
bot.drawPath() | |
bot.fill(*positionHelperColor,) | |
bot.fontSize(self.anchorTextSize) | |
labelwidth = 60 | |
bot.textBox('+ '+add, (x-labelwidth/2, y+size-5, labelwidth, 20), align='center') | |
# alignment not working | |
# | |
# draw a red line to the recommended position | |
if self.anchorsToMove: | |
for move in self.anchorsToMove: | |
toMoveAnchor = self.getAnchorByName(self.glyph, move) | |
x1, y1 = toMoveAnchor.x, toMoveAnchor.y | |
x2, y2 = self.anchorsToMove[move] | |
size = self.anchorSize * 2 | |
# fuzz line | |
bot.fill(None) | |
bot.stroke(*positionHelperColor,) | |
bot.strokeWidth((self.anchorSize*1.5)) | |
bot.newPath() | |
bot.moveTo((x2-fuzz, y2)) | |
bot.lineTo((x2+fuzz, y2)) | |
bot.closePath() | |
bot.drawPath() | |
# target point | |
bot.fill(1, 1, 1, 1) | |
bot.stroke(*positionHelperColor,) | |
bot.strokeWidth((2)) | |
bot.oval(x2-size*.7, y2-size*.7, size*1.4, size*1.4) | |
# path to point | |
bot.fill(None) | |
bot.stroke(*selectedFillColor,) | |
bot.strokeWidth((1)) | |
bot.newPath() | |
bot.moveTo((x1, y1)) | |
bot.lineTo((x2, y2)) | |
bot.closePath() | |
bot.drawPath() | |
def positionHelper(self): | |
font = self.glyph.font | |
# get reference glyph | |
refGlyph = transformation = None | |
if self.glyph.name in glyphRelationships: | |
refGlyph, transformation = glyphRelationships[self.glyph.name] | |
else: | |
for key, value in glyphRelationships.items(): | |
if fnmatch.fnmatch(self.glyph.name, key): | |
cleanGlyph = self.glyph.name | |
wildcardRef = key | |
refGlyph, transformation = value | |
x = refGlyph.split('*') | |
for n, string in enumerate(wildcardRef.split('*')): | |
cleanGlyph = cleanGlyph.replace(string, x[n]) | |
refGlyph = cleanGlyph | |
if transformation != None: | |
refGlyph = getattr(refGlyph, transformation)() | |
# data to fill | |
self.anchorsToRemove = [] | |
self.anchorsToAdd = {} # dict of name: position | |
self.anchorsToMove = {} # dict of name: position | |
# stop if reference glyph doesnt exist | |
if refGlyph not in font.glyphOrder: | |
return | |
refGlyph = font[refGlyph] | |
# get blue-zone-adjusted bounds and scale factors | |
gVerticalbounds = self.snapToBlue(self.glyph.bounds[1], self.glyph.bounds[3]) | |
rVerticalbounds = self.snapToBlue(refGlyph.bounds[1], refGlyph.bounds[3]) | |
rHeight = rVerticalbounds[1] - rVerticalbounds[0] | |
gHeight = gVerticalbounds[1] - gVerticalbounds[0] | |
scaleY = gHeight / rHeight | |
# scaleX = (self.glyph.bounds[2]-self.glyph.bounds[0]) / (refGlyph.bounds[2]-refGlyph.bounds[0]) | |
scaleX = self.glyph.width / refGlyph.width | |
# test number of anchors | |
if len(self.glyph.anchors) != len(refGlyph.anchors): | |
gA = set(self.anchorListByName(self.glyph.anchors)) | |
rA = set(self.anchorListByName(refGlyph.anchors)) | |
self.anchorsToRemove = list(sorted(gA - rA)) | |
for a in list(sorted(rA - gA)): | |
missingAnchor = self.getAnchorByName(refGlyph, a) | |
self.anchorsToAdd[a] = (int(missingAnchor.x * scaleX), int(missingAnchor.y * scaleY)) | |
# test positions of anchors | |
for anchor in self.glyph.anchors: | |
x1 = anchor.x - fuzz | |
x2 = anchor.x + fuzz | |
gY = anchor.y | |
for refAnchor in refGlyph.anchors: | |
if anchor.name in refAnchor.name: | |
oldPos = [anchor.x, anchor.y] | |
newPos = [anchor.x, anchor.y] | |
# check X positions | |
scaledx = int(refAnchor.x * scaleX) | |
if not scaledx - fuzz < anchor.x < scaledx + fuzz: | |
newPos[0] = scaledx | |
# check Y positions | |
rY = refAnchor.y | |
# if inside the glyph bounds - use scale | |
if rVerticalbounds[0] < rY < rVerticalbounds[1]: | |
yshift = gVerticalbounds[0] - rVerticalbounds[0] | |
gY = int(refAnchor.y * scaleY + yshift) | |
# if above the glyph bounds - use offset | |
elif rY > rVerticalbounds[1]: | |
yshift = rY - rVerticalbounds[1] | |
gY = gVerticalbounds[1] + yshift | |
# if below the glyph bounds - use offset | |
elif rY < rVerticalbounds[0]: | |
yshift = rVerticalbounds[0] - rY | |
gY = gVerticalbounds[0] - yshift | |
# if y needs to change | |
if gY != anchor.y: | |
newPos[1] = gY | |
# if x or y changed, add to dict | |
if str(oldPos) != str(newPos): | |
self.anchorsToMove[anchor.name] = newPos | |
def anchorListByName(self, anchors): | |
anchorNames = [] | |
for a in anchors: | |
anchorNames.append(a.name) | |
return anchorNames | |
def snapToBlue(self, bottom, top): | |
for zone in self.blues: | |
if zone[0] <= bottom <= zone[1]: | |
bottom = zone[1] | |
if zone[0] <= top <= zone[1]: | |
top = zone[0] | |
return (bottom, top) | |
def drawHelperUI(self): | |
for anchorName in self.selectedAnchors: | |
anchor = self.getAnchorByName(self.glyph, anchorName) | |
print('draw ui for', anchorName, 'at', anchor.x, anchor.y) | |
def positionFixer(self): | |
print('positionFixer') | |
class okCheckBox(CheckBox): | |
def __init__(self, *args, **kwargs): | |
self.component = kwargs['component'] | |
del kwargs['component'] | |
self.anchor = kwargs['anchor'] | |
del kwargs['anchor'] | |
super(okCheckBox, self).__init__(*args, **kwargs) | |
class DecompPen(BasePen): | |
def __init__(self, glyphSet, outPen): | |
super(DecompPen, self).__init__(glyphSet) | |
self._moveTo = outPen.moveTo | |
self._lineTo = outPen.lineTo | |
self._curveToOne = outPen.curveTo | |
self._closePath = outPen.closePath | |
self._endPath = outPen.endPath | |
eventName = 'okay.Glyph.SelectionChanged' | |
allEvents = getRegisteredSubscriberEvents() | |
if eventName not in allEvents: | |
registerSubscriberEvent( | |
subscriberEventName=eventName, | |
methodName='glyphSelectionChanged', | |
lowLevelEventNames=['Glyph.SelectionChanged'], | |
dispatcher='defcon.Glyph', | |
delay=0, | |
documentation='This will be called when a defcon.Glyph event `Glyph.SelectionChanged` event is posted. Get the glyph with "notification.object". Default delay: 0 seconds.' | |
) | |
registerGlyphEditorSubscriber(anchorHelper) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment