So should we have OOP?
This commit is contained in:
parent
c686715797
commit
c07e07ed7e
|
@ -3,7 +3,7 @@
|
|||
"version": "1.7.3",
|
||||
"main": "./generate.js",
|
||||
"dependencies": {
|
||||
"patel": ">=0.27.0",
|
||||
"patel": ">=0.27.2",
|
||||
"node-sfnt": ">=0.0.20",
|
||||
"bezier-js": "*",
|
||||
"yargs": "*",
|
||||
|
|
|
@ -17,149 +17,6 @@ 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)
|
||||
|
||||
export all : define [Glyph name] : begin
|
||||
set this.name name
|
||||
set this.unicode {}
|
||||
set this.contours {}
|
||||
set this.advanceWidth 500
|
||||
set this.anchors {.}
|
||||
set this.gizmo : Transform.Id
|
||||
set this.dependencies {}
|
||||
set this.defaultTag null
|
||||
return nothing
|
||||
|
||||
define Glyph.is {.unapply [function [obj arity] [if (obj <@ Glyph) {obj} null]]}
|
||||
define [Glyph.prototype.set-width w] : begin
|
||||
this.advanceWidth = w
|
||||
return this
|
||||
|
||||
define [Glyph.prototype.assign-unicode u] : begin
|
||||
this.unicode.push : piecewise
|
||||
([typeof u] === 'string') : u.charCodeAt 0
|
||||
true u
|
||||
return this
|
||||
|
||||
|
||||
define [Glyph.prototype.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
|
||||
Glyph.prototype.moveTo = Glyph.prototype.start-from
|
||||
|
||||
define [Glyph.prototype.line-to x y] : begin
|
||||
this.contours.((this.contours.length - 1)).push [Point.transformed this.gizmo x y true]
|
||||
return this
|
||||
Glyph.prototype.lineTo = Glyph.prototype.line-to
|
||||
|
||||
define [Glyph.prototype.curve-control x y] : begin
|
||||
this.contours.((this.contours.length - 1)).push [tp this.gizmo [new Point x y false]]
|
||||
return this
|
||||
|
||||
define [Glyph.prototype.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
|
||||
Glyph.prototype.curveTo = Glyph.prototype.curve-to
|
||||
|
||||
define [Glyph.prototype.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
|
||||
Glyph.prototype.cubicTo = Glyph.prototype.cubic-to
|
||||
|
||||
define [Glyph.prototype.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
|
||||
Glyph.prototype.reverseLast = Glyph.prototype.reverse-last
|
||||
|
||||
define [Glyph.prototype.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
|
||||
define [Glyph.prototype.retag-contour oldtag newtag] : begin
|
||||
if this.contours : foreach [c : items-of this.contours] : if (c.tag === oldtag) : set c.tag newtag
|
||||
return this
|
||||
define [Glyph.prototype.eject-contour tag] : begin
|
||||
set this.contours : this.contours.filter : lambda [c] (c.tag !== tag)
|
||||
return this
|
||||
|
||||
define [Glyph.prototype.include component copyAnchors] : 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.onCurve 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)
|
||||
piecewise
|
||||
glyph.name : this.dependencies.push glyph.name
|
||||
glyph.dependencies : this.dependencies = [this.dependencies.concat glyph.dependencies]
|
||||
return this
|
||||
|
||||
define [Glyph.prototype.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
|
||||
|
||||
define [Glyph.prototype.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
|
||||
|
||||
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
|
||||
|
@ -167,88 +24,231 @@ define [oncurveRemovable a b c t] : begin
|
|||
local ym : (a.y + c.y) / 2
|
||||
return : [not a.onCurve] && b.onCurve && [not c.onCurve] && [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 [Glyph.prototype.cleanup t] : begin
|
||||
foreach c [range 0 this.contours.length] : begin
|
||||
local ocontour this.contours.(c)
|
||||
# add infections
|
||||
local contour {[new Point ocontour.0.x ocontour.0.y ocontour.0.onCurve]}
|
||||
local flag 0
|
||||
foreach [j : range 1 (ocontour.length - 1)] : piecewise
|
||||
flag : dec flag
|
||||
ocontour.(j).onCurve : contour.push ocontour.(j)
|
||||
ocontour.(j).cubic : begin
|
||||
local p0 contour.(contour.length - 1)
|
||||
local p1 ocontour.(j)
|
||||
local p2 ocontour.(j + 1)
|
||||
local p3 ocontour.(j + 2)
|
||||
local strand : new Bezier p0.x p0.y p1.x p1.y p2.x p2.y p3.x p3.y
|
||||
local ts [strand.inflections].y
|
||||
piecewise
|
||||
(!ts || ts.length < 1) : contour.push p1 p2 p3
|
||||
true : begin
|
||||
set ts [ts.sort byx]
|
||||
ts.unshift 0
|
||||
ts.push 1
|
||||
foreach [k : range 0 (ts.length - 1)] : begin
|
||||
local s : strand.split ts.(k) ts.(k + 1)
|
||||
if s.points : contour.push
|
||||
new Point s.points.1.x s.points.1.y false true
|
||||
new Point s.points.2.x s.points.2.y false true
|
||||
new Point s.points.3.x s.points.3.y true
|
||||
set flag 2
|
||||
true : begin
|
||||
local p0 contour.(contour.length - 1)
|
||||
local p1 ocontour.(j)
|
||||
local p2 ocontour.(j + 1)
|
||||
if [not p2.onCurve] : set p2 : new Point [mix p1.x p2.x 0.5] [mix p1.y p2.y 0.5] true
|
||||
local strand : new Bezier p0.x p0.y p1.x p1.y p2.x p2.y
|
||||
local ts [strand.inflections].y
|
||||
piecewise
|
||||
(!ts || ts.length < 1) : contour.push p1 p2
|
||||
true : begin
|
||||
set ts [ts.sort byx]
|
||||
ts.unshift 0
|
||||
ts.push 1
|
||||
foreach [k : range 0 (ts.length - 1)] : begin
|
||||
local s : strand.split ts.(k) ts.(k + 1)
|
||||
if s.points : contour.push
|
||||
new Point s.points.1.x s.points.1.y false
|
||||
new Point s.points.2.x s.points.2.y true
|
||||
set flag 1
|
||||
export all : class Glyph
|
||||
public [new name] : begin
|
||||
set this.name name
|
||||
set this.unicode {}
|
||||
set this.contours {}
|
||||
set this.advanceWidth 500
|
||||
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
|
||||
|
||||
contour.push [new Point ocontour.(ocontour.length - 1).x ocontour.(ocontour.length - 1).y ocontour.(ocontour.length - 1).onCurve]
|
||||
|
||||
# cleanup
|
||||
local cleanedContour {}
|
||||
foreach j [range 1 : contour.length - 1] : begin
|
||||
local p0 contour.(j - 1)
|
||||
local p1 contour.(j)
|
||||
local p2 contour.(j + 1)
|
||||
if [oncurveRemovable p0 p1 p2 t] : set p1.unimportant true
|
||||
foreach point [items-of contour] : if [not point.unimportant] : cleanedContour.push point
|
||||
set contour cleanedContour
|
||||
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 Type.prototype.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 Type.prototype.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 Type.prototype.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 Type.prototype.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 Type.prototype.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 [include component copyAnchors] : 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.onCurve 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)
|
||||
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
|
||||
|
||||
public [cleanup t] : begin
|
||||
foreach c [range 0 this.contours.length] : begin
|
||||
local ocontour this.contours.(c)
|
||||
# add infections
|
||||
local contour {[new Point ocontour.0.x ocontour.0.y ocontour.0.onCurve]}
|
||||
local flag 0
|
||||
foreach [j : range 1 (ocontour.length - 1)] : piecewise
|
||||
flag : dec flag
|
||||
ocontour.(j).onCurve : contour.push ocontour.(j)
|
||||
ocontour.(j).cubic : begin
|
||||
local p0 contour.(contour.length - 1)
|
||||
local p1 ocontour.(j)
|
||||
local p2 ocontour.(j + 1)
|
||||
local p3 ocontour.(j + 2)
|
||||
local strand : new Bezier p0.x p0.y p1.x p1.y p2.x p2.y p3.x p3.y
|
||||
local ts [strand.inflections].y
|
||||
piecewise
|
||||
(!ts || ts.length < 1) : contour.push p1 p2 p3
|
||||
true : begin
|
||||
set ts [ts.sort byx]
|
||||
ts.unshift 0
|
||||
ts.push 1
|
||||
foreach [k : range 0 (ts.length - 1)] : begin
|
||||
local s : strand.split ts.(k) ts.(k + 1)
|
||||
if s.points : contour.push
|
||||
new Point s.points.1.x s.points.1.y false true
|
||||
new Point s.points.2.x s.points.2.y false true
|
||||
new Point s.points.3.x s.points.3.y true
|
||||
set flag 2
|
||||
true : begin
|
||||
local p0 contour.(contour.length - 1)
|
||||
local p1 ocontour.(j)
|
||||
local p2 ocontour.(j + 1)
|
||||
if [not p2.onCurve] : set p2 : new Point [mix p1.x p2.x 0.5] [mix p1.y p2.y 0.5] true
|
||||
local strand : new Bezier p0.x p0.y p1.x p1.y p2.x p2.y
|
||||
local ts [strand.inflections].y
|
||||
piecewise
|
||||
(!ts || ts.length < 1) : contour.push p1 p2
|
||||
true : begin
|
||||
set ts [ts.sort byx]
|
||||
ts.unshift 0
|
||||
ts.push 1
|
||||
foreach [k : range 0 (ts.length - 1)] : begin
|
||||
local s : strand.split ts.(k) ts.(k + 1)
|
||||
if s.points : contour.push
|
||||
new Point s.points.1.x s.points.1.y false
|
||||
new Point s.points.2.x s.points.2.y true
|
||||
set flag 1
|
||||
|
||||
contour.push [new Point ocontour.(ocontour.length - 1).x ocontour.(ocontour.length - 1).y ocontour.(ocontour.length - 1).onCurve]
|
||||
|
||||
set cleanedContour {}
|
||||
foreach j [range 0 contour.length] : if ([not contour.(j).cubic] && [not contour.(j).unimportant]) : begin
|
||||
local found false
|
||||
for [local k : j + 1] ((k < contour.length) && (
|
||||
[not contour.(k).cubic] && [closepoint contour.(j) contour.(k) t]
|
||||
|| contour.(k).cubic && contour.(k + 1).cubic
|
||||
&& [closepoint contour.(j) contour.(k) t]
|
||||
&& [closepoint contour.(j) contour.(k) t]
|
||||
&& [closepoint contour.(j) contour.(k + 2) t])) [inc k] : begin
|
||||
set contour.(k).unimportant true
|
||||
set found true
|
||||
if (contour.(k).cubic && contour.(k + 1).cubic
|
||||
&& [closepoint contour.(j) contour.(k) t]
|
||||
&& [closepoint contour.(j) contour.(k) t]
|
||||
&& [closepoint contour.(j) contour.(k + 2) t]) : begin
|
||||
set contour.(k + 1).unimportant true
|
||||
set contour.(k + 2).unimportant true
|
||||
set k : k + 2
|
||||
if found : begin
|
||||
set contour.(j).onCurve true
|
||||
set j (k - 1)
|
||||
foreach point [items-of contour] : if [not point.unimportant] : cleanedContour.push point
|
||||
this.contours.(c) = cleanedContour
|
||||
return this
|
||||
# cleanup
|
||||
local cleanedContour {}
|
||||
foreach j [range 1 : contour.length - 1] : begin
|
||||
local p0 contour.(j - 1)
|
||||
local p1 contour.(j)
|
||||
local p2 contour.(j + 1)
|
||||
if [oncurveRemovable p0 p1 p2 t] : set p1.unimportant true
|
||||
foreach point [items-of contour] : if [not point.unimportant] : cleanedContour.push point
|
||||
set contour cleanedContour
|
||||
|
||||
set cleanedContour {}
|
||||
foreach j [range 0 contour.length] : if ([not contour.(j).cubic] && [not contour.(j).unimportant]) : begin
|
||||
local found false
|
||||
for [local k : j + 1] ((k < contour.length) && (
|
||||
[not contour.(k).cubic] && [closepoint contour.(j) contour.(k) t]
|
||||
|| contour.(k).cubic && contour.(k + 1).cubic
|
||||
&& [closepoint contour.(j) contour.(k) t]
|
||||
&& [closepoint contour.(j) contour.(k) t]
|
||||
&& [closepoint contour.(j) contour.(k + 2) t])) [inc k] : begin
|
||||
set contour.(k).unimportant true
|
||||
set found true
|
||||
if (contour.(k).cubic && contour.(k + 1).cubic
|
||||
&& [closepoint contour.(j) contour.(k) t]
|
||||
&& [closepoint contour.(j) contour.(k) t]
|
||||
&& [closepoint contour.(j) contour.(k + 2) t]) : begin
|
||||
set contour.(k + 1).unimportant true
|
||||
set contour.(k + 2).unimportant true
|
||||
set k : k + 2
|
||||
if found : begin
|
||||
set contour.(j).onCurve true
|
||||
set j (k - 1)
|
||||
foreach point [items-of contour] : if [not point.unimportant] : cleanedContour.push point
|
||||
this.contours.(c) = cleanedContour
|
||||
return this
|
|
@ -11,145 +11,138 @@ define-macro xytransform : syntax-rules
|
|||
set @y : @t * @tfm.xy + @y * @tfm.yy + @tfm.y
|
||||
]
|
||||
|
||||
export all : define [SpiroExpansionContext] : begin
|
||||
set this.gizmo [Transform.Id]
|
||||
set this.controlKnots {}
|
||||
set this.defaultd1 0
|
||||
set this.defaultd2 0
|
||||
return nothing
|
||||
|
||||
define [SpiroExpansionContext.prototype.moveTo x y unimportant] : begin
|
||||
if unimportant : return nothing
|
||||
# Transform incoming knots using gizmo
|
||||
xytransform this.gizmo x y
|
||||
this.controlKnots.push {.x x .y y .type 'g4' .d1 this.defaultd1 .d2 this.defaultd2}
|
||||
|
||||
define [SpiroExpansionContext.prototype.lineTo x y unimportant] : begin
|
||||
local lastKnot this.controlKnots.(this.controlKnots.length - 1)
|
||||
xytransform this.gizmo x y
|
||||
local thisKnot {.x x .y y .type 'g4' .d1 lastKnot.d1 .d2 lastKnot.d2}
|
||||
if lastKnot : begin
|
||||
local normalAngle : Math.PI / 2 + [Math.atan2 (y - lastKnot.y) (x - lastKnot.x)]
|
||||
set thisKnot.normalAngle normalAngle
|
||||
if (lastKnot.normalAngle === nothing) : set lastKnot.normalAngle normalAngle
|
||||
if [not unimportant] : this.controlKnots.push thisKnot
|
||||
|
||||
define [SpiroExpansionContext.prototype.cubicTo x1 y1 x2 y2 x y unimportant] : begin
|
||||
local lastKnot this.controlKnots.(this.controlKnots.length - 1)
|
||||
xytransform this.gizmo x1 y1
|
||||
xytransform this.gizmo x2 y2
|
||||
xytransform this.gizmo x y
|
||||
local thisKnot {.x x .y y .type 'g4' .d1 lastKnot.d1 .d2 lastKnot.d2}
|
||||
if (lastKnot && lastKnot.normalAngle === nothing) : begin
|
||||
local normalAngle : Math.PI / 2 + [Math.atan2 (y1 - lastKnot.y) (x1 - lastKnot.x)]
|
||||
if (lastKnot.normalAngle === nothing) : set lastKnot.normalAngle normalAngle
|
||||
if [not unimportant] : begin
|
||||
local normalAngle : Math.PI / 2 + [Math.atan2 (y - y2) (x - x2)]
|
||||
set thisKnot.normalAngle normalAngle
|
||||
this.controlKnots.push thisKnot
|
||||
|
||||
define [SpiroExpansionContext.prototype.set-width l r] : begin
|
||||
local lastKnot this.controlKnots.(this.controlKnots.length - 1)
|
||||
if lastKnot : then
|
||||
lastKnot.d1 = l; lastKnot.d2 = r
|
||||
: else
|
||||
this.defaultd1 = l; this.defaultd2 = r
|
||||
|
||||
define [SpiroExpansionContext.prototype.heads-to direction] : begin
|
||||
local lastKnot this.controlKnots.(this.controlKnots.length - 1)
|
||||
if lastKnot : begin
|
||||
lastKnot.proposedNormal = direction
|
||||
|
||||
define [SpiroExpansionContext.prototype.set-type type] : begin
|
||||
local lastKnot this.controlKnots.(this.controlKnots.length - 1)
|
||||
if lastKnot : begin
|
||||
lastKnot.type = type
|
||||
|
||||
|
||||
define [shortestAngle end start] : begin
|
||||
local a : (end - start) % (Math.PI * 2)
|
||||
if (a > Math.PI / 2) : a = a - Math.PI
|
||||
return a
|
||||
|
||||
|
||||
define [normalY angle] : Math.sin angle
|
||||
define [normalX angle vex] : [Math.cos angle] * vex
|
||||
|
||||
define [SpiroExpansionContext.prototype.expand contrast] : begin
|
||||
local lhs {}
|
||||
local rhs {}
|
||||
|
||||
local contrast : fallback contrast (1 / 0.9)
|
||||
|
||||
local d1s {}
|
||||
local d2s {}
|
||||
local dxs {}
|
||||
local dys {}
|
||||
local js {}
|
||||
foreach j [range 0 this.controlKnots.length] : if [not this.controlKnots.(j).unimportant] : begin
|
||||
local knot this.controlKnots.(j)
|
||||
js.push j
|
||||
d1s.push knot.d1
|
||||
d2s.push knot.d2
|
||||
if knot.proposedNormal : then
|
||||
dxs.push : knot.proposedNormal.x - [normalX knot.normalAngle contrast]
|
||||
dys.push : knot.proposedNormal.y - [normalY knot.normalAngle contrast]
|
||||
: else
|
||||
dxs.push 0; dys.push 0
|
||||
local fd1 : smooth js d1s
|
||||
local fd2 : smooth js d2s
|
||||
local fdx : smooth js dxs
|
||||
local fdy : smooth js dys
|
||||
# console.log deltaAngles
|
||||
export all : class SpiroExpansionContext
|
||||
public [new] : begin
|
||||
set this.gizmo [Transform.Id]
|
||||
set this.controlKnots {}
|
||||
set this.defaultd1 0
|
||||
set this.defaultd2 0
|
||||
|
||||
# interpolate important knots
|
||||
foreach j [range 0 this.controlKnots.length] : begin
|
||||
local knot this.controlKnots.(j)
|
||||
if [not knot.unimportant] : begin
|
||||
set lhs.(j) : object
|
||||
x : knot.x + ([fdx j] + [normalX knot.normalAngle contrast]) * [fd1 j]
|
||||
y : knot.y + ([fdy j] + [normalY knot.normalAngle contrast]) * [fd1 j]
|
||||
type knot.type
|
||||
set rhs.(j) : object
|
||||
x : knot.x - ([fdx j] + [normalX knot.normalAngle contrast]) * [fd2 j]
|
||||
y : knot.y - ([fdy j] + [normalY knot.normalAngle contrast]) * [fd2 j]
|
||||
type : match knot.type
|
||||
"left" "right"
|
||||
"right" "left"
|
||||
type type
|
||||
# interpolate unimportant knots referencing their original position relationship
|
||||
foreach j [range 0 this.controlKnots.length] : begin
|
||||
local knot this.controlKnots.(j)
|
||||
if knot.unimportant : begin
|
||||
local jBefore (j - 1)
|
||||
while this.controlKnots.(jBefore).unimportant : dec jBefore
|
||||
local jAfter (j + 1)
|
||||
while this.controlKnots.(jAfter).unimportant : inc jAfter
|
||||
|
||||
local knotBefore : utp this.gizmo this.controlKnots.(jBefore)
|
||||
local knotAfter : utp this.gizmo this.controlKnots.(jAfter)
|
||||
local ref : utp this.gizmo knot
|
||||
local lhsBefore : utp this.gizmo lhs.(jBefore)
|
||||
local lhsAfter : utp this.gizmo lhs.(jAfter)
|
||||
local rhsBefore : utp this.gizmo rhs.(jBefore)
|
||||
local rhsAfter : utp this.gizmo rhs.(jAfter)
|
||||
|
||||
local kLHS : tp this.gizmo : object
|
||||
x : linreg knotBefore.x lhsBefore.x knotAfter.x lhsAfter.x ref.x
|
||||
y : linreg knotBefore.y lhsBefore.y knotAfter.y lhsAfter.y ref.y
|
||||
local kRHS : tp this.gizmo : object
|
||||
x : linreg knotBefore.x rhsBefore.x knotAfter.x rhsAfter.x ref.x
|
||||
y : linreg knotBefore.y rhsBefore.y knotAfter.y rhsAfter.y ref.y
|
||||
|
||||
set lhs.(j) : object
|
||||
x kLHS.x
|
||||
y kLHS.y
|
||||
type knot.type
|
||||
set rhs.(j) : object
|
||||
x kRHS.x
|
||||
y kRHS.y
|
||||
type : match knot.type
|
||||
"left" "right"
|
||||
"right" "left"
|
||||
type type
|
||||
return {.lhs lhs .rhs rhs}
|
||||
public [moveTo x y unimportant] : begin
|
||||
if unimportant : return nothing
|
||||
# Transform incoming knots using gizmo
|
||||
xytransform this.gizmo x y
|
||||
this.controlKnots.push {.x x .y y .type 'g4' .d1 this.defaultd1 .d2 this.defaultd2}
|
||||
|
||||
public [lineTo x y unimportant] : begin
|
||||
local lastKnot this.controlKnots.(this.controlKnots.length - 1)
|
||||
xytransform this.gizmo x y
|
||||
local thisKnot {.x x .y y .type 'g4' .d1 lastKnot.d1 .d2 lastKnot.d2}
|
||||
if lastKnot : begin
|
||||
local normalAngle : Math.PI / 2 + [Math.atan2 (y - lastKnot.y) (x - lastKnot.x)]
|
||||
set thisKnot.normalAngle normalAngle
|
||||
if (lastKnot.normalAngle === nothing) : set lastKnot.normalAngle normalAngle
|
||||
if [not unimportant] : this.controlKnots.push thisKnot
|
||||
|
||||
public [cubicTo x1 y1 x2 y2 x y unimportant] : begin
|
||||
local lastKnot this.controlKnots.(this.controlKnots.length - 1)
|
||||
xytransform this.gizmo x1 y1
|
||||
xytransform this.gizmo x2 y2
|
||||
xytransform this.gizmo x y
|
||||
local thisKnot {.x x .y y .type 'g4' .d1 lastKnot.d1 .d2 lastKnot.d2}
|
||||
if (lastKnot && lastKnot.normalAngle === nothing) : begin
|
||||
local normalAngle : Math.PI / 2 + [Math.atan2 (y1 - lastKnot.y) (x1 - lastKnot.x)]
|
||||
if (lastKnot.normalAngle === nothing) : set lastKnot.normalAngle normalAngle
|
||||
if [not unimportant] : begin
|
||||
local normalAngle : Math.PI / 2 + [Math.atan2 (y - y2) (x - x2)]
|
||||
set thisKnot.normalAngle normalAngle
|
||||
this.controlKnots.push thisKnot
|
||||
|
||||
public [set-width l r] : begin
|
||||
local lastKnot this.controlKnots.(this.controlKnots.length - 1)
|
||||
if lastKnot : then
|
||||
lastKnot.d1 = l; lastKnot.d2 = r
|
||||
: else
|
||||
this.defaultd1 = l; this.defaultd2 = r
|
||||
|
||||
public [heads-to direction] : begin
|
||||
local lastKnot this.controlKnots.(this.controlKnots.length - 1)
|
||||
if lastKnot : begin
|
||||
lastKnot.proposedNormal = direction
|
||||
|
||||
public [set-type type] : begin
|
||||
local lastKnot this.controlKnots.(this.controlKnots.length - 1)
|
||||
if lastKnot : begin
|
||||
lastKnot.type = type
|
||||
|
||||
public [expand contrast] : begin
|
||||
local lhs {}
|
||||
local rhs {}
|
||||
|
||||
local contrast : fallback contrast (1 / 0.9)
|
||||
|
||||
local d1s {}
|
||||
local d2s {}
|
||||
local dxs {}
|
||||
local dys {}
|
||||
local js {}
|
||||
foreach j [range 0 this.controlKnots.length] : if [not this.controlKnots.(j).unimportant] : begin
|
||||
local knot this.controlKnots.(j)
|
||||
js.push j
|
||||
d1s.push knot.d1
|
||||
d2s.push knot.d2
|
||||
if knot.proposedNormal : then
|
||||
dxs.push : knot.proposedNormal.x - [normalX knot.normalAngle contrast]
|
||||
dys.push : knot.proposedNormal.y - [normalY knot.normalAngle contrast]
|
||||
: else
|
||||
dxs.push 0; dys.push 0
|
||||
local fd1 : smooth js d1s
|
||||
local fd2 : smooth js d2s
|
||||
local fdx : smooth js dxs
|
||||
local fdy : smooth js dys
|
||||
# console.log deltaAngles
|
||||
|
||||
# interpolate important knots
|
||||
foreach j [range 0 this.controlKnots.length] : begin
|
||||
local knot this.controlKnots.(j)
|
||||
if [not knot.unimportant] : begin
|
||||
set lhs.(j) : object
|
||||
x : knot.x + ([fdx j] + [normalX knot.normalAngle contrast]) * [fd1 j]
|
||||
y : knot.y + ([fdy j] + [normalY knot.normalAngle contrast]) * [fd1 j]
|
||||
type knot.type
|
||||
set rhs.(j) : object
|
||||
x : knot.x - ([fdx j] + [normalX knot.normalAngle contrast]) * [fd2 j]
|
||||
y : knot.y - ([fdy j] + [normalY knot.normalAngle contrast]) * [fd2 j]
|
||||
type : match knot.type
|
||||
"left" "right"
|
||||
"right" "left"
|
||||
type type
|
||||
# interpolate unimportant knots referencing their original position relationship
|
||||
foreach j [range 0 this.controlKnots.length] : begin
|
||||
local knot this.controlKnots.(j)
|
||||
if knot.unimportant : begin
|
||||
local jBefore (j - 1)
|
||||
while this.controlKnots.(jBefore).unimportant : dec jBefore
|
||||
local jAfter (j + 1)
|
||||
while this.controlKnots.(jAfter).unimportant : inc jAfter
|
||||
|
||||
local knotBefore : utp this.gizmo this.controlKnots.(jBefore)
|
||||
local knotAfter : utp this.gizmo this.controlKnots.(jAfter)
|
||||
local ref : utp this.gizmo knot
|
||||
local lhsBefore : utp this.gizmo lhs.(jBefore)
|
||||
local lhsAfter : utp this.gizmo lhs.(jAfter)
|
||||
local rhsBefore : utp this.gizmo rhs.(jBefore)
|
||||
local rhsAfter : utp this.gizmo rhs.(jAfter)
|
||||
|
||||
local kLHS : tp this.gizmo : object
|
||||
x : linreg knotBefore.x lhsBefore.x knotAfter.x lhsAfter.x ref.x
|
||||
y : linreg knotBefore.y lhsBefore.y knotAfter.y lhsAfter.y ref.y
|
||||
local kRHS : tp this.gizmo : object
|
||||
x : linreg knotBefore.x rhsBefore.x knotAfter.x rhsAfter.x ref.x
|
||||
y : linreg knotBefore.y rhsBefore.y knotAfter.y rhsAfter.y ref.y
|
||||
|
||||
set lhs.(j) : object
|
||||
x kLHS.x
|
||||
y kLHS.y
|
||||
type knot.type
|
||||
set rhs.(j) : object
|
||||
x kRHS.x
|
||||
y kRHS.y
|
||||
type : match knot.type
|
||||
"left" "right"
|
||||
"right" "left"
|
||||
type type
|
||||
return {.lhs lhs .rhs rhs}
|
Loading…
Reference in New Issue
Block a user