Last active
February 25, 2020 12:11
-
-
Save okay-type/ff56766d5b7f950ae5579c61b398a22f to your computer and use it in GitHub Desktop.
A very simple, very rough interpolated spacecenter window for Robofont
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
# menuTitle : Interpolated Preview | |
''' | |
v 0.2 | |
jackson @ okaytype | |
to do: | |
- clean up imports | |
- better performance | |
- needs a smarter, cheaper redraw trigger | |
- only redraw/reinterpolate the current glyph when changed, keep others | |
- match spacecenter window styling | |
- on open | |
- when space center style changes (size, lineheight, etc) | |
- sort font list by weight / width / angle | |
- uncompatible glyphs feedback (placeholder glyph? fallback glyph? nothing?) | |
- bounce option (animate/bounce through axis to look cool) | |
''' | |
from mojo.UI import MultiLineView, CurrentSpaceCenter | |
from mojo.canvas import CanvasGroup | |
from vanilla import Window, PopUpButton, Slider | |
from defconAppKit.windows.baseWindow import BaseWindowController | |
from mojo.glyphPreview import GlyphPreview | |
from mojo.events import addObserver, removeObserver | |
from mojo.roboFont import CurrentGlyph | |
from mojo.drawingTools import rect, fill | |
from mojo.pens import DecomposePointPen | |
sliderMin = 0 | |
sliderMax = 1 | |
w = 1120 | |
# limit number of glyphs to help performance | |
maxglyphs = 7 | |
class InterpolatedPreview(BaseWindowController): | |
def __init__(self): | |
self.font = CurrentFont() | |
self.w = Window((w, w/2), "Interpolated Preview", minSize=(200, 200)) | |
self.w.lineView = MultiLineView( | |
(0, 0, -0, -0), | |
pointSize=30, | |
lineHeight=1, | |
) | |
self.w.lineView.setFont(self.font) | |
self.w.g = CanvasGroup((0, -45, -0, 45), delegate=CanvasStuff(self.w)) | |
self.fonts = self.fontlist() | |
self.w.g.masterA = PopUpButton( | |
(0, -45, w/2, 22), | |
self.fonts, | |
callback=self.updateFont) | |
self.w.g.masterB = PopUpButton( | |
(w/2, -45, w/2, 22), | |
self.fonts, | |
callback=self.updateFont) | |
self.w.g.slider = Slider( | |
(0, -20, w, 22), | |
minValue=sliderMin, | |
maxValue=sliderMax, | |
value=(sliderMax+sliderMin)/2, | |
tickMarkCount=None, | |
callback=self.updateFont | |
) | |
self.w.g.masterA.set(0) | |
self.w.g.masterB.set(1) | |
self.w.g.masterA.show(False) | |
self.w.g.masterB.show(False) | |
self.w.g.slider.show(False) | |
glyphs = ['o', 'n', 'i', 'o', 'n'] | |
glyphs = [self.font[glyphName] for glyphName in glyphs] | |
self.w.lineView.set(glyphs) | |
self.w.bind("close", self.windowClose) | |
self.w.open() | |
self.updateFont(None) | |
addObserver(self, "updateFont", "currentGlyphChanged") | |
addObserver(self, "viewDidChangeGlyph", "viewDidChangeGlyph") | |
addObserver(self, 'syncSC', 'spaceCenterDrawLineView') | |
def windowClose(self, sender): | |
removeObserver(self, "currentGlyphChanged") | |
removeObserver(self, "viewDidChangeGlyph") | |
removeObserver(self, "spaceCenterDrawLineView") | |
def fontlist(self): | |
fontlist = [] | |
for f in AllFonts(): | |
fontlist.append(f.info.familyName + ' ' + f.info.styleName) | |
return fontlist | |
def updateFont(self, sender): | |
self.masterA = self.fonts[self.w.g.masterA.get()] | |
self.masterB = self.fonts[self.w.g.masterB.get()] | |
for f in AllFonts(): | |
if f.info.familyName != None: | |
fname = f.info.familyName + ' ' + f.info.styleName | |
if self.masterA == fname: | |
self.masterA = f | |
if self.masterB == fname: | |
self.masterB = f | |
self.botherinterpolating = False | |
if self.masterA != self.masterB: | |
self.botherinterpolating = True | |
self.updateWindow() | |
def updateWindow(self): | |
lv = self.w.lineView | |
sc = CurrentSpaceCenter() | |
glyphs = ['o', 'n', 'i', 'o', 'n'] | |
if sc != None: | |
glyphs = sc.get() | |
lv.setPointSize(sc.getPointSize()) | |
lv.setLineHeight(sc.getLineHeight()) | |
cleanglyphs = [] | |
# limit number of glyphs to help performance | |
glyphs = glyphs[:maxglyphs] | |
if self.botherinterpolating == True: | |
for glyphName in glyphs: | |
if glyphName in self.masterA.glyphOrder and glyphName in self.masterB.glyphOrder: | |
if self.masterA[glyphName].isCompatible(self.masterB[glyphName]): | |
cleanglyphs.append(self.interpolateG(glyphName)) | |
else: | |
print('not compatible', glyphName) | |
else: | |
for glyphName in glyphs: | |
if glyphName in self.masterA.glyphOrder: | |
cleanglyphs.append(self.masterA[glyphName]) | |
lv.set(cleanglyphs) | |
def interpolateG(self, glyphName): | |
factor = self.w.g.slider.get() | |
ig = RGlyph() | |
gA = self.masterA[glyphName] | |
gB = self.masterB[glyphName] | |
# decompose if necessary | |
if len(gA.components) > 0: | |
gA = self.masterA[glyphName].copy() | |
for component in reversed(gA.components): | |
decomposePen = DecomposePointPen(self.masterA, gA.getPointPen()) | |
component.drawPoints(decomposePen) | |
gA.removeComponent(component) | |
if len(gB.components) > 0: | |
gB = self.masterB[glyphName].copy() | |
for component in reversed(gB.components): | |
decomposePen = DecomposePointPen(self.masterB, gB.getPointPen()) | |
component.drawPoints(decomposePen) | |
gB.removeComponent(component) | |
ig.interpolate(factor, gA, gB) | |
ig.name = gA.name | |
# ig.unicode = self.masterA[glyphName].unicode | |
return ig | |
def viewDidChangeGlyph(self, notification): | |
self.glyph = CurrentGlyph() | |
if self.glyph is None: | |
return | |
self.unsubscribeGlyph() | |
self.subscribeGlyph(self.glyph) | |
self.updateFont(None) | |
def subscribeGlyph(self, glyph=None): | |
if self.glyph is None: | |
return | |
self.glyph = glyph | |
self.glyph.addObserver(self, "updateFont", "Glyph.Changed") | |
def unsubscribeGlyph(self): | |
if self.glyph is None: | |
return | |
self.glyph.removeObserver(self, "Glyph.Changed") | |
sText = '' | |
def syncSC(self, sender): | |
sc = CurrentSpaceCenter().get() | |
if sc == self.sText: | |
return | |
self.sText = sc | |
self.updateFont(None) | |
class CanvasStuff(object): | |
def __init__(self, window): | |
self.w = window | |
self.bg = False | |
def opaque(self): | |
return False | |
def shouldDrawBackground(self): | |
return False | |
def draw(self): | |
if self.bg == True: | |
fill(1, 1, 1, .5) | |
rect(0, 0, w, 100) | |
def mouseEntered(self, event): | |
self.w.g.masterA.show(True) | |
self.w.g.masterB.show(True) | |
self.w.g.slider.show(True) | |
self.bg = True | |
def mouseExited(self, event): | |
self.w.g.masterA.show(False) | |
self.w.g.masterB.show(False) | |
self.w.g.slider.show(False) | |
self.bg = False | |
OpenWindow(InterpolatedPreview) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment