253 lines
10 KiB
Text
253 lines
10 KiB
Text
extern isFinite
|
|
import 'bezier-js' as Bezier
|
|
import './point' as Point
|
|
import './transform' as : Transform && [object [transformPoint tp] [untransform utp] inverse]
|
|
import './anchor' as Anchor
|
|
|
|
define-macro xytransform : syntax-rules
|
|
`[xytransform @tfm @x @y] : begin
|
|
set [env.declarations.get [formOf x]].isParameter 0
|
|
set [env.declarations.get [formOf y]].isParameter 0
|
|
let [t : env.newt] `[begin \\
|
|
set @t @x
|
|
set @x : @x * @tfm.xx + @y * @tfm.yx + @tfm.x
|
|
set @y : @t * @tfm.xy + @y * @tfm.yy + @tfm.y
|
|
]
|
|
|
|
define [mix a b p] : a + (b - a) * p
|
|
define [ratio l r m] : if [l === r] 0 ((m - l) / (r - l))
|
|
define [byx a b] : a - b
|
|
define [fallback] : for [local j 0] (j < arguments.length) [inc j] : if (arguments.(j) !== nothing) : return arguments.(j)
|
|
|
|
define [closepoint p q t] : begin
|
|
return : [Math.abs (p.x - q.x)] <= t && [Math.abs (p.y - q.y)] <= t
|
|
define [oncurveRemovable a b c t] : begin
|
|
local xm : (a.x + c.x) / 2
|
|
local ym : (a.y + c.y) / 2
|
|
return : [not a.on] && b.on && [not c.on] && [not a.cubic] && [not c.cubic] && (a.x <= b.x && b.x <= c.x || a.x >= b.x && b.x >= c.x) && (a.y <= b.y && b.y <= c.y || a.y >= b.y && b.y >= c.y) && [Math.abs (b.x - xm)] <= (t / 2) && [Math.abs (b.y - ym)] <= (t / 2)
|
|
|
|
define PRECISION 1000
|
|
define [cov x] : piecewise
|
|
[isFinite x] : return : [Math.round : x * PRECISION] / PRECISION
|
|
true : return 0
|
|
|
|
export all : class Glyph
|
|
public [new name] : begin
|
|
set this.name name
|
|
set this.unicode {}
|
|
set this.contours {}
|
|
set this.advanceWidth 500
|
|
set this.cmpPriority 0
|
|
set this.anchors {.}
|
|
set this.gizmo : Transform.Id
|
|
set this.dependencies {}
|
|
set this.defaultTag null
|
|
return nothing
|
|
static is {.unapply [function [obj arity] [if (obj <@ Glyph) {obj} null]]}
|
|
|
|
public [set-width w] : begin
|
|
this.advanceWidth = w
|
|
return this
|
|
|
|
public [assign-unicode u] : begin
|
|
this.unicode.push : piecewise
|
|
([typeof u] === 'string') : u.charCodeAt 0
|
|
true u
|
|
return this
|
|
|
|
public [start-from x y] : begin
|
|
local contour {[Point.transformed this.gizmo x y true]}
|
|
set contour.tag this.defaultTag
|
|
this.contours.push contour
|
|
return this
|
|
public moveTo : public start-from
|
|
|
|
public [line-to x y] : begin
|
|
this.contours.((this.contours.length - 1)).push [Point.transformed this.gizmo x y true]
|
|
return this
|
|
public lineTo : public line-to
|
|
|
|
public [curve-control x y] : begin
|
|
this.contours.((this.contours.length - 1)).push [tp this.gizmo [new Point x y false]]
|
|
return this
|
|
|
|
public [curve-to xc yc x y] : begin
|
|
this.contours.((this.contours.length - 1)).push [Point.transformed this.gizmo xc yc false] [Point.transformed this.gizmo x y true]
|
|
return this
|
|
public curveTo : public curve-to
|
|
|
|
public [cubic-to x1 y1 x2 y2 x y] : begin
|
|
#local lastContour this.contours.(this.contours.length - 1)
|
|
#local lastPoint : utp this.gizmo lastContour.(lastContour.length - 1)
|
|
#local segments : bezierCubic2Q2 lastPoint {.x x1 .y y1} {.x x2 .y y2} {.x x .y y}
|
|
#foreach {p0 {.x xc .y yc} {.x xf .y yf}} [items-of segments] : lastContour.push [Point.transformed this.gizmo xc yc false] [Point.transformed this.gizmo xf yf true]
|
|
this.contours.((this.contours.length - 1)).push
|
|
Point.transformed this.gizmo x1 y1 false true
|
|
Point.transformed this.gizmo x2 y2 false true
|
|
Point.transformed this.gizmo x y true
|
|
return this
|
|
public cubicTo : public cubic-to
|
|
|
|
public [reverse-last] : begin
|
|
if [this.contours && this.contours.(this.contours.length - 1)] : begin
|
|
this.contours.(this.contours.length - 1) = [this.contours.(this.contours.length - 1).reverse]
|
|
return this
|
|
public reverseLast : public reverse-last
|
|
|
|
public [tag-contour tag n] : begin
|
|
if this.contours : begin
|
|
local lastContour this.contours.(this.contours.length - 1)
|
|
if lastContour : if tag : set lastContour.tag tag
|
|
return this
|
|
public [retag-contour oldtag newtag] : begin
|
|
if this.contours : foreach [c : items-of this.contours] : if (c.tag === oldtag) : set c.tag newtag
|
|
return this
|
|
public [eject-contour tag] : begin
|
|
set this.contours : this.contours.filter : lambda [c] (c.tag !== tag)
|
|
return this
|
|
public [depends-on glyph] : begin
|
|
piecewise
|
|
glyph.name : this.dependencies.push glyph.name
|
|
glyph.dependencies : this.dependencies = [this.dependencies.concat glyph.dependencies]
|
|
return this
|
|
public [include component copyAnchors copyWidth] : begin
|
|
piecewise
|
|
(component <@ Function) : begin
|
|
local t this.defaultTag
|
|
if component.tag : set this.defaultTag component.tag
|
|
component.call this
|
|
set this.defaultTag t
|
|
return this
|
|
(component <@ Transform) : return : this.apply-transform component copyAnchors
|
|
(component <@ Array) : begin
|
|
local contours component
|
|
local glyph {.contours contours}
|
|
true : begin
|
|
local contours component.contours
|
|
local glyph component
|
|
|
|
local shiftx 0
|
|
local shifty 0
|
|
local t this
|
|
if (this.anchors && glyph.anchors) : foreach markid [items-of [Object.keys this.anchors]] : begin
|
|
local anchorThis this.anchors.(markid)
|
|
local anchorThat glyph.anchors.(markid)
|
|
if ( anchorThis && (anchorThis.type === Anchor.BASE || anchorThis.mbx !== nothing && anchorThis.mby !== nothing)
|
|
&& anchorThat && anchorThat.type === Anchor.MARK) : begin
|
|
set shiftx : [fallback anchorThis.mbx anchorThis.x] - anchorThat.x
|
|
set shifty : [fallback anchorThis.mby anchorThis.y] - anchorThat.y
|
|
# we have a mark-to-mark position
|
|
if (anchorThat.mbx !== nothing && anchorThat.mby !== nothing) : if (anchorThis.type === Anchor.BASE)
|
|
then : set this.anchors.(markid) : new Anchor
|
|
* (anchorThis.x + anchorThat.mbx - anchorThat.x)
|
|
* (anchorThis.y + anchorThat.mby - anchorThat.y)
|
|
* Anchor.BASE
|
|
else : set this.anchors.(markid) : new Anchor
|
|
* anchorThis.x
|
|
* anchorThis.y
|
|
* anchorThis.type
|
|
* (anchorThis.mbx + anchorThat.mbx - anchorThat.x)
|
|
* (anchorThis.mby + anchorThat.mby - anchorThat.y)
|
|
if contours : begin
|
|
local newcontours {}
|
|
foreach [contour : items-of contours] : begin
|
|
local c {}
|
|
set c.tag : contour.tag || component.tag || t.defaultTag
|
|
foreach [point : items-of contour] : begin
|
|
c.push : new Point (point.x + shiftx) (point.y + shifty) point.on point.cubic point.subdivided
|
|
newcontours.push c
|
|
set this.contours : this.contours.concat newcontours
|
|
if (([not contours] || copyAnchors) && glyph.anchors) : begin
|
|
foreach [k : items-of : Object.keys glyph.anchors] : set this.anchors.(k) glyph.anchors.(k)
|
|
if (glyph.advanceWidth >= 0 && copyWidth) : set this.advanceWidth glyph.advanceWidth
|
|
piecewise
|
|
glyph.name : this.dependencies.push glyph.name
|
|
glyph.dependencies : this.dependencies = [this.dependencies.concat glyph.dependencies]
|
|
return this
|
|
|
|
public [apply-transform transform alsoAnchors] : begin
|
|
foreach [c : items-of this.contours] : foreach [j : range 0 c.length] : set c.(j) : tp transform c.(j)
|
|
if alsoAnchors : foreach key [items-of [Object.keys this.anchors]] : begin
|
|
set this.anchors.(key) : Anchor.transform transform this.anchors.(key)
|
|
return this
|
|
|
|
public [set-anchor id type x y mbx mby] : begin
|
|
xytransform this.gizmo x y
|
|
if (mbx !== nothing && mby !== nothing)
|
|
: then : begin
|
|
xytransform this.gizmo mbx mby
|
|
set this.anchors.(id) : new Anchor x y type mbx mby
|
|
: else : set this.anchors.(id) : new Anchor x y type
|
|
return this
|
|
|
|
static [contourToSVGPath contour delta] : if (contour && contour.length) : begin
|
|
local lx contour.0.x
|
|
local ly contour.0.y
|
|
local buf : if delta "" "M \[cov lx] \[cov ly]"
|
|
for [local j 1] (j < contour.length) [inc j] : begin
|
|
local point contour.(j)
|
|
piecewise
|
|
point.on : begin
|
|
if delta
|
|
: then : set buf : buf + "l \[cov : point.x - lx] \[cov : point.y - ly]"
|
|
: else : set buf : buf + "L \[cov point.x] \[cov point.y]"
|
|
set {.x lx .y ly} point
|
|
point.cubic : begin
|
|
local z1 point
|
|
local z2 contour.(j + 1)
|
|
local z3 contour.(j + 2)
|
|
if delta
|
|
: then : set buf : buf + "c \[cov : z1.x - lx] \[cov : z1.y - ly] \[cov : z2.x - lx] \[cov : z2.y - ly] \[cov : z3.x - lx] \[cov : z3.y - ly]"
|
|
: else : set buf : buf + "C \[cov z1.x] \[cov z1.y] \[cov z2.x] \[cov z2.y] \[cov z3.x] \[cov z3.y]"
|
|
set {.x lx .y ly} z3
|
|
set j : j + 2
|
|
true : begin
|
|
local zc point
|
|
local zf : if contour.(j + 1) contour.(j + 1) contour.0
|
|
local x1 : mix lx zc.x (2 / 3)
|
|
local y1 : mix ly zc.y (2 / 3)
|
|
local x2 : mix zf.x zc.x (2 / 3)
|
|
local y2 : mix zf.y zc.y (2 / 3)
|
|
if delta
|
|
: then : set buf : buf + "c \[cov : x1 - lx] \[cov : y1 - ly] \[cov : x2 - lx] \[cov : y2 - ly] \[cov : zf.x - lx] \[cov : zf.y - ly]"
|
|
: else : set buf : buf + "C \[cov x1] \[cov y1] \[cov x2] \[cov y2] \[cov zf.x] \[cov zf.y]"
|
|
set {.x lx .y ly} zf
|
|
inc j
|
|
set buf : buf + " Z\n"
|
|
return buf
|
|
|
|
static [contourToStandardCubic contour] : begin
|
|
local c {}
|
|
if (!contour || !contour.length) : return c
|
|
|
|
local lx contour.0.x
|
|
local ly contour.0.y
|
|
c.push {.x lx .y ly .on true}
|
|
for [local j 1] (j < contour.length) [inc j] : begin
|
|
local point contour.(j)
|
|
piecewise
|
|
point.on : begin
|
|
c.push {.x point.x .y point.y .on true}
|
|
set {.x lx .y ly} point
|
|
point.cubic : begin
|
|
local z1 point
|
|
local z2 contour.(j + 1)
|
|
local z3 contour.(j + 2)
|
|
c.push : new Point z1.x z1.y false true false
|
|
c.push : new Point z2.x z2.y false true false
|
|
c.push : new Point z3.x z3.y true false false
|
|
set {.x lx .y ly} z3
|
|
set j : j + 2
|
|
true : begin
|
|
local zc point
|
|
local zf : if contour.(j + 1) contour.(j + 1) contour.0
|
|
local x1 : mix lx zc.x (2 / 3)
|
|
local y1 : mix ly zc.y (2 / 3)
|
|
local x2 : mix zf.x zc.x (2 / 3)
|
|
local y2 : mix zf.y zc.y (2 / 3)
|
|
c.push : new Point x1 y1 false true false
|
|
c.push : new Point x2 y2 false true false
|
|
c.push : new Point zf.x zf.y true false false
|
|
set {.x lx .y ly} zf
|
|
inc j
|
|
return c
|