Make basic operations to produce less closures (#2477)

This commit is contained in:
Belleve 2024-08-21 01:28:25 -10:00 committed by GitHub
parent 78b0df7e7d
commit 1c9c2e09a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 307 additions and 227 deletions

View file

@ -2,7 +2,7 @@ $$include '../meta/macros.ptl'
import [mix linreg clamp fallback boole boolePn] from "@iosevka/util" import [mix linreg clamp fallback boole boolePn] from "@iosevka/util"
import [Transform] from "@iosevka/geometry/transform" import [Transform] from "@iosevka/geometry/transform"
import [Interpolator WithKnotProxy] from "@iosevka/geometry/spiro-control" import [FunctionInterpolator WithKnotProxy AfCombine] from "@iosevka/geometry/spiro-control"
import [RadicalGeometry StrokeGeometry RemoveHolesGeometry] from "@iosevka/geometry" import [RadicalGeometry StrokeGeometry RemoveHolesGeometry] from "@iosevka/geometry"
import [CMixCoord CopyBackKnotProxy] from "@iosevka/font-kits/derived-coordinates" import [CMixCoord CopyBackKnotProxy] from "@iosevka/font-kits/derived-coordinates"
@ -133,7 +133,6 @@ glyph-block CommonShapes : begin
local mx ((l + r) / 2) local mx ((l + r) / 2)
currentGlyph.gizmo = [if transformShiftOnly [Transform.Id] giz] currentGlyph.gizmo = [if transformShiftOnly [Transform.Id] giz]
include : dispiro include : dispiro
begin [lambda : set this.gizmo currentGlyph.gizmo]
widths.rhs [fallback s Stroke] widths.rhs [fallback s Stroke]
g4 mx d [heading Leftward] g4 mx d [heading Leftward]
archv archv
@ -168,7 +167,6 @@ glyph-block CommonShapes : begin
local mx ((l + r) / 2) local mx ((l + r) / 2)
currentGlyph.gizmo = [if transformShiftOnly [Transform.Id] giz] currentGlyph.gizmo = [if transformShiftOnly [Transform.Id] giz]
include : spiro-outline include : spiro-outline
begin [lambda : set this.gizmo currentGlyph.gizmo]
g4 mx d g4 mx d
archv 32 2.0 archv 32 2.0
g4 l my g4 l my
@ -463,22 +461,16 @@ glyph-block CommonShapes : begin
local depth : v + skew0 * sw - sw local depth : v + skew0 * sw - sw
local shallowLimit : sw / 2 local shallowLimit : sw / 2
local skew : clamp 0 (1 / 2) : skew0 + [clamp 0 shallowLimit (shallowLimit - depth)] / rad local skew : clamp 0 (1 / 2) : skew0 + [clamp 0 shallowLimit (shallowLimit - depth)] / rad
local faf toFinish.af
if doSwash local headDirection : if doSwash
: then : begin object
set toFinish.af : lambda [] : begin x (Contrast / [Math.hypot 1 skew] * [if dtu (-1) 1])
if faf : faf.apply this arguments y (skew / [Math.hypot 1 skew] * [if ltr 1 (-1)])
if this.headsTo : this.headsTo { object
.x (Contrast / [Math.hypot 1 skew] * [if dtu (-1) 1]) x (Contrast * [if dtu (-1) 1])
.y (skew / [Math.hypot 1 skew] * [if ltr 1 (-1)]) y 0
}
: else : begin set toFinish.af : new AfCombine toFinish.af [heading headDirection]
set toFinish.af : lambda [] : begin
if faf : faf.apply this arguments
if this.headsTo : this.headsTo{
.x (Contrast * [if dtu (-1) 1])
.y 0
}
# Create the arc knots # Create the arc knots
local segBefore {} local segBefore {}
@ -487,14 +479,14 @@ glyph-block CommonShapes : begin
local fraction : j / nHookSegments local fraction : j / nHookSegments
local mixRatioAdjust : Math.max (1 / 2) : (1 / 2) + [if doSwash 1 (1 / 8)] * (mixRatio - (1 / 2)) local mixRatioAdjust : Math.max (1 / 2) : (1 / 2) + [if doSwash 1 (1 / 8)] * (mixRatio - (1 / 2))
local fractionAfter : fraction * (1 - mixRatioAdjust) / mixRatioAdjust local fractionAfter : fraction * (1 - mixRatioAdjust) / mixRatioAdjust
local myfinal : _SuperXY ((1 - mixRatioAdjust) / mixRatioAdjust) superness local myfinal : 1 - [archv.yFromX ((1 - mixRatioAdjust) / mixRatioAdjust) superness]
segBefore.push : g4 segBefore.push : g4
mix mx toStraight.x fraction mix mx toStraight.x fraction
mix y toStraight.y (1 - [_SuperXY fraction superness]) mix y toStraight.y [archv.yFromX fraction superness]
begin unimportant begin unimportant
segAfter.push : g4 segAfter.push : g4
mix mx toFinish.x fraction mix mx toFinish.x fraction
mix y toFinish.y ((1 - [_SuperXY fractionAfter superness]) / (1 - myfinal)) mix y toFinish.y ([archv.yFromX fractionAfter superness] / (1 - myfinal))
begin unimportant begin unimportant
if isStart if isStart
@ -525,7 +517,7 @@ glyph-block CommonShapes : begin
local-parameter : noSwash -- false local-parameter : noSwash -- false
local-parameter : o -- O local-parameter : o -- O
local args : object [yRef y] sw swTerminal isTail noSwash [overshoot o] local args : object [yRef y] sw swTerminal isTail noSwash [overshoot o]
return : WithKnotProxy [hookProxy args] : Interpolator hookStartBlender args return : WithKnotProxy [hookProxy args] : new FunctionInterpolator hookStartBlender args
glyph-block-export hookend glyph-block-export hookend
define flex-params [hookend] : begin define flex-params [hookend] : begin
@ -536,7 +528,7 @@ glyph-block CommonShapes : begin
local-parameter : noSwash -- false local-parameter : noSwash -- false
local-parameter : o -- O local-parameter : o -- O
local args : object [yRef y] sw swTerminal isTail noSwash [overshoot o] local args : object [yRef y] sw swTerminal isTail noSwash [overshoot o]
return : WithKnotProxy [hookProxy args] : Interpolator hookEndBlender args return : WithKnotProxy [hookProxy args] : new FunctionInterpolator hookEndBlender args
glyph-block-export arch glyph-block-export arch
define arch : namespace define arch : namespace
@ -638,7 +630,8 @@ glyph-block CommonShapes : begin
local-parameter : blendPre -- [if anglePre nothing [arcvh]] local-parameter : blendPre -- [if anglePre nothing [arcvh]]
local-parameter : blendPost -- [if anglePost nothing [archv]] local-parameter : blendPost -- [if anglePost nothing [archv]]
local args : object [lhs true] y p sw compact o swBefore swAfter mockPre mockPost blendPre blendPost anglePre anglePost local args : object [lhs true] y p sw compact o swBefore swAfter mockPre mockPost blendPre blendPost anglePre anglePost
return : WithKnotProxy [archBlenderProxy args] : Interpolator archBlender args return : WithKnotProxy [archBlenderProxy args]
new FunctionInterpolator archBlender args
export : define flex-params [rhs] : begin export : define flex-params [rhs] : begin
local-parameter : y local-parameter : y
@ -655,7 +648,8 @@ glyph-block CommonShapes : begin
local-parameter : blendPre -- [if anglePre nothing [arcvh]] local-parameter : blendPre -- [if anglePre nothing [arcvh]]
local-parameter : blendPost -- [if anglePost nothing [archv]] local-parameter : blendPost -- [if anglePost nothing [archv]]
local args : object [lhs false] y p sw compact o swBefore swAfter mockPre mockPost blendPre blendPost anglePre anglePost local args : object [lhs false] y p sw compact o swBefore swAfter mockPre mockPost blendPre blendPost anglePre anglePost
return : WithKnotProxy [archBlenderProxy args] : Interpolator archBlender args return : WithKnotProxy [archBlenderProxy args]
new FunctionInterpolator archBlender args
foreach side {lhs rhs} : begin foreach side {lhs rhs} : begin
set side.centerAt : object set side.centerAt : object

View file

@ -4,7 +4,7 @@ import [mix fallback] from "@iosevka/util"
import [SpiroPenGeometry] from "@iosevka/geometry" import [SpiroPenGeometry] from "@iosevka/geometry"
import [Vec2] from "@iosevka/geometry/point" import [Vec2] from "@iosevka/geometry/point"
import [Box] from "@iosevka/geometry/box" import [Box] from "@iosevka/geometry/box"
import [Interpolator] from "@iosevka/geometry/spiro-control" import [AfBase FunctionInterpolator] from "@iosevka/geometry/spiro-control"
import [PenKnotCollector] from "@iosevka/geometry/spiro-pen-expand" import [PenKnotCollector] from "@iosevka/geometry/spiro-pen-expand"
glyph-module glyph-module
@ -61,8 +61,13 @@ glyph-block LetterLike-Fraktur-Common : begin
# Directive to change the profile # Directive to change the profile
glyph-block-export change-pen glyph-block-export change-pen
define [change-pen newPen] : function : begin define [change-pen newPen] : new AfChangePen newPen
this.setProfile : newPen.getPenShape this.gizmo
class AfChangePen : inherits AfBase
public [new newPen] : begin
this.newPen = newPen
public [applyTo target] : begin
target.setProfile : this.newPen.getPenShape target.gizmo
# A pen profile describes a virtual flat-tip pen. We use a 45-degree arrangement to # A pen profile describes a virtual flat-tip pen. We use a 45-degree arrangement to
# simplify the math. # simplify the math.
@ -152,26 +157,26 @@ glyph-block LetterLike-Fraktur-Common : begin
export : define DepthX : 1 * DecoSizeX export : define DepthX : 1 * DecoSizeX
export : define LTDecoSize : 0.75 * DecoSizeX export : define LTDecoSize : 0.75 * DecoSizeX
export : define [h o] : Interpolator hBlender [object o] export : define [h o] : new FunctionInterpolator hBlender [object o]
define [hBlender before after args] : begin define [hBlender before after args] : begin
return : list return : list
g2 [mix before.x after.x 0.375] (after.y + [fallback args.o 0]) g2 [mix before.x after.x 0.375] (after.y + [fallback args.o 0])
g2 [mix before.x after.x 0.625] (before.y - [fallback args.o 0]) g2 [mix before.x after.x 0.625] (before.y - [fallback args.o 0])
export : define [vc waveDepth] : Interpolator vcBlender [object waveDepth] export : define [vc waveDepth] : new FunctionInterpolator vcBlender [object waveDepth]
define [vcBlender before after args] : begin define [vcBlender before after args] : begin
local [object waveDepth] args local [object waveDepth] args
return : list return : list
g2 (before.x + 0.5 * waveDepth) [mix before.y after.y 0.375] g2 (before.x + 0.5 * waveDepth) [mix before.y after.y 0.375]
g2 (after.x - 0.5 * waveDepth) [mix before.y after.y 0.625] g2 (after.x - 0.5 * waveDepth) [mix before.y after.y 0.625]
export : define [v] : Interpolator vBlender export : define [v] : new FunctionInterpolator vBlender
define [vBlender before after] : begin define [vBlender before after] : begin
return : list return : list
g2 after.x [mix before.y after.y 0.375] g2 after.x [mix before.y after.y 0.375]
g2 before.x [mix before.y after.y 0.625] g2 before.x [mix before.y after.y 0.625]
export : define [vDistAfter d] : Interpolator vDistAfterBlender [object d] export : define [vDistAfter d] : new FunctionInterpolator vDistAfterBlender [object d]
define [vDistAfterBlender before after args] : begin define [vDistAfterBlender before after args] : begin
return : list return : list
g2 after.x [mix before.y after.y 0.375] g2 after.x [mix before.y after.y 0.375]

View file

@ -166,7 +166,7 @@ glyph-block Letter-Latin-Lower-E : begin
dispiro dispiro
g4 lastKnot.x lastKnot.y [widths.rhs fine] g4 lastKnot.x lastKnot.y [widths.rhs fine]
g4 (lastKnot.x - beginCoSlope * TINY) (lastKnot.y - TINY) g4 (lastKnot.x - beginCoSlope * TINY) (lastKnot.y - TINY)
alsoThruThem.fromTWithOffset {(1/3) (2/3)} : object alsoThruThem.computed { (1/3) (2/3) } : object
rx : function [rt] rt rx : function [rt] rt
deltaX : function [rt] 0 deltaX : function [rt] 0
ry : function [rt] : 1/24 + rt + (1/2 - rt) * (3/8) ry : function [rt] : 1/24 + rt + (1/2 - rt) * (3/8)

View file

@ -1,7 +1,7 @@
$$include '../meta/macros.ptl' $$include '../meta/macros.ptl'
import [mix clamp fallback] from "@iosevka/util" import [mix clamp fallback] from "@iosevka/util"
import [Interpolator] from "@iosevka/geometry/spiro-control" import [FunctionInterpolator] from "@iosevka/geometry/spiro-control"
import [Dotless CvDecompose] from "@iosevka/glyph/relation" import [Dotless CvDecompose] from "@iosevka/glyph/relation"
import [RightDependentTrigger RightDependentLink DependentSelector] from "@iosevka/glyph/relation" import [RightDependentTrigger RightDependentLink DependentSelector] from "@iosevka/glyph/relation"
import [DesignParameters] from "../meta/aesthetics.mjs" import [DesignParameters] from "../meta/aesthetics.mjs"
@ -211,7 +211,7 @@ glyph-block Letter-Shared-Shapes : begin
local-parameter : swBefore -- Stroke local-parameter : swBefore -- Stroke
local-parameter : terminalSlopeAdj -- 0.5 local-parameter : terminalSlopeAdj -- 0.5
return : Interpolator normalBlender return : new FunctionInterpolator normalBlender
object [flat false] fine bottom xOuter x2 y2 yLoopTop swBefore terminalSlopeAdj object [flat false] fine bottom xOuter x2 y2 yLoopTop swBefore terminalSlopeAdj
export : define flex-params [f] : begin export : define flex-params [f] : begin
@ -223,7 +223,7 @@ glyph-block Letter-Shared-Shapes : begin
local-parameter : swBefore -- Stroke local-parameter : swBefore -- Stroke
local-parameter : terminalSlopeAdj -- 0.5 local-parameter : terminalSlopeAdj -- 0.5
return : Interpolator normalBlender return : new FunctionInterpolator normalBlender
object [flat true] fine bottom xOuter x2 yLoopTop swBefore terminalSlopeAdj object [flat true] fine bottom xOuter x2 yLoopTop swBefore terminalSlopeAdj
glyph-block-export HCurlyTail glyph-block-export HCurlyTail

View file

@ -177,10 +177,6 @@ export : define [calculateMetrics para] : begin
define GeometryStroke : AdviceStroke 4 define GeometryStroke : AdviceStroke 4
define ShoulderFine : Math.min (Stroke * para.shoulderFineMin) [AdviceStroke 16] define ShoulderFine : Math.min (Stroke * para.shoulderFineMin) [AdviceStroke 16]
define [_SuperXY x superness] : Math.pow
1 - [Math.pow x [fallback superness DesignParameters.superness]]
1 / [fallback superness DesignParameters.superness]
define [AdviceGlottalStopArchDepth y sign] : begin define [AdviceGlottalStopArchDepth y sign] : begin
return : ((y - Stroke) * 0.24 + Stroke * 0.625) + sign * TanSlope * SmoothAdjust return : ((y - Stroke) * 0.24 + Stroke * 0.625) + sign * TanSlope * SmoothAdjust
@ -199,7 +195,7 @@ export : define [calculateMetrics para] : begin
EssUpper EssLower EssQuestion HalfStroke RightSB Middle DotRadius PeriodRadius SideJut EssUpper EssLower EssQuestion HalfStroke RightSB Middle DotRadius PeriodRadius SideJut
ArchDepthA ArchDepthB SmallArchDepthA SmallArchDepthB CorrectionOMidX CorrectionOMidS ArchDepthA ArchDepthB SmallArchDepthA SmallArchDepthB CorrectionOMidX CorrectionOMidS
compositeBaseAnchors AdviceStroke AdviceStroke2 OverlayStroke OperatorStroke GeometryStroke compositeBaseAnchors AdviceStroke AdviceStroke2 OverlayStroke OperatorStroke GeometryStroke
ShoulderFine _SuperXY AdviceGlottalStopArchDepth StrokeWidthBlend ArchDepthAOf ArchDepthBOf ShoulderFine AdviceGlottalStopArchDepth StrokeWidthBlend ArchDepthAOf ArchDepthBOf
SmoothAdjust MidJutSide MidJutCenter YSmoothMidR YSmoothMidL HSwToV NarrowUnicodeT SmoothAdjust MidJutSide MidJutCenter YSmoothMidR YSmoothMidL HSwToV NarrowUnicodeT
WideUnicodeT VERY-FAR TINY] WideUnicodeT VERY-FAR TINY]

View file

@ -284,10 +284,10 @@ define-macro glyph-block : syntax-rules
WideWidth2 WideWidth3 WideWidth4 EssUpper EssLower EssQuestion HalfStroke RightSB WideWidth2 WideWidth3 WideWidth4 EssUpper EssLower EssQuestion HalfStroke RightSB
Middle DotRadius PeriodRadius SideJut ArchDepthA ArchDepthB SmallArchDepthA Middle DotRadius PeriodRadius SideJut ArchDepthA ArchDepthB SmallArchDepthA
SmallArchDepthB CorrectionOMidX CorrectionOMidS AdviceStroke AdviceStroke2 SmallArchDepthB CorrectionOMidX CorrectionOMidS AdviceStroke AdviceStroke2
OverlayStroke OperatorStroke GeometryStroke ShoulderFine _SuperXY OverlayStroke OperatorStroke GeometryStroke ShoulderFine AdviceGlottalStopArchDepth
AdviceGlottalStopArchDepth StrokeWidthBlend ArchDepthAOf ArchDepthBOf SmoothAdjust StrokeWidthBlend ArchDepthAOf ArchDepthBOf SmoothAdjust MidJutSide MidJutCenter
MidJutSide MidJutCenter compositeBaseAnchors YSmoothMidR YSmoothMidL HSwToV compositeBaseAnchors YSmoothMidR YSmoothMidL HSwToV NarrowUnicodeT WideUnicodeT
NarrowUnicodeT WideUnicodeT VERY-FAR TINY] VERY-FAR TINY]
define spiroFnImports `[g4 g2 corner flat curl virt close end straight g2c cg2 flatc ccurl define spiroFnImports `[g4 g2 corner flat curl virt close end straight g2c cg2 flatc ccurl
widths disable-contrast heading unimportant important alsoThru alsoThruThem bezControls widths disable-contrast heading unimportant important alsoThru alsoThruThem bezControls
quadControls archv arcvh dispiro spiro-outline spiro-collect] quadControls archv arcvh dispiro spiro-outline spiro-collect]

