Separated spiro-related DSL definitions into support/spirokit.
This commit is contained in:
parent
8365b41219
commit
f912331225
7 changed files with 238 additions and 223 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -46,4 +46,5 @@ parameters.js
|
|||
support/glyph.js
|
||||
support/stroke.js
|
||||
support/spiroexpand.js
|
||||
support/spirokit.js
|
||||
testdrive/assets
|
|
@ -5,8 +5,7 @@ define utp [require './support/transform'].untransform
|
|||
define inverse [require './support/transform'].inverse
|
||||
define unorm : require 'unorm'
|
||||
|
||||
define libspiro : require 'libspiro-js'
|
||||
define SpiroExpansionContext [require './support/spiroexpand'].SpiroExpansionContext
|
||||
define spirokit [require './support/spirokit']
|
||||
|
||||
### COMMON FUNCTIONS
|
||||
define [mix a b p] : a + (b - a) * p
|
||||
|
@ -24,6 +23,7 @@ define [TempFont] {.glyf {} .head {.} .hhea {.} ."OS/2" {.} .name {.} .post {.}}
|
|||
|
||||
# Empty font base file
|
||||
|
||||
### File inclusion macro
|
||||
define-macro $$include : syntax-rules
|
||||
{'$$include' {'.quote' file}} : begin
|
||||
local path : require 'path'
|
||||
|
@ -37,6 +37,53 @@ define-macro $$include : syntax-rules
|
|||
return {'.syntactic-closure' ast env}
|
||||
otherwise `nothing
|
||||
|
||||
### Necessary macros
|
||||
# Remap Glyph's methods to macros in order to simplify writing
|
||||
define-macro set-width : syntax-rules
|
||||
`[set-width @::args] {'.syntactic-closure' `[currentGlyph.set-width @::args] env}
|
||||
define-macro start-from : syntax-rules
|
||||
`[start-from @::args] {'.syntactic-closure' `[currentGlyph.start-from @::args] env}
|
||||
define-macro line-to : syntax-rules
|
||||
`[line-to @::args] {'.syntactic-closure' `[currentGlyph.line-to @::args] env}
|
||||
define-macro curve-to : syntax-rules
|
||||
`[curve-to @::args] {'.syntactic-closure' `[currentGlyph.curve-to @::args] env}
|
||||
define-macro cubic-to : syntax-rules
|
||||
`[cubic-to @::args] {'.syntactic-closure' `[currentGlyph.cubic-to @::args] env}
|
||||
define-macro include : syntax-rules
|
||||
`[include @::args] {'.syntactic-closure' `[currentGlyph.include @::args] env}
|
||||
define-macro set-anchor : syntax-rules
|
||||
`[set-anchor @::args] {'.syntactic-closure' `[currentGlyph.set-anchor @::args] env}
|
||||
define-macro apply-transform : syntax-rules
|
||||
`[apply-transform @::args] {'.syntactic-closure' `[currentGlyph.apply-transform @::args] env}
|
||||
define-macro reverse-last : syntax-rules
|
||||
`[reverse-last @::args] {'.syntactic-closure' `[currentGlyph.reverse-last @::args] env}
|
||||
define-macro eject-contour : syntax-rules
|
||||
`[eject-contour @::args] {'.syntactic-closure' `[currentGlyph.eject-contour @::args] env}
|
||||
define-macro tag-contour : syntax-rules
|
||||
`[tag-contour @name] {'.syntactic-closure' `(currentGlyph.contours.(currentGlyph.contours.length - 1).tag = @name) env}
|
||||
define-macro dont-export : syntax-rules
|
||||
`[dont-export] {".syntactic-closure" `[set currentGlyph.dontExport true] env}
|
||||
define-macro assign-unicode : syntax-rules
|
||||
`[assign-unicode @code] {".syntactic-closure" `[begin \\
|
||||
currentGlyph.assign-unicode @code
|
||||
set unicodeGlyphs.(currentGlyph.unicode.((currentGlyph.unicode.length - 1))) currentGlyph
|
||||
] env}
|
||||
# The macro [glyph-construction] creates a function which builds a glyph.
|
||||
define-macro glyph-construction : syntax-rules
|
||||
`[glyph-construction @::steps] {'.syntactic-closure' `[lambda [] [begin \\
|
||||
local currentGlyph this
|
||||
currentGlyph.gizmo = globalTransform
|
||||
begin @::[steps.map formOf]
|
||||
return nothing
|
||||
]] env}
|
||||
|
||||
# contour tagging
|
||||
define [tagged tag fn] : lambda [] : begin
|
||||
local _tag this.defaultTag
|
||||
set this.defaultTag tag
|
||||
fn.apply this arguments
|
||||
set this.defaultTag _tag
|
||||
return nothing
|
||||
|
||||
define [buildFont para recursive] : begin
|
||||
define variantSelector para.variantSelector
|
||||
|
@ -217,53 +264,6 @@ define [buildFont para recursive] : begin
|
|||
define pMarks : StdAnchorGroup markAboveLower markBelowLower markToprightLower markBottomrightLower
|
||||
define ifMarks : StdAnchorGroup markAboveCap markBelowLower markToprightCap markBottomrightLower
|
||||
|
||||
### Necessary macros
|
||||
# Remap Glyph's methods to macros in order to simplify writing
|
||||
define-macro set-width : syntax-rules
|
||||
`[set-width @::args] {'.syntactic-closure' `[currentGlyph.set-width @::args] env}
|
||||
define-macro start-from : syntax-rules
|
||||
`[start-from @::args] {'.syntactic-closure' `[currentGlyph.start-from @::args] env}
|
||||
define-macro line-to : syntax-rules
|
||||
`[line-to @::args] {'.syntactic-closure' `[currentGlyph.line-to @::args] env}
|
||||
define-macro curve-to : syntax-rules
|
||||
`[curve-to @::args] {'.syntactic-closure' `[currentGlyph.curve-to @::args] env}
|
||||
define-macro cubic-to : syntax-rules
|
||||
`[cubic-to @::args] {'.syntactic-closure' `[currentGlyph.cubic-to @::args] env}
|
||||
define-macro include : syntax-rules
|
||||
`[include @::args] {'.syntactic-closure' `[currentGlyph.include @::args] env}
|
||||
define-macro set-anchor : syntax-rules
|
||||
`[set-anchor @::args] {'.syntactic-closure' `[currentGlyph.set-anchor @::args] env}
|
||||
define-macro apply-transform : syntax-rules
|
||||
`[apply-transform @::args] {'.syntactic-closure' `[currentGlyph.apply-transform @::args] env}
|
||||
define-macro reverse-last : syntax-rules
|
||||
`[reverse-last @::args] {'.syntactic-closure' `[currentGlyph.reverse-last @::args] env}
|
||||
define-macro eject-contour : syntax-rules
|
||||
`[eject-contour @::args] {'.syntactic-closure' `[currentGlyph.eject-contour @::args] env}
|
||||
define-macro tag-contour : syntax-rules
|
||||
`[tag-contour @name] {'.syntactic-closure' `(currentGlyph.contours.(currentGlyph.contours.length - 1).tag = @name) env}
|
||||
define-macro dont-export : syntax-rules
|
||||
`[dont-export] {".syntactic-closure" `[set currentGlyph.dontExport true] env}
|
||||
define-macro assign-unicode : syntax-rules
|
||||
`[assign-unicode @code] {".syntactic-closure" `[begin \\
|
||||
currentGlyph.assign-unicode @code
|
||||
set unicodeGlyphs.(currentGlyph.unicode.((currentGlyph.unicode.length - 1))) currentGlyph
|
||||
] env}
|
||||
# The macro [glyph-construction] creates a function which builds a glyph.
|
||||
define-macro glyph-construction : syntax-rules
|
||||
`[glyph-construction @::steps] {'.syntactic-closure' `[lambda [_args] [begin \\
|
||||
local glyphArguments : fallback _args {.}
|
||||
local currentGlyph this
|
||||
currentGlyph.gizmo = globalTransform
|
||||
|
||||
local _tag currentGlyph.defaultTag
|
||||
if glyphArguments.tag : set currentGlyph.defaultTag glyphArguments.tag
|
||||
|
||||
begin @::[steps.map formOf]
|
||||
|
||||
set currentGlyph.defaultTag _tag
|
||||
return nothing
|
||||
]] env}
|
||||
|
||||
|
||||
### Glyph slots and dependency profile generation (used for recursive subfonts)
|
||||
local dependencyProfile {.}
|
||||
|
@ -312,175 +312,14 @@ define [buildFont para recursive] : begin
|
|||
if unicode : assign-unicode unicode
|
||||
include glyphs.(oldid) AS_BASE
|
||||
set-width glyphs.(oldid).advanceWidth
|
||||
|
||||
|
||||
### Spiro constructions
|
||||
# Basic knots
|
||||
define [g4 x y f] {.x x .y y .type 'g4' .af f}
|
||||
define [g2 x y f] {.x x .y y .type 'g2' .af f}
|
||||
define [corner x y f] {.x x .y y .type 'corner' .af f}
|
||||
define [flat x y f] {.x x .y y .type 'left' .af f}
|
||||
define [curl x y f] {.x x .y y .type 'right' .af f}
|
||||
define [close f] {.type 'close' .af f}
|
||||
define [end f] {.type 'end' .af f}
|
||||
|
||||
define straight {.l flat .r curl}
|
||||
|
||||
#derived knots
|
||||
#"ai" knots, used for left and right edges of letters `o`, and similar letters
|
||||
define flat.ai : if para.isItalic g4 flat
|
||||
define curl.ai : if para.isItalic g4 curl
|
||||
#directional bi-knots
|
||||
let
|
||||
directions {{.name 'up' .x 0 .y 1}, {.name 'down' .x 0 .y (-1)}, {.name 'left' .x (-1) .y 0}, {.name 'right' .x 1 .y 0}}
|
||||
adhensions {{.name 'start' .l 0 .r 0.01}, {.name 'mid', .l (-0.005) .r 0.005}, {.name 'end', .l (-0.01) .r 0}}
|
||||
knottypes {g4, g2, corner, straight}
|
||||
foreach [direction : items-of directions] : let [d direction] : begin
|
||||
foreach [knottype : items-of knottypes] : let [kt knottype] : begin
|
||||
set kt.(d.name) {.}
|
||||
foreach [adh : items-of adhensions] : let [a adh] : begin
|
||||
set kt.(d.name).(a.name) : lambda [x y f] : list
|
||||
[fallback kt.l kt] (x + d.x * a.l) (y + d.y * a.l) f
|
||||
[fallback kt.r kt] (x + d.x * a.r) (y + d.y * a.r) f
|
||||
|
||||
# Aux functions
|
||||
define [widths l r] : lambda [] : this.set-width l r
|
||||
define [widths.lhs w] : widths [fallback w STROKE] 0
|
||||
define [widths.rhs w] : widths 0 [fallback w STROKE]
|
||||
define [widths.center w] : widths ([fallback w STROKE] / 2) ([fallback w STROKE] / 2)
|
||||
|
||||
define [heading d] : lambda [] : this.heads-to d
|
||||
define [widths.heading l r d] : lambda [] : begin [this.set-width l r] [this.heads-to d]
|
||||
define [widths.lhs.heading w d] : lambda [] : begin [this.set-width [fallback w STROKE] 0] [this.heads-to d]
|
||||
define [widths.rhs.heading w d] : lambda [] : begin [this.set-width 0 [fallback w STROKE]] [this.heads-to d]
|
||||
define [widths.center.heading w d] : lambda [] : begin [this.set-width ([fallback w STROKE] / 2) ([fallback w STROKE] / 2)] [this.heads-to d]
|
||||
define [unimportant] : begin
|
||||
if (this.points && this.points.length && this.points.(this.points.length - 1)) : this.points.(this.points.length - 1).subdivided = true
|
||||
if (this.controlKnots && this.controlKnots.length && this.controlKnots.(this.controlKnots.length - 1)) : this.controlKnots.(this.controlKnots.length - 1).unimportant = true
|
||||
define [important] nothing
|
||||
|
||||
# Interpolation pesudoknots
|
||||
define [afInterpolate before after args] : g4
|
||||
mix before.x after.x args.rx
|
||||
mix before.y after.y args.ry
|
||||
fallback args.raf unimportant
|
||||
define [afInterpolateThem before after args] : begin
|
||||
local knots {}
|
||||
foreach {rx ry preserve} [items-of args.rs] : knots.push : [fallback args.ty g2] [mix before.x after.x rx] [mix before.y after.y ry] : fallback args.raf : match preserve
|
||||
1 before.af
|
||||
2 after.af
|
||||
otherwise unimportant
|
||||
return knots
|
||||
define [alsothru rx ry raf] {.type 'interpolate' .rx rx .ry ry .raf raf .af afInterpolate}
|
||||
define [alsothruthem rs raf ty] {.type 'interpolate' .rs rs .raf raf .ty ty .af afInterpolateThem}
|
||||
define [bez2 a b c t] : (1 - t) * (1 - t) * a + 2 * (1 - t) * t * b + t * t * c
|
||||
define [bez3 a b c d t] : (1 - t) * (1 - t) * (1 - t) * a + 3 * (1 - t) * (1 - t) * t * b + 3 * t * t * (1 - t) * c + t * t * t * d
|
||||
define [bezcontrols x1 y1 x2 y2 _samples raf ty] : begin
|
||||
local samples : fallback _samples 3
|
||||
local rs {}
|
||||
foreach j [range 1 samples] : rs.push : list
|
||||
bez3 0 x1 x2 1 (j / samples)
|
||||
bez3 0 y1 y2 1 (j / samples)
|
||||
alsothruthem rs raf ty
|
||||
define [quadcontrols x1 y1 samples raf ty] : bezcontrols (x1 * 2 / 3) (y1 * 2 / 3) [mix 1 x1 (2 / 3)] [mix 1 y1 (2 / 3)] samples raf ty
|
||||
|
||||
define [bezcontrols.absolute x1 y1 x2 y2 _samples raf ty] : object [type 'interpolate'] : af : lambda [before after] : begin
|
||||
local samples : fallback _samples 3
|
||||
local rs {}
|
||||
foreach j [range 1 samples] : rs.push : g4
|
||||
bez3 before.x x1 x2 after.x (j / samples)
|
||||
bez3 before.y y1 y2 after.y (j / samples)
|
||||
fallback raf unimportant
|
||||
return rs
|
||||
|
||||
|
||||
define {jhv, jvh} : let [cache {}] : begin
|
||||
local [build samples] : begin
|
||||
local hv {}
|
||||
local vh {}
|
||||
foreach [j : range 1 samples] : begin
|
||||
local theta : j / samples * Math.PI / 2
|
||||
local c : Math.pow [Math.cos theta] (2 / SUPERNESS)
|
||||
local s : Math.pow [Math.sin theta] (2 / SUPERNESS)
|
||||
hv.push {s (1 - c)}
|
||||
vh.push {(1 - c) s}
|
||||
cache.(samples) = {.hv hv .vh vh}
|
||||
local [hv samples] : begin
|
||||
if [not cache.(samples)] : build samples
|
||||
return cache.(samples).hv
|
||||
local [vh samples] : begin
|
||||
if [not cache.(samples)] : build samples
|
||||
return cache.(samples).vh
|
||||
list hv vh
|
||||
define [archv samples notiny k raf] : alsothruthem [jhv [fallback samples 4]] raf
|
||||
define [arcvh samples notiny k raf] : alsothruthem [jvh [fallback samples 4]] raf
|
||||
|
||||
define [complexThru] : begin
|
||||
local a : {}.slice.call arguments
|
||||
return {.type 'interpolate' .af [lambda [before after args] : begin \\
|
||||
local ks {}
|
||||
foreach knot [items-of a] : ks.push [knot.af.call this before after knot]
|
||||
return ks
|
||||
]}
|
||||
|
||||
# Spiro construction
|
||||
define [flatten knots] : begin
|
||||
local a {}
|
||||
foreach p [items-of knots] : piecewise
|
||||
(p <@ Array) : set a : a.concat [flatten p]
|
||||
true : a.push p
|
||||
return a
|
||||
define [prepareSpiroKnots knots s] : begin
|
||||
local closed false
|
||||
local lastafs {}
|
||||
while (knots.0 && knots.0 <@ Function) : begin
|
||||
knots.0.call s
|
||||
set knots : knots.slice 1
|
||||
while (knots.(knots.length - 1) && (knots.(knots.length - 1).type === 'close' || knots.(knots.length - 1).type === 'end')) : begin
|
||||
set closed : knots.(knots.length - 1).type === 'close'
|
||||
lastafs.push knots.(knots.length - 1).af
|
||||
set knots : knots.slice 0 (-1)
|
||||
set knots : flatten knots
|
||||
if closed : knots.push knots.0
|
||||
foreach j [range 0 knots.length] : if (knots.(j) && knots.(j).type === 'interpolate') : begin
|
||||
set knots.(j) : knots.(j).af.call s knots.(j - 1) knots.(j + 1) knots.(j)
|
||||
if closed : knots.pop
|
||||
return {.knots [flatten knots] .closed closed .lastafs lastafs}
|
||||
|
||||
define [dispiro] : begin
|
||||
local s : new SpiroExpansionContext
|
||||
set s.gizmo globalTransform
|
||||
local {.knots knots .closed closed .lastafs lastafs} : prepareSpiroKnots [{}.slice.call arguments 0] s
|
||||
foreach knot [items-of knots] : let [ty knot.type] [af knot.af] : begin
|
||||
set knot.af : lambda [] : begin
|
||||
this.set-type ty
|
||||
if af : af.apply this arguments
|
||||
libspiro.spiroToBezierOnContext knots closed s
|
||||
foreach af [items-of lastafs] : if af : af.call s
|
||||
|
||||
local {.lhs lhs .rhs rhs} : s.expand [fallback s.contrast CONTRAST]
|
||||
if closed : then
|
||||
local g : new Glyph
|
||||
libspiro.spiroToBezierOnContext [lhs.slice 0 (-1)] true g true
|
||||
local lhsContour g.contours.0
|
||||
set g.contours {}
|
||||
libspiro.spiroToBezierOnContext [rhs.reverse :.slice 0 (-1)] true g true
|
||||
local rhsContour g.contours.0
|
||||
set g.contours {[lhsContour.concat rhsContour]}
|
||||
: else
|
||||
local g : new Glyph
|
||||
lhs.0.type = rhs.0.type = lhs.(lhs.length - 1).type = rhs.(rhs.length - 1).type = 'corner'
|
||||
libspiro.spiroToBezierOnContext [lhs.concat : rhs.reverse] true g true
|
||||
|
||||
return g
|
||||
define [spiro-outline] : let [k : {}.slice.call arguments 0] : glyph-construction
|
||||
local {.knots knots .closed closed .lastafs lastafs} : prepareSpiroKnots k this
|
||||
libspiro.spiroToBezierOnContext knots closed this
|
||||
foreach af [items-of lastafs] : if af : af.call this
|
||||
|
||||
# contour tagging
|
||||
define [tagged tag fn] : lambda [_args] : begin
|
||||
local args : if _args [begin [local a : Object.create _args] [set a.tag tag] a] {.tag tag}
|
||||
return : fn.call this args
|
||||
local [object
|
||||
g4 g2 corner flat curl close end straight
|
||||
widths heading unimportant important
|
||||
alsothru alsothruthem bezcontrols quadcontrols archv arcvh complexThru
|
||||
dispiro spiro-outline] : spirokit.SetupBuilders : object globalTransform CONTRAST STROKE Glyph para SUPERNESS
|
||||
|
||||
###### HERE WE GO!
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ define [oLeft _top _left] : glyph-construction
|
|||
close
|
||||
|
||||
define [oRight top right] : glyph-construction
|
||||
include : create-glyph [oLeft top right]
|
||||
include : create-glyph [oLeft top (WIDTH - [fallback right RIGHTSB])]
|
||||
include : FlipAround MIDDLE ([fallback top XH] / 2)
|
||||
|
||||
create-glyph 'p' : glyph-construction
|
||||
|
|
|
@ -755,13 +755,11 @@ create-glyph 'dcurlytail' : glyph-construction
|
|||
local y2 : -fine * 1.5
|
||||
include eMarks
|
||||
include : oRight XH m1
|
||||
reverse-last
|
||||
include : dispiro
|
||||
widths.rhs
|
||||
flat m1 CAP [heading DOWNWARD]
|
||||
curl m1 (fine * 2)
|
||||
CurlyTail fine rinner m1 0 (m1 + rinner * 2 + fine) x2 y2
|
||||
|
||||
if SLAB : begin
|
||||
include : LeftwardTopSerif (m1 - STROKE * CORRECTION_HX) CAP SIDEJUT
|
||||
|
||||
|
|
5
makefile
5
makefile
|
@ -1,5 +1,5 @@
|
|||
OBJDIR = build
|
||||
SUPPORT_FILES = support/glyph.js support/stroke.js support/spiroexpand.js parameters.js extract.js generate.js emptyfont.toml parameters.toml
|
||||
SUPPORT_FILES = support/glyph.js support/stroke.js support/spiroexpand.js support/spirokit.js parameters.js extract.js generate.js emptyfont.toml parameters.toml
|
||||
GLYPH_SEGMENTS = glyphs/common-shapes.patel glyphs/overmarks.patel glyphs/latin-basic-capital.patel glyphs/latin-basic-lower.patel glyphs/greek.patel glyphs/cyrillic-basic.patel glyphs/latin-extend-basis.patel glyphs/latin-extend-decorated.patel glyphs/cyrillic-extended.patel glyphs/numbers.patel glyphs/symbol-ascii.patel glyphs/symbol-punctuation.patel glyphs/symbol-math.patel glyphs/symbol-geometric.patel glyphs/symbol-other.patel glyphs/symbol-letter.patel glyphs/autobuilds.patel
|
||||
FILES = $(SUPPORT_FILES) buildglyphs.js
|
||||
PARAM_DEFAULT = FAST='$(FAST)' SUFFIX='' VARIANTNAME='$(VARIANTNAME)' STYLE_COMMON='$(STYLE_COMMON)' STYLE_UPRIGHT='$(STYLE_UPRIGHT)' STYLE_ITALIC='$(STYLE_ITALIC)' VERSION='$(VERSION)'
|
||||
|
@ -9,7 +9,7 @@ PARAM_CC_SLAB = FAST='$(FAST)' SUFFIX='cc-slab' VARIANTNAME='$(VARIANTNAME)' STY
|
|||
|
||||
### Sometimes make will freak out and report ACCESS VIOLATION for me... so i have to add some repeation
|
||||
ifeq ($(OS),Windows_NT)
|
||||
LOOPS = 0 1 2 3 4 5 6 7 8 9
|
||||
LOOPS = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||||
else
|
||||
LOOPS = 1
|
||||
endif
|
||||
|
@ -102,5 +102,6 @@ buildglyphs.js : buildglyphs.patel $(GLYPH_SEGMENTS)
|
|||
patel-c --strict $< -o $@
|
||||
support/glyph.js : support/glyph.patel
|
||||
support/stroke.js : support/stroke.patel
|
||||
support/spirokit.js : support/spirokit.patel
|
||||
support/spiroexpand.js : support/spiroexpand.patel
|
||||
parameters.js : parameters.patel
|
|
@ -2,7 +2,7 @@ TARGETUPM = 1000
|
|||
|
||||
PREFIX = $(VARIANTNAME)iosevka$(SUFFIX)
|
||||
|
||||
SUPPORT_FILES = support/glyph.js support/stroke.js support/spiroexpand.js parameters.js extract.js generate.js emptyfont.toml parameters.toml
|
||||
SUPPORT_FILES = support/glyph.js support/stroke.js support/spiroexpand.js support/spirokit.js parameters.js extract.js generate.js emptyfont.toml parameters.toml
|
||||
GLYPH_SEGMENTS = glyphs/common-shapes.patel glyphs/overmarks.patel glyphs/latin-basic-capital.patel glyphs/latin-basic-lower.patel glyphs/greek.patel glyphs/cyrillic-basic.patel glyphs/latin-extend-basis.patel glyphs/latin-extend-decorated.patel glyphs/cyrillic-extended.patel glyphs/numbers.patel glyphs/symbol-ascii.patel glyphs/symbol-punctuation.patel glyphs/symbol-math.patel glyphs/symbol-geometric.patel glyphs/symbol-other.patel glyphs/symbol-letter.patel glyphs/autobuilds.patel
|
||||
OBJDIR = build
|
||||
FILES = $(SUPPORT_FILES) buildglyphs.js
|
||||
|
@ -18,7 +18,7 @@ SUPPRESS_ERRORS = 2> /dev/null
|
|||
endif
|
||||
|
||||
|
||||
NODE = node --max_old_space_size=8192
|
||||
NODE = node --max_old_space_size=4096
|
||||
|
||||
UPRIGHT = $(OBJDIR)/$(PREFIX)-regular.ttf $(OBJDIR)/$(PREFIX)-bold.ttf
|
||||
ITALIC = $(OBJDIR)/$(PREFIX)-italic.ttf $(OBJDIR)/$(PREFIX)-bolditalic.ttf
|
||||
|
@ -86,6 +86,7 @@ buildglyphs.js : buildglyphs.patel $(GLYPH_SEGMENTS)
|
|||
patel-c --strict $< -o $@
|
||||
support/glyph.js : support/glyph.patel
|
||||
support/stroke.js : support/stroke.patel
|
||||
support/spirokit.js : support/spirokit.patel
|
||||
support/spiroexpand.js : support/spiroexpand.patel
|
||||
parameters.js : parameters.patel
|
||||
|
||||
|
|
175
support/spirokit.patel
Normal file
175
support/spirokit.patel
Normal file
|
@ -0,0 +1,175 @@
|
|||
define libspiro : require 'libspiro-js'
|
||||
define SpiroExpansionContext [require './spiroexpand'].SpiroExpansionContext
|
||||
define [fallback] : for [local j 0] (j < arguments.length) [inc j] : if (arguments.(j) !== nothing) : return arguments.(j)
|
||||
define [mix a b p] : a + (b - a) * p
|
||||
|
||||
define [SetupBuilders args] : begin
|
||||
define [object para Glyph CONTRAST globalTransform CONTRAST STROKE SUPERNESS] args
|
||||
|
||||
define [g4 x y f] {.x x .y y .type 'g4' .af f}
|
||||
define [g2 x y f] {.x x .y y .type 'g2' .af f}
|
||||
define [corner x y f] {.x x .y y .type 'corner' .af f}
|
||||
define [flat x y f] {.x x .y y .type 'left' .af f}
|
||||
define [curl x y f] {.x x .y y .type 'right' .af f}
|
||||
define [close f] {.type 'close' .af f}
|
||||
define [end f] {.type 'end' .af f}
|
||||
|
||||
define straight {.l flat .r curl}
|
||||
|
||||
#derived knots
|
||||
#"ai" knots, used for left and right edges of letters `o`, and similar letters
|
||||
define flat.ai : if para.isItalic g4 flat
|
||||
define curl.ai : if para.isItalic g4 curl
|
||||
#directional bi-knots
|
||||
let
|
||||
directions {{.name 'up' .x 0 .y 1}, {.name 'down' .x 0 .y (-1)}, {.name 'left' .x (-1) .y 0}, {.name 'right' .x 1 .y 0}}
|
||||
adhensions {{.name 'start' .l 0 .r 0.01}, {.name 'mid', .l (-0.005) .r 0.005}, {.name 'end', .l (-0.01) .r 0}}
|
||||
knottypes {g4, g2, corner, straight}
|
||||
foreach [direction : items-of directions] : let [d direction] : begin
|
||||
foreach [knottype : items-of knottypes] : let [kt knottype] : begin
|
||||
set kt.(d.name) {.}
|
||||
foreach [adh : items-of adhensions] : let [a adh] : begin
|
||||
set kt.(d.name).(a.name) : lambda [x y f] : list
|
||||
[fallback kt.l kt] (x + d.x * a.l) (y + d.y * a.l) f
|
||||
[fallback kt.r kt] (x + d.x * a.r) (y + d.y * a.r) f
|
||||
|
||||
# Aux functions
|
||||
define [widths l r] : lambda [] : this.set-width l r
|
||||
define [widths.lhs w] : widths [fallback w STROKE] 0
|
||||
define [widths.rhs w] : widths 0 [fallback w STROKE]
|
||||
define [widths.center w] : widths ([fallback w STROKE] / 2) ([fallback w STROKE] / 2)
|
||||
|
||||
define [heading d] : lambda [] : this.heads-to d
|
||||
define [widths.heading l r d] : lambda [] : begin [this.set-width l r] [this.heads-to d]
|
||||
define [widths.lhs.heading w d] : lambda [] : begin [this.set-width [fallback w STROKE] 0] [this.heads-to d]
|
||||
define [widths.rhs.heading w d] : lambda [] : begin [this.set-width 0 [fallback w STROKE]] [this.heads-to d]
|
||||
define [widths.center.heading w d] : lambda [] : begin [this.set-width ([fallback w STROKE] / 2) ([fallback w STROKE] / 2)] [this.heads-to d]
|
||||
define [unimportant] : begin
|
||||
if (this.points && this.points.length && this.points.(this.points.length - 1)) : this.points.(this.points.length - 1).subdivided = true
|
||||
if (this.controlKnots && this.controlKnots.length && this.controlKnots.(this.controlKnots.length - 1)) : this.controlKnots.(this.controlKnots.length - 1).unimportant = true
|
||||
define [important] nothing
|
||||
|
||||
# Interpolation pesudoknots
|
||||
define [afInterpolate before after args] : g4
|
||||
mix before.x after.x args.rx
|
||||
mix before.y after.y args.ry
|
||||
fallback args.raf unimportant
|
||||
define [afInterpolateThem before after args] : begin
|
||||
local knots {}
|
||||
foreach {rx ry preserve} [items-of args.rs] : knots.push : [fallback args.ty g2] [mix before.x after.x rx] [mix before.y after.y ry] : fallback args.raf : match preserve
|
||||
1 before.af
|
||||
2 after.af
|
||||
otherwise unimportant
|
||||
return knots
|
||||
define [alsothru rx ry raf] {.type 'interpolate' .rx rx .ry ry .raf raf .af afInterpolate}
|
||||
define [alsothruthem rs raf ty] {.type 'interpolate' .rs rs .raf raf .ty ty .af afInterpolateThem}
|
||||
define [bez2 a b c t] : (1 - t) * (1 - t) * a + 2 * (1 - t) * t * b + t * t * c
|
||||
define [bez3 a b c d t] : (1 - t) * (1 - t) * (1 - t) * a + 3 * (1 - t) * (1 - t) * t * b + 3 * t * t * (1 - t) * c + t * t * t * d
|
||||
define [bezcontrols x1 y1 x2 y2 _samples raf ty] : begin
|
||||
local samples : fallback _samples 3
|
||||
local rs {}
|
||||
foreach j [range 1 samples] : rs.push : list
|
||||
bez3 0 x1 x2 1 (j / samples)
|
||||
bez3 0 y1 y2 1 (j / samples)
|
||||
alsothruthem rs raf ty
|
||||
define [quadcontrols x1 y1 samples raf ty] : bezcontrols (x1 * 2 / 3) (y1 * 2 / 3) [mix 1 x1 (2 / 3)] [mix 1 y1 (2 / 3)] samples raf ty
|
||||
|
||||
define [bezcontrols.absolute x1 y1 x2 y2 _samples raf ty] : object [type 'interpolate'] : af : lambda [before after] : begin
|
||||
local samples : fallback _samples 3
|
||||
local rs {}
|
||||
foreach j [range 1 samples] : rs.push : g4
|
||||
bez3 before.x x1 x2 after.x (j / samples)
|
||||
bez3 before.y y1 y2 after.y (j / samples)
|
||||
fallback raf unimportant
|
||||
return rs
|
||||
|
||||
|
||||
define {jhv, jvh} : let [cache {}] : begin
|
||||
local [build samples] : begin
|
||||
local hv {}
|
||||
local vh {}
|
||||
foreach [j : range 1 samples] : begin
|
||||
local theta : j / samples * Math.PI / 2
|
||||
local c : Math.pow [Math.cos theta] (2 / SUPERNESS)
|
||||
local s : Math.pow [Math.sin theta] (2 / SUPERNESS)
|
||||
hv.push {s (1 - c)}
|
||||
vh.push {(1 - c) s}
|
||||
cache.(samples) = {.hv hv .vh vh}
|
||||
local [hv samples] : begin
|
||||
if [not cache.(samples)] : build samples
|
||||
return cache.(samples).hv
|
||||
local [vh samples] : begin
|
||||
if [not cache.(samples)] : build samples
|
||||
return cache.(samples).vh
|
||||
list hv vh
|
||||
define [archv samples notiny k raf] : alsothruthem [jhv [fallback samples 4]] raf
|
||||
define [arcvh samples notiny k raf] : alsothruthem [jvh [fallback samples 4]] raf
|
||||
|
||||
define [complexThru] : begin
|
||||
local a : {}.slice.call arguments
|
||||
return {.type 'interpolate' .af [lambda [before after args] : begin \\
|
||||
local ks {}
|
||||
foreach knot [items-of a] : ks.push [knot.af.call this before after knot]
|
||||
return ks
|
||||
]}
|
||||
define [flatten knots] : begin
|
||||
local a {}
|
||||
foreach p [items-of knots] : piecewise
|
||||
(p <@ Array) : set a : a.concat [flatten p]
|
||||
true : a.push p
|
||||
return a
|
||||
define [prepareSpiroKnots knots s] : begin
|
||||
local closed false
|
||||
local lastafs {}
|
||||
while (knots.0 && knots.0 <@ Function) : begin
|
||||
knots.0.call s
|
||||
set knots : knots.slice 1
|
||||
while (knots.(knots.length - 1) && (knots.(knots.length - 1).type === 'close' || knots.(knots.length - 1).type === 'end')) : begin
|
||||
set closed : knots.(knots.length - 1).type === 'close'
|
||||
lastafs.push knots.(knots.length - 1).af
|
||||
set knots : knots.slice 0 (-1)
|
||||
set knots : flatten knots
|
||||
if closed : knots.push knots.0
|
||||
foreach j [range 0 knots.length] : if (knots.(j) && knots.(j).type === 'interpolate') : begin
|
||||
set knots.(j) : knots.(j).af.call s knots.(j - 1) knots.(j + 1) knots.(j)
|
||||
if closed : knots.pop
|
||||
return {.knots [flatten knots] .closed closed .lastafs lastafs}
|
||||
|
||||
define [dispiro] : begin
|
||||
local s : new SpiroExpansionContext
|
||||
set s.gizmo globalTransform
|
||||
local {.knots knots .closed closed .lastafs lastafs} : prepareSpiroKnots [{}.slice.call arguments 0] s
|
||||
foreach knot [items-of knots] : let [ty knot.type] [af knot.af] : begin
|
||||
set knot.af : lambda [] : begin
|
||||
this.set-type ty
|
||||
if af : af.apply this arguments
|
||||
libspiro.spiroToBezierOnContext knots closed s
|
||||
foreach af [items-of lastafs] : if af : af.call s
|
||||
|
||||
local {.lhs lhs .rhs rhs} : s.expand [fallback s.contrast CONTRAST]
|
||||
if closed : then
|
||||
local g : new Glyph
|
||||
libspiro.spiroToBezierOnContext [lhs.slice 0 (-1)] true g true
|
||||
local lhsContour g.contours.0
|
||||
set g.contours {}
|
||||
libspiro.spiroToBezierOnContext [rhs.reverse :.slice 0 (-1)] true g true
|
||||
local rhsContour g.contours.0
|
||||
set g.contours {[lhsContour.concat rhsContour]}
|
||||
: else
|
||||
local g : new Glyph
|
||||
lhs.0.type = rhs.0.type = lhs.(lhs.length - 1).type = rhs.(rhs.length - 1).type = 'corner'
|
||||
libspiro.spiroToBezierOnContext [lhs.concat : rhs.reverse] true g true
|
||||
|
||||
return g
|
||||
define [spiro-outline] : let [k : {}.slice.call arguments 0] : lambda [] : begin
|
||||
local {.knots knots .closed closed .lastafs lastafs} : prepareSpiroKnots k this
|
||||
libspiro.spiroToBezierOnContext knots closed this
|
||||
foreach af [items-of lastafs] : if af : af.call this
|
||||
|
||||
return [object
|
||||
g4 g2 corner flat curl close end straight
|
||||
widths heading unimportant important
|
||||
alsothru alsothruthem bezcontrols quadcontrols archv arcvh complexThru
|
||||
dispiro spiro-outline]
|
||||
|
||||
exports.SetupBuilders = SetupBuilders
|
Loading…
Add table
Add a link
Reference in a new issue