From 14acdd1f1ed60a0b4f9bee16b5f1b40970e0fe0d Mon Sep 17 00:00:00 2001 From: be5invis Date: Mon, 17 Aug 2015 06:52:28 +0800 Subject: [PATCH] Introduced libspiro-js into Iosevka. --- buildglyphs.patel | 78 +++++++++++++++- glyphs/common-shapes.patel | 85 ++++++++++++++---- glyphs/latin-basic-lower.patel | 58 ++++++------ pass1-cleanup.py | 1 + pass2-finalize.py | 3 +- support/stroke.patel | 160 +++++++++++++++++++++------------ support/transform.js | 6 +- 7 files changed, 284 insertions(+), 107 deletions(-) diff --git a/buildglyphs.patel b/buildglyphs.patel index 94fa57595..026c8cdc0 100644 --- a/buildglyphs.patel +++ b/buildglyphs.patel @@ -2,6 +2,45 @@ define Glyph [require './support/glyph'].Glyph define Stroke [require './support/stroke'].Stroke define tp [require './support/transform'].transformPoint define inverse [require './support/transform'].inverse +define unspiro : let [s2b [require 'libspiro-js'].spiroToBezierOnContext] : function [c closed pts] [s2b pts closed c] + +### Spiro functions +define [PREPARE f] (.type 'prepare' .af f) +define [CLOSE f] (.type 'close' .af f) +define [SETWIDTH l r] : function [] : this.set-width l r +define [HEADS d] : function [] : this.heads-to d +define [WIDTHHEADS l r d] : function [] : begin [this.set-width l r] [this.heads-to d] +define [SAMPLES n] : lambda [] : this.set-samples n +define [VIRTUAL] : this.points.[this.points.length - 1].subdivided = true +define [reverse a] : a.reverse + +define [flatpts pts] : begin { + local ans () + foreach p [items-of pts] : piecewise { + [p <@ Array] : set ans : ans.concat [flatpts p] + true : ans.push p + } + return ans +} +define [spiro pts] : begin { + local s : new Stroke + local points : ().slice.call arguments 0 + local closed false + local lastaf null + if [points.0 && points.0.type === 'prepare'] : begin { + points.0.af.call s + set points : points.slice 1 + } + if [points.[points.length - 1] && points.[points.length - 1].type === 'close'] : begin { + set lastaf points.[points.length - 1].af + set points : points.slice 0 [-1] + set closed true + } + unspiro s closed [flatpts points] + if lastaf : lastaf.call s + s.set-samples 2 + return s +} ### COMMON FUNCTIONS @@ -77,6 +116,43 @@ define [buildFont para recursive] : begin { define globalTransform : Italify para.italicAngle define ITALICCOR : 1 / [Math.sqrt [1 - globalTransform.yx * globalTransform.yx]] + define [G4 _x _y f] : begin { + local (.x x .y y) : tp globalTransform (.x _x .y _y) + return (.x x .y y .type 'g4' .af f) + } + define [G2 _x _y f] : begin { + local (.x x .y y) : tp globalTransform (.x _x .y _y) + return (.x x .y y .type 'g2' .af f) + } + define [LEFT _x _y f] : begin { + local (.x x .y y) : tp globalTransform (.x _x .y _y) + return (.x x .y y .type 'left' .af f) + } + define [RIGHT _x _y f] : begin { + local (.x x .y y) : tp globalTransform (.x _x .y _y) + return (.x x .y y .type 'right' .af f) + } + define [CAP_RTL x y _radius f] : begin { + local radius : fallback _radius [RIGHTSB - SB - STROKE] + local shift : radius * [1 - [Math.cos : Math.asin [0.25 / radius]]] + return : list { + G4 [x + 0.25] [y - shift] VIRTUAL + G4 x y f + G4 [x - 0.25] [y - shift] VIRTUAL + } + } + define [CAP_LTR x y r f] : [CAP_RTL x y r f].reverse + define [CUP_RTL x y _radius f] : begin { + local radius : fallback _radius [RIGHTSB - SB - STROKE] + local shift : radius * [1 - [Math.cos : Math.asin [0.25 / radius]]] + return : list { + G4 [x + 0.25] [y + shift] VIRTUAL + G4 x y f + G4 [x - 0.25] [y + shift] VIRTUAL + } + } + define [CUP_LTR x y r f] : [CUP_RTL x y r f].reverse + define UPWARD (.x [-ITALICCOR] .y 0) define DOWNWARD (.x ITALICCOR .y 0) define RIGHTWARD (.x globalTransform.yx .y 1) @@ -264,7 +340,7 @@ define [buildFont para recursive] : begin { define [create-glyph name actions] : piecewise { [name && actions] : begin { if [pickHash && [not pickHash.(name)]] : return nothing - process.stderr.write : "Building /" + name + [if recursive " (recursive)" ""] + " for " + para.family + ' ' + para.style + "\n" +# process.stderr.write : "Building /" + name + [if recursive " (recursive)" ""] + " for " + para.family + ' ' + para.style + "\n" set dependencyProfile`name () define glyphObject [new Glyph name] glyphObject.set-width WIDTH diff --git a/glyphs/common-shapes.patel b/glyphs/common-shapes.patel index 79ba08544..1288e1412 100644 --- a/glyphs/common-shapes.patel +++ b/glyphs/common-shapes.patel @@ -213,6 +213,36 @@ define [EHookLower bottom smooth hook _middle _stroke _o] : XSHookLower bottom [ define [CHookLower bottom smooth hook _middle _stroke _o] : XSHookLower bottom [RIGHTSB + TAILADJX * globalTransform.yx] _middle [SB + [fallback _o O]] smooth [hook - TAILADJY * globalTransform.yx] _stroke define [RevEHookLower bottom smooth hook _middle _stroke _o] : XSHookLower bottom SB _middle [RIGHTSB - [fallback _o O]] smooth hook _stroke +define [SpiroGeneralHookUpper conn free smooth y hook connectType fconn ffree tension refsmooth] : begin { + set tension : fallback tension 0 + set refsmooth : fallback refsmooth smooth + local dist : free - conn + local radius : dist * dist / [[2.6 + tension] * refsmooth] + return : flatpts : list { + G4 free [y - hook] ffree + [if [free > conn] CAP_RTL CAP_LTR] [[mix conn free SBALANCE] - OMIDCOR_S] y radius + [fallback connectType G4] conn [y - smooth] fconn + } +} +define [SpiroGeneralHookLower conn free smooth y hook connectType fconn ffree tension refsmooth] : begin { + set tension : fallback tension 0 + set refsmooth : fallback refsmooth smooth + local dist : free - conn + local radius : dist * dist / [[2.6 + tension] * refsmooth] + return : flatpts : list { + G4 free [y + hook] ffree + [if [free > conn] CUP_RTL CUP_LTR] [[mix conn free SBALANCE] - OMIDCOR_S] y radius + [fallback connectType G4] conn [y + smooth] fconn + } +} + +define [SpiroCHookUpper l r y connectType s] : begin { + return : SpiroGeneralHookUpper [l + O] r SMALLSMOOTHA [y - O] HOOK connectType nothing nothing 0 SMALLSMOOTH +} +define [SpiroCHookLower l r y connectType s] : list { + return : reverse : SpiroGeneralHookLower [l + O] r SMALLSMOOTHB [y + O] HOOK connectType nothing nothing 0 SMALLSMOOTH +} + define [smallo u d l r _width _sma _smb] : glyph-construction { local middle : [l + r] / 2 local width : fallback _width STROKE @@ -221,31 +251,50 @@ define [smallo u d l r _width _sma _smb] : glyph-construction { local mc : OMIDCOR * width if [u - d > sma + smb] { then : begin { - include : create-stroke - :.set-transform globalTransform - :.start-from [middle - mc] [u - O] - :.set-width width 0 - :.arc-hv-to [l + O] [u - sma] - :.line-to [l + O] [d + smb] - :.arc-vh-to [middle + mc] [d + O] - :.arc-hv-to [r - O] [d + sma] - :.line-to [r - O] [u - smb] - :.arc-vh-to [middle - mc] [u - O] + include : spiro { + G4 [middle - mc] [u - O] [SETWIDTH width 0] + LEFT [l + O] [u - sma] + RIGHT [l + O] [d + smb] + G4 [middle + mc] [d + O] + LEFT [r - O] [d + sma] + RIGHT [r - O] [u - smb] + CLOSE [SAMPLES 2] + } } else : begin { local ymiddlea : mix d u [smb / [sma + smb]] local ymiddleb : mix d u [sma / [sma + smb]] - include : create-stroke - :.set-transform globalTransform - :.start-from [middle - mc] [u - O] - :.set-width width 0 - :.arc-hv-to [l + O] ymiddlea - :.arc-vh-to [middle + mc] [d + O] - :.arc-hv-to [r - O] ymiddleb - :.arc-vh-to [middle - mc] [u - O] + include : spiro { + G4 [middle - mc] [u - O] [SETWIDTH width 0] + G4 [l + O] ymiddlea + G4 [middle + mc] [d + O] + G4 [r - O] ymiddleb + CLOSE [SAMPLES 2] + } } } } +define [SmalloAlt u d l r _width _sma _smb] : glyph-construction { + local middle : [l + r] / 2 + local width : fallback _width STROKE + local sma : fallback _sma SMALLSMOOTHA + local smb : fallback _smb SMALLSMOOTHB + local mc : OMIDCOR * width + local rho 0.77 + local ymiddlea : mix d u [smb / [sma + smb]] + local ymiddleb : mix d u [sma / [sma + smb]] + include : spiro { + G4 [middle - mc] [u - O] [SETWIDTH width 0] + G4 [mix middle l rho] [mix ymiddlea u rho] + G4 [l + O] ymiddlea + G4 [mix middle l rho] [mix ymiddlea d rho] + G4 [middle + mc] [d + O] + G4 [mix middle r rho] [mix ymiddleb d rho] + G4 [r - O] ymiddleb + G4 [mix middle r rho] [mix ymiddleb u rho] + CLOSE [SAMPLES 2] + } +} define [HBar xleft xright y _fine] : glyph-construction { local fine : [fallback _fine STROKE] / 2 diff --git a/glyphs/latin-basic-lower.patel b/glyphs/latin-basic-lower.patel index e0c25ba6c..82d189804 100644 --- a/glyphs/latin-basic-lower.patel +++ b/glyphs/latin-basic-lower.patel @@ -77,7 +77,7 @@ create-glyph 'g' : glyph-construction { assign-unicode 'g' include pMarks - include : smallo XH [XH * GBARPOS - O] SB [RIGHTSB - 0.3 * SB] + include : SmalloAlt XH [XH * GBARPOS] SB [RIGHTSB - 0.3 * SB] local gleftx [SB * 0.8 + O] local grightx [RIGHTSB + SB * 0.1 - O] @@ -106,27 +106,29 @@ create-glyph 'c' : glyph-construction { set-width WIDTH assign-unicode 'c' include eMarks - - include : CHookLower 0 SMALLSMOOTHB HOOK - include : CHookUpper XH SMALLSMOOTHA HOOK - include : create-stroke - :.start-from [SB + O] [XH - SMALLSMOOTHA] :.set-width STROKE 0 - :.line-to [SB + O] SMALLSMOOTHB + include : spiro { + PREPARE [SETWIDTH STROKE 0] + SpiroCHookUpper SB RIGHTSB XH LEFT + SpiroCHookLower SB RIGHTSB 0 RIGHT + } +# include : CHookLower 0 SMALLSMOOTHB HOOK +# include : CHookUpper XH SMALLSMOOTHA HOOK +# include : create-stroke +# :.start-from [SB + O] [XH - SMALLSMOOTHA] :.set-width STROKE 0 +# :.line-to [SB + O] SMALLSMOOTHB } define [SmallEShape top stroke barpos] : glyph-construction { local barbottom [top * [fallback barpos EBARPOS]] local hookx [RIGHTSB - OXHOOK + TAILADJX * globalTransform.yx] local hookmiddle : [mix [SB + O] hookx 0.55] + OMIDCOR_S - include : create-stroke - :.start-from [RIGHTSB - O] barbottom - :.heads-to UPWARD - :.set-width stroke 0 - :.line-to [RIGHTSB - O] [top - SMALLSMOOTHB] - :.arc-vh-to MIDDLE [top - O] - :.arc-hv-to [SB + O] [top - SMALLSMOOTHA] - :.line-to [SB + O] SMALLSMOOTHB - include : EHookLower 0 SMALLSMOOTHB SHOOK [mix SB RIGHTSB SBALANCE] stroke + include : spiro { + LEFT [RIGHTSB - O] barbottom [WIDTHHEADS stroke 0 UPWARD] + RIGHT [RIGHTSB - O] [top - SMALLSMOOTHB] + G4 [MIDDLE - OMIDCOR_S] [top - O] + LEFT [SB + O] [top - SMALLSMOOTHA] + SpiroCHookLower SB RIGHTSB 0 RIGHT + } include : create-stroke :.start-from [SB + [stroke / 2]] barbottom :.set-width stroke 0 @@ -171,14 +173,14 @@ define [SmallTShape top bot] : glyph-construction { local hookx [center + [WIDTH - SB * 2] * 0.78 - OXHOOK + TAILADJX * globalTransform.yx] local turn : mix center hookx [0.5 + globalTransform.yx * 0.5] local smb : [turn - center] * 1.1 + local rho 0.77 - include : create-stroke - :.start-from center top - :.set-width STROKE 0 - :.heads-to DOWNWARD - :.line-to center [bot + smb] - :.arc-vh-to turn [bot + O] - :.curve-to [turn + [KAPPA_HOOK + TAILADJKAPPA * globalTransform.yx + 0.1] * [hookx - turn]] [bot + O] hookx [bot + HOOK - TAILADJY * globalTransform.yx] + include : spiro { + LEFT center top [WIDTHHEADS STROKE 0 DOWNWARD] + RIGHT center [bot + smb] + CUP_LTR turn [bot + O] [[hookx - center] * 0.6] + G2 hookx [bot + HOOK - TAILADJY * globalTransform.yx] + } include : HBarTop [center + HALFSTROKE - LONGJUT + TBALANCE2] [center + HALFSTROKE + LONGJUT + TBALANCE2] [top * XH / CAP] } @@ -283,22 +285,24 @@ create-glyph 'm' : glyph-construction { local m2 : m1 + [MIDDLE - sw / 2 - SB] include : create-stroke :.start-from [MIDDLE - sw / 2] 0 - :.set-width 0 sw :.heads-to UPWARD + :.set-width 0 sw + :.heads-to UPWARD :.line-to [MIDDLE - sw / 2] [XH - SMALLSMOOTHA] :.arc-vh-to m1 [XO - STROKE] :.set-width 0 STROKE :.heads-to LEFTWARD - :.arc-hv-to [SB + sw * 0.75] [XH - SMALLSMOOTHA] + :.arc-hv-to [SB + sw * 0.75 * ITALICCOR] [XH - SMALLSMOOTHA] :.heads-to DOWNWARD :.set-width 0 [sw * 0.4] include : create-stroke :.start-from [RIGHTSB - sw - O] 0 - :.set-width 0 sw :.heads-to UPWARD + :.set-width 0 sw + :.heads-to UPWARD :.line-to [RIGHTSB - sw - O] [XH - SMALLSMOOTHA] :.arc-vh-to m2 [XO - STROKE] :.set-width 0 STROKE :.heads-to LEFTWARD - :.arc-hv-to [MIDDLE + sw * 0.25] [XH - SMALLSMOOTHA] + :.arc-hv-to [MIDDLE + sw * 0.25 * ITALICCOR] [XH - SMALLSMOOTHA] :.heads-to DOWNWARD :.set-width 0 [sw * 0.4] include : create-stroke diff --git a/pass1-cleanup.py b/pass1-cleanup.py index be98fc293..397ec9c74 100644 --- a/pass1-cleanup.py +++ b/pass1-cleanup.py @@ -21,6 +21,7 @@ font.removeOverlap() font.addExtrema() font.simplify(2) font.layers["Fore"].is_quadratic = False +font.addExtrema() font.simplify(6, ("smoothcurves", "removesingletonpoints", "setstarttoextremum"), 0.2) font.canonicalContours() font.canonicalStart() diff --git a/pass2-finalize.py b/pass2-finalize.py index b6e51bb70..3eea89e2e 100644 --- a/pass2-finalize.py +++ b/pass2-finalize.py @@ -7,7 +7,8 @@ font = fontforge.open(source) font.selection.all() font.removeOverlap() font.em = 1000 -font.simplify(1) +font.addExtrema() +font.simplify(0.1) font.canonicalContours() font.canonicalStart() font.generate(sys.argv[2], flags = ("short-post", "opentype")) \ No newline at end of file diff --git a/support/stroke.patel b/support/stroke.patel index 995a4ac2c..b0d24f9e7 100644 --- a/support/stroke.patel +++ b/support/stroke.patel @@ -6,11 +6,7 @@ define utp [require './transform'].untransform define [fallback] : for [local j 0] [j < arguments.length] [inc j] : if [arguments`j !== nothing] : return arguments`j -define [xs-array low high] : begin { - local a () - for [local j [low - 1]] [j <= high] [inc j] : a.push j - return a -} +define [xs-array a] ([a.0 - 1] :: [a.concat ([a`[a.length - 1] + 1])]) define [ys-array a] (a.0 :: [a.concat (a`[a.length - 1])]) define SAMPLES 3 @@ -33,6 +29,8 @@ define [Stroke] : begin { .x 0 .y 0 ) + this.defaultD1 = 0 + this.defaultD2 = 0 return this } define Stroke.is : object { @@ -51,8 +49,16 @@ define [Stroke.bindParameters para] : begin { } define [Stroke.prototype.set-width d1 d2] : begin { local point this.points`[this.points.length - 1] - point.d1 = d1 - point.d2 = d2 + if point { + then { + point.d1 = d1 + point.d2 = d2 + } + else { + this.defaultD1 = d1 + this.defaultD2 = d2 + } + } return this } define [Stroke.prototype.heads-to x y] : begin { @@ -69,18 +75,28 @@ define [Stroke.prototype.start-from x y] : begin { this.points = ([tp this.gizmo (.x x .y y .onCurve true)]) return this } -define [Stroke.prototype.line-to x y] : begin { - this.points.push [tp this.gizmo (.x x .y y .onCurve true)] +define Stroke.prototype.moveTo Stroke.prototype.start-from +define [Stroke.prototype.line-to x y subdivided] : begin { + this.points.push [tp this.gizmo (.x x .y y .onCurve true .subdivided subdivided)] return this } -define [Stroke.prototype.curve-to xc yc x y] : begin { - this.points.push [tp this.gizmo (.x xc .y yc .onCurve false)] [tp this.gizmo (.x x .y y .onCurve true)] +define Stroke.prototype.lineTo Stroke.prototype.line-to +define [Stroke.prototype.curve-to xc yc x y subdivided] : begin { + this.points.push { + tp this.gizmo (.x xc .y yc .onCurve false) + tp this.gizmo (.x x .y y .onCurve true .subdivided subdivided) + } return this } -define [Stroke.prototype.cubic-to x1 y1 x2 y2 x y] : begin { - this.points.push [tp this.gizmo (.x x1 .y y1 .onCurve false .cubic true)] [tp this.gizmo (.x x2 .y y2 .onCurve false .cubic true)] [tp this.gizmo (.x x .y y .onCurve true)] +define [Stroke.prototype.cubic-to x1 y1 x2 y2 x y subdivided] : begin { + this.points.push { + tp this.gizmo (.x x1 .y y1 .onCurve false .cubic true) + tp this.gizmo (.x x2 .y y2 .onCurve false .cubic true) + tp this.gizmo (.x x .y y .onCurve true .subdivided subdivided) + } return this } +define Stroke.prototype.cubicTo Stroke.prototype.cubic-to define [Stroke.prototype.arc-vh-to x y _kappah _kappav] : begin { local kappah : fallback _kappah BKAPPA local kappav : fallback _kappav _kappah CKAPPA @@ -120,55 +136,74 @@ define [near a b c] : begin { local dist : Math.max [Math.abs [a.y - c.y]] [Math.abs [a.x - c.x]] return : [Math.abs [b.y - my]] < dist && [Math.abs [b.x - mx]] < dist } -define [computeOffsetPoint curve t j foffset fpdx fpdy] : begin { - local onpoint : curve.compute [t - j] - local normal : curve.normal [t - j] +define [computeOffsetPoint curve t j sl foffset fpdx fpdy] : begin { + local onpoint : curve.compute [[t - j] / sl] + local normal : curve.normal [[t - j] / sl] return (.x onpoint.x + [foffset t] * [normal.x + [fpdx t]] .y onpoint.y + [foffset t] * [normal.y + [fpdy t]]) } define [Stroke.prototype.to-outline d1 d2 _samples straight] : begin { - local d1s ([set d1 [if [this.points.0.d1 >= 0] this.points.0.d1 d1]]) - local d2s ([set d2 [if [this.points.0.d2 >= 0] this.points.0.d2 d2]]) + + local ts (0) + local d1s ([set d1 [if [this.points.0.d1 >= 0] this.points.0.d1 this.defaultD1]]) + local d2s ([set d2 [if [this.points.0.d2 >= 0] [-this.points.0.d2] this.defaultD2]]) local pdxs (0) local pdys (0) + local notSubdivideds () local samples : fallback _samples this.samples SAMPLES local shapes () local subSegments () local p0 this.points.0 + local arclengthSofar 0 for [local j 1] [j < this.points.length] [inc j] : begin { local p1 this.points`j piecewise { p1.onCurve : begin { subSegments.push : local seg : new Bezier p0.x p0.y [[p0.x + p1.x] / 2] [[p0.y + p1.y] / 2] p1.x p1.y - d1s.push : set d1 [if [p1.d1 >= 0] p1.d1 d1] - d2s.push : set d2 [if [p1.d2 >= 0] p1.d2 d2] - local normalpt : seg.normal 1 - pdxs.push : if [p1.pdx !== nothing] [p1.pdx - normalpt.x] 0 - pdys.push : if [p1.pdy !== nothing] [p1.pdy - normalpt.y] 0 + arclengthSofar = arclengthSofar + [seg.length] + if [not p1.subdivided] : begin { + ts.push arclengthSofar + d1s.push : set d1 [if [p1.d1 >= 0] p1.d1 d1] + d2s.push : set d2 [if [p1.d2 >= 0] [-p1.d2] d2] + local normalpt : seg.normal 1 + pdxs.push : if [p1.pdx !== nothing] [p1.pdx - normalpt.x] 0 + pdys.push : if [p1.pdy !== nothing] [p1.pdy - normalpt.y] 0 + set notSubdivideds.[subSegments.length - 1] true + } p0 = p1 } p1.cubic : begin { local p2 this.points`[j + 1] local p3 this.points`[j + 2] subSegments.push : local seg : new Bezier p0.x p0.y p1.x p1.y p2.x p2.y p3.x p3.y - d1s.push : set d1 [if [p3.d1 >= 0] p3.d1 d1] - d2s.push : set d2 [if [p3.d2 >= 0] p3.d2 d2] - local normalpt : seg.normal 1 - pdxs.push : if [p3.pdx !== nothing] [p3.pdx - normalpt.x] 0 - pdys.push : if [p3.pdy !== nothing] [p3.pdy - normalpt.y] 0 + arclengthSofar = arclengthSofar + [seg.length] + if [not p3.subdivided] : begin { + ts.push arclengthSofar + d1s.push : set d1 [if [p3.d1 >= 0] p3.d1 d1] + d2s.push : set d2 [if [p3.d2 >= 0] [-p3.d2] d2] + local normalpt : seg.normal 1 + pdxs.push : if [p3.pdx !== nothing] [p3.pdx - normalpt.x] 0 + pdys.push : if [p3.pdy !== nothing] [p3.pdy - normalpt.y] 0 + set notSubdivideds.[subSegments.length - 1] true + } p0 = p3 j = j + 2 } true : begin { local p2 this.points`[j + 1] subSegments.push : local seg : new Bezier p0.x p0.y p1.x p1.y p2.x p2.y - d1s.push : set d1 [if [p2.d1 >= 0] p2.d1 d1] - d2s.push : set d2 [if [p2.d2 >= 0] p2.d2 d2] - local normalpt : seg.normal 1 - pdxs.push : if [p2.pdx !== nothing] [p2.pdx - normalpt.x] 0 - pdys.push : if [p2.pdy !== nothing] [p2.pdy - normalpt.y] 0 + arclengthSofar = arclengthSofar + [seg.length] + if [not p2.subdivided] : begin { + ts.push arclengthSofar + d1s.push : set d1 [if [p2.d1 >= 0] p2.d1 d1] + d2s.push : set d2 [if [p2.d2 >= 0] [-p2.d2] d2] + local normalpt : seg.normal 1 + pdxs.push : if [p2.pdx !== nothing] [p2.pdx - normalpt.x] 0 + pdys.push : if [p2.pdy !== nothing] [p2.pdy - normalpt.y] 0 + set notSubdivideds.[subSegments.length - 1] true + } p0 = p2 j = j + 1 } @@ -177,43 +212,44 @@ define [Stroke.prototype.to-outline d1 d2 _samples straight] : begin { if [this.points.0.pdx !== nothing] : set pdxs.0 [this.points.0.pdx - [subSegments.0.normal 0].x] if [this.points.0.pdy !== nothing] : set pdys.0 [this.points.0.pdy - [subSegments.0.normal 0].y] - local f1 : smooth [xs-array 0 d1s.length] [ys-array d1s] - local f2 : smooth [xs-array 0 d2s.length] [ys-array [d2s.map [[x] -> [-x]]]] - local fpdx : smooth [xs-array 0 d1s.length] [ys-array pdxs] - local fpdy : smooth [xs-array 0 d2s.length] [ys-array pdys] + local f1 : smooth [xs-array ts] [ys-array d1s] + local f2 : smooth [xs-array ts] [ys-array d2s] + local fpdx : smooth [xs-array ts] [ys-array pdxs] + local fpdy : smooth [xs-array ts] [ys-array pdys] local left () local right () + set arclengthSofar 0 for [local j 0] [j < subSegments.length] [inc j] : begin { local curve subSegments`j - local arcLength : curve.length + local segLength : curve.length local segLengths (0) foreach sample [range 1 till samples] : begin { segLengths.push : curve.split 0 [sample / samples] :.length } - left.push [begin [local last [computeOffsetPoint curve j j f1 fpdx fpdy]] (.x last.x .y last.y .onCurve true)] - right.push [begin [local last [computeOffsetPoint curve j j f2 fpdx fpdy]] (.x last.x .y last.y .onCurve true)] + left.push [begin [local last [computeOffsetPoint curve arclengthSofar arclengthSofar segLength f1 fpdx fpdy]] (.x last.x .y last.y .onCurve true)] + right.push [begin [local last [computeOffsetPoint curve arclengthSofar arclengthSofar segLength f2 fpdx fpdy]] (.x last.x .y last.y .onCurve true)] foreach sample [range 0 samples] : begin { - local t : j + segLengths.(sample) / arcLength - local tn : j + segLengths.[sample + 1] / arcLength + local t : arclengthSofar + segLengths.(sample) + local tn : arclengthSofar + segLengths.[sample + 1] - local lthis : computeOffsetPoint curve t j f1 fpdx fpdy - local rthis : computeOffsetPoint curve t j f2 fpdx fpdy - local lnext : computeOffsetPoint curve tn j f1 fpdx fpdy - local rnext : computeOffsetPoint curve tn j f2 fpdx fpdy - local lnthis1 : computeOffsetPoint curve [t + TINY] j f1 fpdx fpdy - local rnthis1 : computeOffsetPoint curve [t + TINY] j f2 fpdx fpdy - local lnnext1 : computeOffsetPoint curve [tn - TINY] j f1 fpdx fpdy - local rnnext1 : computeOffsetPoint curve [tn - TINY] j f2 fpdx fpdy - local lnthis2 : computeOffsetPoint curve [t + 2 * TINY] j f1 fpdx fpdy - local rnthis2 : computeOffsetPoint curve [t + 2 * TINY] j f2 fpdx fpdy - local lnnext2 : computeOffsetPoint curve [tn - 2 * TINY] j f1 fpdx fpdy - local rnnext2 : computeOffsetPoint curve [tn - 2 * TINY] j f2 fpdx fpdy - local lnthis3 : computeOffsetPoint curve [t + 3 * TINY] j f1 fpdx fpdy - local rnthis3 : computeOffsetPoint curve [t + 3 * TINY] j f2 fpdx fpdy - local lnnext3 : computeOffsetPoint curve [tn - 3 * TINY] j f1 fpdx fpdy - local rnnext3 : computeOffsetPoint curve [tn - 3 * TINY] j f2 fpdx fpdy + local lthis : computeOffsetPoint curve t arclengthSofar segLength f1 fpdx fpdy + local rthis : computeOffsetPoint curve t arclengthSofar segLength f2 fpdx fpdy + local lnext : computeOffsetPoint curve tn arclengthSofar segLength f1 fpdx fpdy + local rnext : computeOffsetPoint curve tn arclengthSofar segLength f2 fpdx fpdy + local lnthis1 : computeOffsetPoint curve [t + TINY] arclengthSofar segLength f1 fpdx fpdy + local rnthis1 : computeOffsetPoint curve [t + TINY] arclengthSofar segLength f2 fpdx fpdy + local lnnext1 : computeOffsetPoint curve [tn - TINY] arclengthSofar segLength f1 fpdx fpdy + local rnnext1 : computeOffsetPoint curve [tn - TINY] arclengthSofar segLength f2 fpdx fpdy + local lnthis2 : computeOffsetPoint curve [t + 2 * TINY] arclengthSofar segLength f1 fpdx fpdy + local rnthis2 : computeOffsetPoint curve [t + 2 * TINY] arclengthSofar segLength f2 fpdx fpdy + local lnnext2 : computeOffsetPoint curve [tn - 2 * TINY] arclengthSofar segLength f1 fpdx fpdy + local rnnext2 : computeOffsetPoint curve [tn - 2 * TINY] arclengthSofar segLength f2 fpdx fpdy + local lnthis3 : computeOffsetPoint curve [t + 3 * TINY] arclengthSofar segLength f1 fpdx fpdy + local rnthis3 : computeOffsetPoint curve [t + 3 * TINY] arclengthSofar segLength f2 fpdx fpdy + local lnnext3 : computeOffsetPoint curve [tn - 3 * TINY] arclengthSofar segLength f1 fpdx fpdy + local rnnext3 : computeOffsetPoint curve [tn - 3 * TINY] arclengthSofar segLength f2 fpdx fpdy local dlthis [dforward lthis lnthis1 lnthis2 lnthis3] local drthis [dforward rthis rnthis1 rnthis2 rnthis3] @@ -234,6 +270,14 @@ define [Stroke.prototype.to-outline d1 d2 _samples straight] : begin { right.push (.x rnext.x .y rnext.y .onCurve true) }] } + arclengthSofar = arclengthSofar + segLength + if notSubdivideds.(j) : begin { + shapes.push : left.concat [right.reverse] + set left () + set right () + } + } + if [left.length + right.length > 2] : begin { shapes.push : left.concat [right.reverse] set left () set right () diff --git a/support/transform.js b/support/transform.js index 0b35a4776..88ec28994 100644 --- a/support/transform.js +++ b/support/transform.js @@ -3,7 +3,8 @@ exports.transformPoint = function(tfm, pt){ x : pt.x * tfm.xx + pt.y * tfm.yx + tfm.x, y : pt.x * tfm.xy + pt.y * tfm.yy + tfm.y, onCurve: pt.onCurve, - cubic: pt.cubic + cubic: pt.cubic, + subdivided: pt.subdivided } } exports.inverse = function(tfm){ @@ -25,6 +26,7 @@ exports.untransform = function(tfm, pt){ x : (xx * tfm.yy - yy * tfm.yx) / denom, y : (yy * tfm.xx - xx * tfm.xy) / denom, onCurve: pt.onCurve, - cubic: pt.cubic + cubic: pt.cubic, + subdivided: pt.subdivided } } \ No newline at end of file