View file

@ -1,6 +1,7 @@
import { DiSpiroGeometry, SpiroGeometry } from "@iosevka/geometry"; import { DiSpiroGeometry, SpiroGeometry } from "@iosevka/geometry";
import { import {
Interpolator, AfBase,
InterpolatorBase,
SpiroFlattener, SpiroFlattener,
TerminateInstruction, TerminateInstruction,
UserCloseKnotPair, UserCloseKnotPair,
@ -167,12 +168,21 @@ export function SetupBuilders(bindings) {
} }
} }
class AfSetWidths extends AfBase {
constructor(l, r) {
super();
this.l = l;
this.r = r;
}
applyTo(target) {
target.setWidth(this.l, this.r);
}
}
function widths(l, r) { function widths(l, r) {
if (!isFinite(l)) throw new TypeError("NaN detected for left width"); if (!isFinite(l)) throw new TypeError("NaN detected for left width");
if (!isFinite(r)) throw new TypeError("NaN detected for right width"); if (!isFinite(r)) throw new TypeError("NaN detected for right width");
return function () { return new AfSetWidths(l, r);
if (this.setWidth) this.setWidth(l, r);
};
} }
widths.lhs = function (w) { widths.lhs = function (w) {
w = fallback(w, Stroke); w = fallback(w, Stroke);
@ -190,154 +200,172 @@ export function SetupBuilders(bindings) {
return widths(w / 2, w / 2); return widths(w / 2, w / 2);
}; };
class AfHeading extends AfBase {
constructor(d) {
super();
this.d = d;
}
applyTo(target) {
target.headsTo(this.d);
}
}
function heading(d) { function heading(d) {
if (!isFinite(d.x) || !isFinite(d.y)) if (!isFinite(d.x) || !isFinite(d.y))
throw new TypeError("NaN detected for heading directions"); throw new TypeError("NaN detected for heading directions");
return function () { return new AfHeading(d);
if (this.headsTo) this.headsTo(d);
};
} }
class AfWidthsHeading extends AfBase {
constructor(l, r, d) {
super();
this.l = l;
this.r = r;
this.d = d;
}
applyTo(target) {
target.setWidth(this.l, this.r);
target.headsTo(this.d);
}
}
widths.heading = function (l, r, d) { widths.heading = function (l, r, d) {
if (!isFinite(l)) throw new TypeError("NaN detected for left width"); if (!isFinite(l)) throw new TypeError("NaN detected for left width");
if (!isFinite(r)) throw new TypeError("NaN detected for left width"); if (!isFinite(r)) throw new TypeError("NaN detected for left width");
if (!isFinite(d.x) || !isFinite(d.y)) if (!isFinite(d.x) || !isFinite(d.y))
throw new TypeError("NaN detected for heading directions"); throw new TypeError("NaN detected for heading directions");
return function () { return new AfWidthsHeading(l, r, d);
if (this.setWidth) this.setWidth(l, r);
if (this.headsTo) this.headsTo(d);
};
}; };
widths.lhs.heading = function (w, d) { widths.lhs.heading = function (w, d) {
w = fallback(w, Stroke); w = fallback(w, Stroke);
if (!isFinite(w)) throw new TypeError("NaN detected for left width"); if (!isFinite(w)) throw new TypeError("NaN detected for left width");
if (!isFinite(d.x) || !isFinite(d.y)) if (!isFinite(d.x) || !isFinite(d.y))
throw new TypeError("NaN detected for heading directions"); throw new TypeError("NaN detected for heading directions");
return function () { return new AfWidthsHeading(w, 0, d);
if (this.setWidth) this.setWidth(w, 0);
if (this.headsTo) this.headsTo(d);
};
}; };
widths.rhs.heading = function (w, d) { widths.rhs.heading = function (w, d) {
w = fallback(w, Stroke); w = fallback(w, Stroke);
if (!isFinite(w)) throw new TypeError("NaN detected for left width"); if (!isFinite(w)) throw new TypeError("NaN detected for left width");
if (!isFinite(d.x) || !isFinite(d.y)) if (!isFinite(d.x) || !isFinite(d.y))
throw new TypeError("NaN detected for heading directions"); throw new TypeError("NaN detected for heading directions");
return function () { return new AfWidthsHeading(0, w, d);
if (this.setWidth) this.setWidth(0, w);
if (this.headsTo) this.headsTo(d);
};
}; };
widths.center.heading = function (w, d) { widths.center.heading = function (w, d) {
w = fallback(w, Stroke); w = fallback(w, Stroke);
if (!isFinite(w)) throw new TypeError("NaN detected for left width"); if (!isFinite(w)) throw new TypeError("NaN detected for left width");
if (!isFinite(d.x) || !isFinite(d.y)) if (!isFinite(d.x) || !isFinite(d.y))
throw new TypeError("NaN detected for heading directions"); throw new TypeError("NaN detected for heading directions");
return function () { return new AfWidthsHeading(w / 2, w / 2, d);
if (this.setWidth) this.setWidth(w / 2, w / 2);
if (this.headsTo) this.headsTo(d);
};
}; };
class AfDisableContrast extends AfBase {
applyTo(target) {
target.setContrast(1);
}
}
function disableContrast() { function disableContrast() {
return function () { return new AfDisableContrast();
if (this.setContrast) this.setContrast(1);
};
}
function unimportant() {
if (this.setUnimportant) this.setUnimportant(1);
}
function important() {
return void 0;
} }
function afInterpolate(before, after, args) { class AfUnimportant extends AfBase {
return g4( applyTo(target) {
mix(before.x, after.x, args.rx), target.setUnimportant();
mix(before.y, after.y, args.ry), }
fallback(args.raf, unimportant),
);
} }
function afInterpolateDelta(before, after, args) { const unimportant = new AfUnimportant();
return g4(
mix(before.x, after.x, args.rx) + args.deltaX, class AfImportant extends AfBase {
mix(before.y, after.y, args.ry) + args.deltaY, applyTo(target) {
fallback(args.raf, unimportant), target.setImportant();
); }
} }
function afInterpolateG2(before, after, args) { const important = new AfImportant();
return g2(
mix(before.x, after.x, args.rx), /// Simple (single mix) interpolator
mix(before.y, after.y, args.ry), class SimpleMixInterpolator extends InterpolatorBase {
fallback(args.raf, unimportant), constructor(ty, rx, ry, deltaX, deltaY, raf) {
); super();
} this.ty = ty;
function afInterpolateThem(before, after, args) { this.rx = rx;
let innerKnots = []; this.ry = ry;
for (const [rx, ry, rt] of args.rs) { this.deltaX = deltaX;
innerKnots.push( this.deltaY = deltaY;
fallback(args.ty, g2)( this.raf = fallback(raf, unimportant);
mix(before.x, after.x, rx), }
mix(before.y, after.y, ry), resolveInterpolation(before, after) {
args.raf && args.raf.blend && rt !== void 0 return this.ty(
? args.raf.blend(rt) mix(before.x, after.x, this.rx) + this.deltaX,
: args.raf mix(before.y, after.y, this.ry) + this.deltaY,
? args.raf this.raf,
: unimportant,
),
); );
} }
return innerKnots;
}
function afInterpolateThemWithDelta(before, after, args) {
let innerKnots = [];
for (const [rx, ry, deltaX, deltaY, rt] of args.rs) {
innerKnots.push(
fallback(args.ty, g2)(
mix(before.x, after.x, rx) + deltaX,
mix(before.y, after.y, ry) + deltaY,
args.raf && args.raf.blend && rt !== void 0
? args.raf.blend(rt)
: args.raf
? args.raf
: unimportant,
),
);
}
return innerKnots;
}
function afInterpolateThemFromTWithDelta(before, after, args) {
let innerKnots = [];
for (const rt of args.rs) {
innerKnots.push(
fallback(args.ty, g2)(
mix(before.x, after.x, args.raf.rx(rt)) + args.raf.deltaX(rt),
mix(before.y, after.y, args.raf.ry(rt)) + args.raf.deltaY(rt),
args.raf.modifier(rt),
),
);
}
return innerKnots;
} }
function alsoThru(rx, ry, raf) { function alsoThru(rx, ry, raf) {
return Interpolator(afInterpolate, { rx, ry, raf }); return new SimpleMixInterpolator(g4, rx, ry, 0, 0, raf);
} }
alsoThru.withOffset = function (rx, ry, deltaX, deltaY, raf) { alsoThru.withOffset = function (rx, ry, deltaX, deltaY, raf) {
return Interpolator(afInterpolateDelta, { rx, ry, deltaX, deltaY, raf }); return new SimpleMixInterpolator(g4, rx, ry, deltaX, deltaY, raf);
}; };
alsoThru.g2 = function (rx, ry, raf) { alsoThru.g2 = function (rx, ry, raf) {
return Interpolator(afInterpolateG2, { rx, ry, raf }); return new SimpleMixInterpolator(g2, rx, ry, 0, 0, raf);
}; };
function alsoThruThem(rs, raf, ty) {
return Interpolator(afInterpolateThem, { rs, raf, ty }); /// Multi-mix interpolator
class MultiMixInterpolator extends InterpolatorBase {
constructor(rs, raf, ty) {
super();
this.rs = rs;
this.raf = raf;
this.ty = fallback(ty, g2);
}
resolveInterpolation(before, after) {
let innerKnots = [];
for (const [rx, ry, rt] of this.rs) {
const x = mix(before.x, after.x, rx);
const y = mix(before.y, after.y, ry);
const af =
this.raf && this.raf.blend && rt !== void 0
? this.raf.blend(rt)
: this.raf
? this.raf
: unimportant;
innerKnots.push(this.ty(x, y, af));
}
return innerKnots;
}
} }
alsoThruThem.withOffset = function (rs, raf, ty) { function alsoThruThem(rs, raf, ty) {
return Interpolator(afInterpolateThemWithDelta, { rs, raf, ty }); return new MultiMixInterpolator(rs, raf, ty);
}; }
alsoThruThem.fromTWithOffset = function (rs, raf, ty) {
return Interpolator(afInterpolateThemFromTWithDelta, { rs, raf, ty }); /// Multi-mix interpolator that use function set to compute proportion/deltas
class MultiMixComputeInterpolator extends InterpolatorBase {
constructor(rs, raf, ty) {
super();
this.rs = rs;
this.raf = raf;
this.ty = fallback(ty, g2);
}
resolveInterpolation(before, after) {
let innerKnots = [];
for (const rt of this.rs) {
innerKnots.push(
this.ty(
mix(before.x, after.x, this.raf.rx(rt)) + this.raf.deltaX(rt),
mix(before.y, after.y, this.raf.ry(rt)) + this.raf.deltaY(rt),
this.raf.modifier(rt),
),
);
}
return innerKnots;
}
}
alsoThruThem.computed = function (rs, raf, ty) {
return new MultiMixComputeInterpolator(rs, raf, ty);
}; };
// Bezier control interpolator
function bezControlsImpl(x1, y1, x2, y2, samples, raf, ty) { function bezControlsImpl(x1, y1, x2, y2, samples, raf, ty) {
let rs = []; let rs = [];
for (let j = 1; j < samples; j = j + 1) for (let j = 1; j < samples; j = j + 1)
@ -362,44 +390,64 @@ export function SetupBuilders(bindings) {
); );
} }
let DEFAULT_STEPS = 6; // ArcHV and ArcVH interpolators
let [buildHV, buildVH] = (function (cache) { class ArcHvInterpolator extends InterpolatorBase {
function build(samples, _superness) { constructor(steps, superness) {
const superness = fallback(_superness, Superness); super();
let hv = []; this.steps = steps;
let vh = []; this.superness = superness;
for (let j = 1; j < samples; j = j + 1) { }
const theta = (((j + 1) / (samples + 2)) * Math.PI) / 2; resolveInterpolation(before, after) {
const c = Math.pow(Math.cos(theta), 2 / superness); let innerKnots = [];
const s = Math.pow(Math.sin(theta), 2 / superness); for (let j = 1; j < this.steps; j++) {
hv.push([s, 1 - c]); const theta = (((j + 1) / (this.steps + 2)) * Math.PI) / 2;
vh.push([1 - c, s]); const c = Math.pow(Math.cos(theta), 2 / this.superness);
const s = Math.pow(Math.sin(theta), 2 / this.superness);
const x = mix(before.x, after.x, s);
const y = mix(before.y, after.y, 1 - c);
innerKnots.push(g2(x, y, unimportant));
} }
return { hv, vh: vh }; return innerKnots;
} }
function buildHVImpl(samples, _superness) { }
if (_superness) return build(samples, _superness).hv; class ArcVhInterpolator extends InterpolatorBase {
if (!cache[samples]) cache[samples] = build(samples, _superness); constructor(steps, superness) {
return cache[samples].hv; super();
this.steps = steps;
this.superness = superness;
} }
function buildVHImpl(samples, _superness) { resolveInterpolation(before, after) {
if (_superness) return build(samples, _superness).vh; let innerKnots = [];
if (!cache[samples]) cache[samples] = build(samples, _superness); for (let j = 1; j < this.steps; j++) {
return cache[samples].vh; const theta = (((j + 1) / (this.steps + 2)) * Math.PI) / 2;
const c = Math.pow(Math.cos(theta), 2 / this.superness);
const s = Math.pow(Math.sin(theta), 2 / this.superness);
const x = mix(before.x, after.x, 1 - c);
const y = mix(before.y, after.y, s);
innerKnots.push(g2(x, y, unimportant));
}
return innerKnots;
} }
return [buildHVImpl, buildVHImpl]; }
})([]);
let DEFAULT_STEPS = 6;
function archv(samples, superness) { function archv(samples, superness) {
return alsoThruThem(buildHV(fallback(samples, DEFAULT_STEPS), superness)); return new ArcHvInterpolator(
fallback(samples, DEFAULT_STEPS),
fallback(superness, Superness),
);
} }
archv.superness = function (s) { archv.superness = function (s) {
return archv(DEFAULT_STEPS, s); return new ArcHvInterpolator(DEFAULT_STEPS, s);
}; };
function arcvh(samples, superness) { function arcvh(samples, superness) {
return alsoThruThem(buildVH(fallback(samples, DEFAULT_STEPS), superness)); return new ArcVhInterpolator(
fallback(samples, DEFAULT_STEPS),
fallback(superness, Superness),
);
} }
arcvh.superness = function (s) { arcvh.superness = function (s) {
return arcvh(DEFAULT_STEPS, s); return new ArcVhInterpolator(DEFAULT_STEPS, s);
}; };
archv.yFromX = function (px, _s) { archv.yFromX = function (px, _s) {
const s = fallback(_s, Superness); const s = fallback(_s, Superness);

View file

@ -58,15 +58,23 @@ class CvLookupManager
return lookup return lookup
public [linkDeps] : begin public [linkDeps] : begin
if (this.decompositionLookup && this.altrenatesLookup) : begin if this.decompositionLookup : begin
this.table.setDependency this.decompositionLookup this.altrenatesLookup if this.altrenatesLookup : begin
this.table.setDependency this.decompositionLookup this.altrenatesLookup
foreach lookupSS [items-of this.singleSubstLookups] : if lookupSS : begin
this.table.setDependency this.decompositionLookup lookupSS
foreach lookupCP [items-of this.cherryPickingLookups] : if lookupCP : begin
this.table.setDependency this.decompositionLookup lookupCP
if this.altrenatesLookup : begin if this.altrenatesLookup : begin
foreach lookupSS [items-of this.singleSubstLookups] : if lookupSS : begin foreach lookupSS [items-of this.singleSubstLookups] : if lookupSS : begin
this.table.setDependency this.altrenatesLookup lookupSS this.table.setDependency this.altrenatesLookup lookupSS
foreach lookupCP [items-of this.cherryPickingLookups] : if lookupCP : begin foreach lookupCP [items-of this.cherryPickingLookups] : if lookupCP : begin
this.table.setDependency lookupCP this.altrenatesLookup this.table.setDependency lookupCP this.altrenatesLookup
foreach lookupSS [items-of this.singleSubstLookups] : if lookupSS : begin
this.table.setDependency lookupCP lookupSS foreach lookupCP [items-of this.cherryPickingLookups] : if lookupCP : begin
foreach lookupSS [items-of this.singleSubstLookups] : if lookupSS : begin
this.table.setDependency lookupCP lookupSS
public [linkCrossDeps other] : begin public [linkCrossDeps other] : begin
if (this.altrenatesLookup && other.altrenatesLookup) : begin if (this.altrenatesLookup && other.altrenatesLookup) : begin
@ -101,41 +109,35 @@ export : define [buildCVSS gsub para glyphStore] : begin
local lookup : [cvs.get gr.tag].createDecompositionSubst local lookup : [cvs.get gr.tag].createDecompositionSubst
if [not lookup.substitutions.(gn)] : set lookup.substitutions.(gn) parts if [not lookup.substitutions.(gn)] : set lookup.substitutions.(gn) parts
do "cvxx" do "cvxx / ssxx"
local cvGrs {} local grSetUsedBySs : new Set
foreach {name prime} para.variants.primes : foreach {vn variant} prime.variants : begin foreach {name composition} para.variants.composites : if composition.tag : begin
if (variant.tag && variant.rank) : cvGrs.push variant define feature : gsub.addCommonFeature : gsub.createFeature composition.tag
cvGrs.sort AnyCv.compare
foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin define decomp : composition.decompose para para.variants.selectorTree
foreach gr [items-of cvGrs] : begin local ssGrs {}
local subst : gr.get glyph foreach { prime pv } [items-of decomp] : if (pv.tag && pv.rank) : begin
if (subst && subst != gn) : begin ssGrs.push pv
local cvAlt : [cvs.get gr.tag].createAlternateSubst local dl [cvs.get pv.tag].decompositionLookup
if [not cvAlt.substitutions.(gn)] : set cvAlt.substitutions.(gn) { } if dl : feature.addLookup dl
set cvAlt.substitutions.(gn).(gr.rank - 1) : glyphStore.ensureExists subst ssGrs.sort AnyCv.compare
do "ssxx" : foreach {name composition} para.variants.composites : if composition.tag : begin
define feature : gsub.addCommonFeature : gsub.createFeature composition.tag
define decomp : composition.decompose para para.variants.selectorTree
local ssGrs {}
foreach { prime pv } [items-of decomp] : if (pv.tag && pv.rank) : begin
ssGrs.push pv
local dl [cvs.get pv.tag].decompositionLookup
if dl : feature.addLookup dl
ssGrs.sort AnyCv.compare
foreach gr [items-of ssGrs] : begin
local cvSingle : [cvs.get gr.tag].createSingleSubstFor gr.rank
feature.addLookup cvSingle
foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin
foreach gr [items-of ssGrs] : begin foreach gr [items-of ssGrs] : begin
local cvSingle : [cvs.get gr.tag].createSingleSubstFor gr.rank
feature.addLookup cvSingle
grSetUsedBySs.add gr
foreach {gn glyph} [glyphStore.namedEntriesWithFilter nonDecomposable] : begin
foreach gr [items-of : AnyCv.query glyph] : begin
local subst : gr.get glyph local subst : gr.get glyph
if (subst && subst != gn) : begin if (subst && subst != gn) : begin
local cvSingle : [cvs.get gr.tag].createSingleSubstFor gr.rank if (gr.tag && gr.rank) : begin
set cvSingle.substitutions.(gn) : glyphStore.ensureExists subst local cvAlt : [cvs.get gr.tag].createAlternateSubst
if [not cvAlt.substitutions.(gn)] : set cvAlt.substitutions.(gn) { }
set cvAlt.substitutions.(gn).(gr.rank - 1) : glyphStore.ensureExists subst
if [grSetUsedBySs.has gr] : begin
local cvSingle : [cvs.get gr.tag].createSingleSubstFor gr.rank
set cvSingle.substitutions.(gn) : glyphStore.ensureExists subst
do "CV cherry picking" do "CV cherry picking"
foreach {name prime} para.variants.primes : if prime.cherryPicking : begin foreach {name prime} para.variants.primes : if prime.cherryPicking : begin
@ -147,7 +149,7 @@ export : define [buildCVSS gsub para glyphStore] : begin
if cv.decompositionLookup : feature.addLookup cv.decompositionLookup if cv.decompositionLookup : feature.addLookup cv.decompositionLookup
feature.addLookup lookup feature.addLookup lookup
foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin foreach {gn glyph} [glyphStore.namedEntriesWithFilter nonDecomposable] : begin
local subst : gr.get glyph local subst : gr.get glyph
if (subst && subst != gn) : begin if (subst && subst != gn) : begin
set lookup.substitutions.(gn) : glyphStore.ensureExists subst set lookup.substitutions.(gn) : glyphStore.ensureExists subst
@ -172,3 +174,4 @@ export : define [buildCVSS gsub para glyphStore] : begin
return cvs return cvs
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length define [objectIsNotEmpty obj] : obj && [Object.keys obj].length
define [nonDecomposable gn g] : not : CvDecompose.get g

View file

@ -1,7 +1,7 @@
// This class is used to "flatten" the spiro controls into a plain list of UserControlKnot // This class is used to "flatten" the spiro controls into a plain list of UserControlKnot
export class SpiroFlattener { export class SpiroFlattener {
constructor() { constructor() {
this.preControlFunctions = []; this.preControls = [];
this.controls = []; this.controls = [];
this.postControls = []; this.postControls = [];
} }
@ -9,13 +9,21 @@ export class SpiroFlattener {
add(c) { add(c) {
if (Array.isArray(c)) { if (Array.isArray(c)) {
for (const item of c) this.add(item); for (const item of c) this.add(item);
} else if (c instanceof Function) { } else if (c instanceof AfBase) {
if (!this.controls.length) this.preControlFunctions.push(c); if (this.controls.length) {
else throw new Error("Invalid spiro control sequence"); throw new Error(
"Invalid spiro control sequence: pre-control functions must be added first",
);
}
this.preControls.push(c);
} else if (c instanceof TerminateInstruction) { } else if (c instanceof TerminateInstruction) {
this.postControls.push(c); this.postControls.push(c);
} else { } else {
if (this.postControls.length) throw new Error("Invalid spiro control sequence"); if (this.postControls.length) {
throw new Error(
"Invalid spiro control sequence: post-control functions must be added last",
);
}
this.controls.push(c); this.controls.push(c);
} }
} }
@ -34,7 +42,7 @@ export class SpiroFlattener {
} }
pipe(collector) { pipe(collector) {
for (const fn of this.preControlFunctions) fn.call(collector); for (const fn of this.preControls) fn.applyTo(collector);
for (const control of this.controls) collector.pushKnot(control); for (const control of this.controls) collector.pushKnot(control);
for (const postControl of this.postControls) postControl.applyTo(collector); for (const postControl of this.postControls) postControl.applyTo(collector);
collector.finish(); collector.finish();
@ -214,6 +222,27 @@ class CoordinatePropagator {
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
/** The "amendmend function" */
export class AfBase {
applyTo() {
throw new Error("Unimplemented");
}
}
export class AfCombine extends AfBase {
constructor(af1, af2) {
super();
this.af1 = af1;
this.af2 = af2;
}
applyTo(target) {
if (this.af1) this.af1.applyTo(target);
if (this.af2) this.af2.applyTo(target);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
const RES_DEP_STAGE_COORDINATE_PROPOGATION_X = 0; const RES_DEP_STAGE_COORDINATE_PROPOGATION_X = 0;
const RES_DEP_STAGE_COORDINATE_PROPOGATION_Y = 1; const RES_DEP_STAGE_COORDINATE_PROPOGATION_Y = 1;
const RES_DEP_STAGE_INTERPOLATION = 2; const RES_DEP_STAGE_INTERPOLATION = 2;
@ -241,7 +270,7 @@ export class UserControlKnot {
this.af = af; this.af = af;
} }
applyTo(ctx) { applyTo(ctx) {
if (this.af) this.af.call(ctx); if (this.af) this.af.applyTo(ctx);
} }
getDependency(stage) { getDependency(stage) {
@ -261,7 +290,6 @@ export class UserControlKnot {
return this; return this;
} }
resolveCoordiantePropogation(ic, pre, post) { resolveCoordiantePropogation(ic, pre, post) {
// console.log(this, ic, pre, post);
switch (ic) { switch (ic) {
case 0: case 0:
this.x = this.x.resolveX(pre, this, post); this.x = this.x.resolveX(pre, this, post);
@ -388,7 +416,7 @@ export class DecorInterpolator extends InterpolatorBase {
} }
} }
class FunctionInterpolator extends InterpolatorBase { export class FunctionInterpolator extends InterpolatorBase {
constructor(blendFn, extraArgs) { constructor(blendFn, extraArgs) {
super(); super();
this.blendFn = blendFn; this.blendFn = blendFn;
@ -430,10 +458,6 @@ export class KnotProxyInterpolator extends InterpolatorBase {
return this.actual.resolveInterpolation(pre, post); return this.actual.resolveInterpolation(pre, post);
} }
} }
export function Interpolator(blender, restParameters) {
return new FunctionInterpolator(blender, restParameters);
}
export function WithKnotProxy(proxy, actual) { export function WithKnotProxy(proxy, actual) {
return new KnotProxyInterpolator(proxy, actual); return new KnotProxyInterpolator(proxy, actual);
} }
@ -448,7 +472,6 @@ export class TerminateInstruction {
applyTo(ctx) { applyTo(ctx) {
if (this.type === "close") ctx.closed = true; if (this.type === "close") ctx.closed = true;
if (this.af) throw new Error("Unreachable"); if (this.af) throw new Error("Unreachable");
// if (this.af) this.af.call(ctx);
} }
} }

View file

@ -55,6 +55,11 @@ export class BiKnotCollector {
this.lastKnot.proposedNormal = direction; this.lastKnot.proposedNormal = direction;
} }
} }
setImportant() {
if (this.lastKnot) {
this.lastKnot.unimportant = 0;
}
}
setUnimportant() { setUnimportant() {
if (this.lastKnot) { if (this.lastKnot) {
this.lastKnot.unimportant = 1; this.lastKnot.unimportant = 1;

View file

@ -33,6 +33,7 @@ export class PenKnotCollector {
setUnimportant() { setUnimportant() {
if (this.m_lastKnot) this.m_lastKnot.profile = null; if (this.m_lastKnot) this.m_lastKnot.profile = null;
} }
setImportant() {} // Not used
setContrast() {} setContrast() {}
setProfile(profile) { setProfile(profile) {

View file

@ -14,6 +14,11 @@ export class GlyphStore {
namedEntries() { namedEntries() {
return this.nameForward.entries(); return this.nameForward.entries();
} }
*namedEntriesWithFilter(fn) {
for (const [name, g] of this.nameForward.entries()) {
if (fn(name, g)) yield [name, g];
}
}
glyphNames() { glyphNames() {
return this.nameForward.keys(); return this.nameForward.keys();
} }