Add Fraktur e/o/ø (#2445)
* Add - LATIN SMALL LETTER BLACKLETTER E (`U+AB32`). - LATIN SMALL LETTER BLACKLETTER O (`U+AB3D`). - LATIN SMALL LETTER BLACKLETTER O WITH STROKE (`U+AB3E`). - MATHEMATICAL FRAKTUR SMALL E (`U+1D522`). - MATHEMATICAL FRAKTUR SMALL O (`U+1D52C`). * Notes (#2443, #444) * Refine
This commit is contained in:
parent
edfd04dae6
commit
67f12d42af
11 changed files with 382 additions and 119 deletions
|
@ -2,8 +2,9 @@ $$include '../meta/macros.ptl'
|
|||
|
||||
import [mix linreg clamp fallback boole boolePn] from "@iosevka/util"
|
||||
import [Transform] from "@iosevka/geometry/transform"
|
||||
import [Interpolator] from "@iosevka/geometry/spiro-control"
|
||||
import [Interpolator WithKnotProxy] from "@iosevka/geometry/spiro-control"
|
||||
import [RadicalGeometry StrokeGeometry RemoveHolesGeometry] from "@iosevka/geometry"
|
||||
import [CMixCoord] from "@iosevka/font-kits/derived-coordinates"
|
||||
|
||||
glyph-module
|
||||
|
||||
|
@ -510,6 +511,9 @@ glyph-block CommonShapes : begin
|
|||
define [hookEndBlender before after args] : begin
|
||||
return : HookShape before after false args
|
||||
|
||||
define [hookProxy args] : begin
|
||||
return : g4 [new CMixCoord 0.5] args.y
|
||||
|
||||
glyph-block-export hookstart
|
||||
define flex-params [hookstart] : begin
|
||||
local-parameter : y
|
||||
|
@ -517,8 +521,8 @@ glyph-block CommonShapes : begin
|
|||
local-parameter : swTerminal -- sw
|
||||
local-parameter : isTail -- false
|
||||
local-parameter : noSwash -- false
|
||||
return : Interpolator hookStartBlender
|
||||
object y sw swTerminal isTail noSwash
|
||||
local args : object y sw swTerminal isTail noSwash
|
||||
return : WithKnotProxy [hookProxy args] : Interpolator hookStartBlender args
|
||||
|
||||
glyph-block-export hookend
|
||||
define flex-params [hookend] : begin
|
||||
|
@ -527,8 +531,8 @@ glyph-block CommonShapes : begin
|
|||
local-parameter : swTerminal -- sw
|
||||
local-parameter : isTail -- false
|
||||
local-parameter : noSwash -- false
|
||||
return : Interpolator hookEndBlender
|
||||
object y sw swTerminal isTail noSwash
|
||||
local args : object y sw swTerminal isTail noSwash
|
||||
return : WithKnotProxy [hookProxy args] : Interpolator hookEndBlender args
|
||||
|
||||
glyph-block-export arch
|
||||
define arch : namespace
|
||||
|
@ -569,6 +573,11 @@ glyph-block CommonShapes : begin
|
|||
set r : 0.5 + (r - 0.5) * (v + w) / (u * 2)
|
||||
return r
|
||||
|
||||
define [archBlenderProxy args] : begin
|
||||
return : [if args.compact g2 g4]
|
||||
new CMixCoord [fallback args.p 0.5] 0 args.mockPre args.mockPost
|
||||
begin args.y
|
||||
|
||||
define [archBlender _before _after args] : begin
|
||||
local before : fallback args.mockPre _before
|
||||
local after : fallback args.mockPost _after
|
||||
|
@ -600,8 +609,8 @@ glyph-block CommonShapes : begin
|
|||
local-parameter : mockPost -- nothing
|
||||
local-parameter : blendPre -- [arcvh]
|
||||
local-parameter : blendPost -- [archv]
|
||||
return : Interpolator archBlender
|
||||
object [lhs true] y p sw compact o swBefore swAfter mockPre mockPost blendPre blendPost
|
||||
local args : object [lhs true] y p sw compact o swBefore swAfter mockPre mockPost blendPre blendPost
|
||||
return : WithKnotProxy [archBlenderProxy args] : Interpolator archBlender args
|
||||
|
||||
export : define flex-params [rhs] : begin
|
||||
local-parameter : y
|
||||
|
@ -615,8 +624,8 @@ glyph-block CommonShapes : begin
|
|||
local-parameter : mockPost -- nothing
|
||||
local-parameter : blendPre -- [arcvh]
|
||||
local-parameter : blendPost -- [archv]
|
||||
return : Interpolator archBlender
|
||||
object [lhs false] y p sw compact o swBefore swAfter mockPre mockPost blendPre blendPost
|
||||
local args : object [lhs false] y p sw compact o swBefore swAfter mockPre mockPost blendPre blendPost
|
||||
return : WithKnotProxy [archBlenderProxy args] : Interpolator archBlender args
|
||||
|
||||
foreach side {lhs rhs} : begin
|
||||
set side.centerAt : object
|
||||
|
|
|
@ -4,6 +4,7 @@ import [GlyphBlock GlyphBuildExecutor] from "@iosevka/glyph/block"
|
|||
import as Gr from "@iosevka/glyph/relation"
|
||||
import as SpiroKit from "@iosevka/font-kits/spiro-kit"
|
||||
import as BooleKit from "@iosevka/font-kits/boole-kit"
|
||||
import as DerivedCoordinates from "@iosevka/font-kits/derived-coordinates"
|
||||
import [DesignParameters calculateMetrics setFontMetrics GenDivFrame] from "./meta/aesthetics.mjs"
|
||||
import [$NamedParameterPair$] from "@iosevka/util"
|
||||
|
||||
|
@ -81,6 +82,7 @@ export : define [buildGlyphs para recursive] : begin
|
|||
Superness DesignParameters.superness
|
||||
define BooleFns : BooleKit.SetupBuilders : object
|
||||
globalTransform Metrics.GlobalTransform
|
||||
define DerivedCoordinateFns : DerivedCoordinates.SetupBuilders : object
|
||||
|
||||
# Setup the capture
|
||||
define $$Capture$$ : object
|
||||
|
@ -93,6 +95,7 @@ export : define [buildGlyphs para recursive] : begin
|
|||
glyphStore
|
||||
SpiroFns
|
||||
BooleFns
|
||||
DerivedCoordinateFns
|
||||
DivFrame
|
||||
MarkSet
|
||||
AS_BASE
|
||||
|
|
|
@ -9,6 +9,7 @@ $$include '../meta/macros.ptl'
|
|||
import [mix fallback] from "@iosevka/util"
|
||||
import [SpiroPenGeometry] from "@iosevka/geometry"
|
||||
import [Vec2] from "@iosevka/geometry/point"
|
||||
import [Box] from "@iosevka/geometry/box"
|
||||
import [Interpolator] from "@iosevka/geometry/spiro-control"
|
||||
|
||||
glyph-module
|
||||
|
@ -57,6 +58,8 @@ glyph-block LetterLike-Fraktur-Shared : begin
|
|||
new Vec2 (-thickTf.x - this.thin) (-thickTf.y - this.thin)
|
||||
new Vec2 (-thickTf.x - this.thin) (-thickTf.y + this.thin)
|
||||
|
||||
public [box u d l r] : new Box [this.yt u] [this.yb d] [this.xl l] [this.xr r]
|
||||
|
||||
public [xl x] : x + this.thick
|
||||
public [xr x] : x - this.thick
|
||||
public [xp l r p] : mix [this.xl l] [this.xr r] p
|
||||
|
@ -93,8 +96,8 @@ glyph-block LetterLike-Fraktur-Shared : begin
|
|||
define LbFootRise : 0.375 * SHook + 0.375 * S.thick
|
||||
|
||||
glyph-block-export SlopeA SlopeB
|
||||
define SlopeA : DecoSizeY / DecoSizeX
|
||||
define SlopeB 1.0
|
||||
define SlopeA : 0.875 * DecoSizeY / DecoSizeX
|
||||
define SlopeB : 0.875 * -[mix SlopeA 1.0 0.5]
|
||||
|
||||
# Wave blenders
|
||||
glyph-block-export Wave
|
||||
|
@ -115,11 +118,23 @@ glyph-block LetterLike-Fraktur-Shared : begin
|
|||
g2 [mix before.x after.x 0.375] after.y
|
||||
g2 [mix before.x after.x 0.625] before.y
|
||||
|
||||
glyph-block-export PHexTop
|
||||
define PHexTop : 0.5 + 0.5 * ([Math.max DecoSizeX frakThin] / (RightSB - SB))
|
||||
glyph-block-export PHexBot
|
||||
define PHexBot : 1 - PHexTop
|
||||
|
||||
glyph-block LetterLike-Fraktur : begin
|
||||
glyph-block-import Common-Derivatives
|
||||
glyph-block-import CommonShapes
|
||||
glyph-block-import LetterLike-Fraktur-Shared : S F T fraktur-stroke
|
||||
glyph-block-import LetterLike-Fraktur-Shared : DecoSizeX DecoSizeY SlopeA SlopeB
|
||||
glyph-block-import LetterLike-Fraktur-Shared : Wave LbFootRise FHook
|
||||
glyph-block-import LetterLike-Fraktur-Shared : Wave LbFootRise FHook PHexTop PHexBot
|
||||
|
||||
|
||||
define [LtDecorativeWave sx sy ey] : fraktur-stroke F
|
||||
g2.ld.start [F.connL S : S.xl sx] [F.connB S : S.yt sy]
|
||||
~~~ [Wave.vc (-Wave.DepthX)]
|
||||
g2.ld.end pre@ (ey - 2 * S.thick)
|
||||
|
||||
create-glyph "frak/C" 0x212D : glyph-proc
|
||||
include : MarkSet.capital
|
||||
|
@ -137,7 +152,7 @@ glyph-block LetterLike-Fraktur : begin
|
|||
include : fraktur-stroke S
|
||||
g4.ld.start [S.xp SB RightSB 0.1] ([S.yt CAP] - 0.1 * ArchDepthA)
|
||||
flat [S.xl SB] ([S.yt CAP] - 0.6 * ArchDepthA)
|
||||
curl same-x ([S.yb 0] + ArchDepthB)
|
||||
curl pre@ ([S.yb 0] + ArchDepthB)
|
||||
~~~ [hookend [S.yb 0] (sw -- S.thick)]
|
||||
g2 [S.xr RightSB] ([S.yb 0] + SHook)
|
||||
|
||||
|
@ -151,7 +166,7 @@ glyph-block LetterLike-Fraktur : begin
|
|||
include : fraktur-stroke F
|
||||
g2.ld.start [F.connL S xCenter] [F.connB S : [S.yt CAP] - DecoSizeX]
|
||||
~~~ [Wave.vc (-Wave.DepthX)]
|
||||
g2.ld.end same-x (CAP * 0.375)
|
||||
g2.ld.end pre@ (CAP * 0.375)
|
||||
|
||||
create-glyph "frak/H" 0x210C : glyph-proc
|
||||
include : MarkSet.capDesc
|
||||
|
@ -190,36 +205,32 @@ glyph-block LetterLike-Fraktur : begin
|
|||
g2.ld.start [S.xr RightSB] [S.yt CAP]
|
||||
~~~ [Wave.h]
|
||||
g2.ld.mid [S.xl SB] [S.yt (CAP - Wave.DepthY)]
|
||||
include : fraktur-stroke F
|
||||
g2.ld.start [F.connL S : S.xl SB] [F.connB S : S.yt (CAP - Wave.DepthY)]
|
||||
~~~ [Wave.vc (-Wave.DepthX)]
|
||||
g2.ld.end [F.connL S : S.xl SB] (CAP * 0.625 - 2 * S.thick)
|
||||
include : LtDecorativeWave [S.xl SB] (CAP - Wave.DepthY) (CAP * 0.625)
|
||||
|
||||
create-glyph "frak/I" 0x2111 : glyph-proc
|
||||
include : MarkSet.capital
|
||||
include : IJTopStroke
|
||||
# Main stroke
|
||||
|
||||
local iBox : S.box CAP 0 SB RightSB
|
||||
include : fraktur-stroke S
|
||||
g2.ld.start [S.xr RightSB] [S.yt CAP]
|
||||
g4 [S.xp SB RightSB 0.75] [S.yp ArchDepthA CAP 0.625]
|
||||
g4 [S.xr RightSB] [S.yb ArchDepthA]
|
||||
~~~ [hookend [S.yb 0] (sw -- S.thick)]
|
||||
g2 [S.xl SB] [S.yb SHook]
|
||||
g2.ld.start iBox.right iBox.top
|
||||
g4 [iBox.xp 0.7] [mix@ 0.375]
|
||||
g4 iBox.right (post@ <+> ArchDepthA)
|
||||
hookend (sw -- S.thick) iBox.bot
|
||||
g2 iBox.left (pre@ <+> SHook)
|
||||
|
||||
create-glyph "frak/J" 0x1D50D : glyph-proc
|
||||
include : MarkSet.capDesc
|
||||
|
||||
include : IJTopStroke
|
||||
|
||||
# Main stroke
|
||||
local mainStrokeTop : S.yt (CAP - 1.5 * Wave.DepthY - 2 * S.thick)
|
||||
local jBox : S.box [S.yt (CAP - 1.5 * Wave.DepthY - 2 * S.thick)] Descender SB RightSB
|
||||
include : fraktur-stroke S
|
||||
g4 [S.xr RightSB] (mainStrokeTop - FHook)
|
||||
~~~ [hookstart mainStrokeTop (sw -- S.thick)]
|
||||
g4 [S.xr : mix SB RightSB 0.6] (mainStrokeTop - 0.6 * ArchDepthA)
|
||||
g4 [S.xr RightSB] [S.yb (Descender + ArchDepthA)]
|
||||
~~~ [hookend [S.yb Descender] (sw -- S.thick)]
|
||||
g2 [S.xl SB] [S.yb (Descender + SHook)]
|
||||
g4 jBox.right (post@ <-> FHook)
|
||||
hookstart (sw -- S.thick) jBox.top
|
||||
g4 ([jBox.xp 0.6] - S.thick) (pre@ <-> 0.6 * ArchDepthA)
|
||||
g4 jBox.right (post@ <+> ArchDepthA)
|
||||
hookend (sw -- S.thick) jBox.bot
|
||||
g2 jBox.left (pre@ <+> SHook)
|
||||
|
||||
create-glyph "frak/R" 0x211C : glyph-proc
|
||||
include : MarkSet.capital
|
||||
|
@ -231,10 +242,7 @@ glyph-block LetterLike-Fraktur : begin
|
|||
local adb : 0.6 * ArchDepthB
|
||||
|
||||
# Deocration at top-left
|
||||
include : fraktur-stroke F
|
||||
g2.ld.start [F.connL S : S.xl xExt] [F.connB S : S.yt (CAP - ltHook)]
|
||||
~~~ [Wave.vc (-Wave.DepthX)]
|
||||
g2.ld.end [F.connL S : S.xl xExt] (CAP * 0.625 - 2 * S.thick)
|
||||
include : LtDecorativeWave xExt (CAP - ltHook) (CAP * 0.625)
|
||||
|
||||
local xMidStrokeL : S.xl xLeftStem
|
||||
local xMidStrokeR : S.xp xLeftStem RightSB 0.625
|
||||
|
@ -251,16 +259,16 @@ glyph-block LetterLike-Fraktur : begin
|
|||
g2.ru.start [S.xl xExt] [S.yt (CAP - ltHook)]
|
||||
~~~ [arch.rhs [S.yt CAP] 0.6 (blendPre -- null)]
|
||||
flat [S.xl xLeftStem] [Math.max ([S.yb CAP] - ada) yArchStart]
|
||||
curl [S.xl xLeftStem] yMidStrokeL
|
||||
corner [S.xl xExt] ([S.yb 0] + LbFootRise)
|
||||
g2c.right.end (Middle - DecoSizeX) [S.yb 0]
|
||||
corner Middle ([S.yb 0] + DecoSizeY)
|
||||
curl (pre@ <+> 0) yMidStrokeL
|
||||
corner [S.xl xExt] (post@ <+> LbFootRise)
|
||||
g2c.right.end (post@ <-> DecoSizeX) [S.yb 0]
|
||||
corner Middle (pre@ <+> DecoSizeY)
|
||||
|
||||
# Top-right arch
|
||||
include : fraktur-stroke S
|
||||
flat xArchStart yArchStart
|
||||
corner xArchTop yArchTop
|
||||
g2.down.mid [S.xr RightSB] [mix yMidStrokeR yArchTop 0.5]
|
||||
g2.down.mid [S.xr RightSB] [mix@ 0.5]
|
||||
flat xMidStrokeR yMidStrokeR
|
||||
curl xMidStrokeL yMidStrokeL
|
||||
|
||||
|
@ -271,32 +279,62 @@ glyph-block LetterLike-Fraktur : begin
|
|||
|
||||
# Leg
|
||||
include : fraktur-stroke S
|
||||
g2 xLegStart yLegStart
|
||||
g2 (xLegStart + TINY) (yLegStart + SlopeA * TINY)
|
||||
flat [mix xLegStart xLegEnd 0.75] (yLegStart - adb)
|
||||
curl [mix xLegStart xLegEnd 0.75] (yLegEnd + ada)
|
||||
corner xLegEnd yLegEnd
|
||||
corner (xLegEnd + DecoSizeX) (yLegEnd + DecoSizeY)
|
||||
[g2.sr SlopeA].start xLegStart yLegStart
|
||||
flat [mix@ 0.75] (pre@ <-> adb)
|
||||
decor@ : curl pre@ (post@ <+> ada)
|
||||
corner xLegEnd yLegEnd
|
||||
corner (pre@ <+> DecoSizeX) (pre@ <+> DecoSizeY)
|
||||
|
||||
create-glyph "frak/Z" 0x2128 : glyph-proc
|
||||
include : MarkSet.capital
|
||||
|
||||
local zBox : S.box CAP 0 SB RightSB
|
||||
|
||||
# Top Stroke
|
||||
include : fraktur-stroke S
|
||||
g2.ld.start [S.xr RightSB] [S.yt CAP]
|
||||
g2.ld.start zBox.right zBox.top
|
||||
~~~ [Wave.h]
|
||||
g2.ld.mid [S.xl SB] [S.yt (CAP - Wave.DepthY)]
|
||||
g2.ld.mid zBox.left (pre@ <-> Wave.DepthY)
|
||||
|
||||
# Bottom Stroke
|
||||
local diag : list
|
||||
corner [S.xr RightSB] [S.yt CAP]
|
||||
corner [S.xp SB RightSB 0.166] [S.yp 0 CAP 0.5]
|
||||
corner zBox.right zBox.top
|
||||
corner [zBox.xp 0.166] [zBox.yp 0.5]
|
||||
include : fraktur-stroke S diag
|
||||
include : difference
|
||||
fraktur-stroke S
|
||||
g2.ru.start [S.xp SB RightSB 0.166] [S.yp 0 CAP 0.5]
|
||||
~~~ [arch.rhs [S.yp 0 CAP 0.55] 0.375 (sw -- S.thick)]
|
||||
g2.down.mid [S.xr RightSB] [S.yb ArchDepthA]
|
||||
~~~ [hookend [S.yb 0] (sw -- S.thick)]
|
||||
g2 [S.xl SB] [S.yb SHook]
|
||||
g2.ru.start diag.1.x diag.1.y
|
||||
~~~ [arch.rhs [zBox.yp 0.55] 0.375 (sw -- S.thick)]
|
||||
g2.down.mid zBox.right (post@ <+> ArchDepthA)
|
||||
hookend zBox.bot (sw -- S.thick)
|
||||
g2 zBox.left (pre@ <+> SHook)
|
||||
MaskAboveLine diag.0.x diag.0.y diag.1.x diag.1.y 4
|
||||
|
||||
|
||||
create-glyph "frak/e" 0xAB32 : glyph-proc
|
||||
include : MarkSet.e
|
||||
local eBox : S.box XH 0 SB RightSB
|
||||
include : fraktur-stroke S
|
||||
corner (eBox.left + S.thick) [post@slope SlopeA]
|
||||
g2 eBox.xMid [eBox.yp DesignParameters.eBarPos]
|
||||
corner eBox.right [pre@slope SlopeA]
|
||||
corner [eBox.xp PHexTop] eBox.top
|
||||
corner eBox.left [pre@slope SlopeA]
|
||||
curl pre@ (post@ <+> ArchDepthB)
|
||||
[g2c.sr (-SlopeA)].end [eBox.xp PHexTop] eBox.bot
|
||||
corner eBox.right [pre@slope SlopeA]
|
||||
|
||||
alias 'mathFrak/e' 0x1D522 'frak/e'
|
||||
|
||||
create-glyph "frak/o" 0xAB3D : glyph-proc
|
||||
include : MarkSet.e
|
||||
local oBox : S.box XH 0 SB RightSB
|
||||
include : fraktur-stroke S
|
||||
[cg2.sr SlopeB].start [oBox.xp PHexTop] oBox.top
|
||||
g2.down.mid oBox.right [mix@ 0.5]
|
||||
[g2c.sl SlopeA].start [oBox.xp PHexBot] oBox.bot
|
||||
corner oBox.left [pre@slope SlopeB]
|
||||
corner pre@ [post@slope SlopeA]
|
||||
close
|
||||
|
||||
alias 'mathFrak/o' 0x1D52C 'frak/o'
|
||||
|
|
|
@ -7,9 +7,18 @@ define-operator "--" 890 'right' : syntax-rules
|
|||
define-operator "~>" 880 'right' : syntax-rules
|
||||
`(@l ~> @r) `{.left @l .right @r}
|
||||
|
||||
### The operator indicating a "mix"
|
||||
### The operator indicating a "delay"
|
||||
define-operator prefix "~~~" : syntax-rules
|
||||
`(~~~ @x) : dirty [formOf x]
|
||||
`(~~~ @x) : dirty `[decor@ @x]
|
||||
|
||||
### This operator is a method variant of (+)
|
||||
define-operator infix "<+>" 350 'left' : syntax-rules
|
||||
`(@l <+> @r) : dirty `[@l.op_add @r]
|
||||
|
||||
### This operator is a method variant of (-)
|
||||
define-operator infix "<->" 350 'left' : syntax-rules
|
||||
`(@l <-> @r) : dirty `[@l.op_sub @r]
|
||||
|
||||
|
||||
### Macro for identity match
|
||||
define-macro Just : begin
|
||||
|
@ -281,10 +290,13 @@ define-macro glyph-block : syntax-rules
|
|||
NarrowUnicodeT WideUnicodeT VERY-FAR TINY]
|
||||
define spiroFnImports `[g4 g2 corner flat curl close end straight g2c cg2 flatc ccurl widths
|
||||
disable-contrast heading unimportant important alsoThru alsoThruThem bezControls
|
||||
quadControls archv arcvh dispiro spiro-outline spiro-collect same-x same-y same-x-post
|
||||
same-y-post]
|
||||
quadControls archv arcvh dispiro spiro-outline spiro-collect]
|
||||
|
||||
define booleFnImports `[union intersection difference]
|
||||
|
||||
define drvCoordImports `[pre@ post@ mix@ pre@slope post@slope
|
||||
decor@ decor@@ decor@@@ pre@tang-in post@tang-in pre@tang-out post@tang-out]
|
||||
|
||||
dirty `[$GlyphBlocks$.push : lambda [$Capture_Ext$] : begin \\
|
||||
$Capture_Ext$.$Exec$.defineGlyphBlock $Capture_Ext$ @blockName
|
||||
function [$Capture$ $ExportCapture$] : begin
|
||||
|
@ -292,6 +304,7 @@ define-macro glyph-block : syntax-rules
|
|||
define [object @::[metricImports.filter : lambda [x] variableSet.(x)]] $Capture$.Metrics
|
||||
define [object @::[spiroFnImports.filter : lambda [x] variableSet.(x)]] $Capture$.SpiroFns
|
||||
define [object @::[booleFnImports.filter : lambda [x] variableSet.(x)]] $Capture$.BooleFns
|
||||
define [object @::drvCoordImports] $Capture$.DerivedCoordinateFns
|
||||
|
||||
* @body
|
||||
|
||||
|
|
|
@ -212,6 +212,7 @@ export : define decompOverrides : object
|
|||
0xA7A9 { 's' 'oblStrike' }
|
||||
|
||||
0xAB30 { 'scripta' 'hStrike' }
|
||||
0xAB3E { 'frak/o' 'shortSlash' }
|
||||
0xAB3F { 'turnc' 'shortSlash' }
|
||||
0xAB4F { 'uShortLeg' 'hStrike' }
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"version": "31.1.0",
|
||||
"private": true,
|
||||
"exports": {
|
||||
"./derived-coordinates": "./src/derived-coordinates.mjs",
|
||||
"./boole-kit": "./src/boole-kit.mjs",
|
||||
"./spiro-kit": "./src/spiro-kit.mjs"
|
||||
},
|
||||
|
|
171
packages/font-kits/src/derived-coordinates.mjs
Normal file
171
packages/font-kits/src/derived-coordinates.mjs
Normal file
|
@ -0,0 +1,171 @@
|
|||
import {
|
||||
DecorInterpolator,
|
||||
DEP_POST_X,
|
||||
DEP_POST_Y,
|
||||
DEP_PRE_X,
|
||||
DEP_PRE_Y,
|
||||
DEP_SAME_X,
|
||||
DEP_SAME_Y,
|
||||
DerivedCoordinateBase,
|
||||
InterpolatorBase,
|
||||
} from "@iosevka/geometry/spiro-control";
|
||||
|
||||
const TINY = 1 / 128;
|
||||
|
||||
export function SetupBuilders(_bindings) {
|
||||
return {
|
||||
// Derived coordinates
|
||||
// pre@, post@: copy or ade delta to the pre or post point's X or Y coordinate
|
||||
// use with pre@ or like (pre@ <+> delta)
|
||||
"pre@": new CDeltaPre(0),
|
||||
"post@": new CDeltaPost(0),
|
||||
|
||||
// "Tangent" derived cooredinates, which is simply a tiny delta.
|
||||
"pre@tang-out": s => new CDeltaPre(TINY * s),
|
||||
"post@tang-out": s => new CDeltaPost(TINY * s),
|
||||
"pre@tang-in": s => new CDeltaPre(-TINY * s),
|
||||
"post@tang-in": s => new CDeltaPost(-TINY * s),
|
||||
|
||||
// mix@: mix between pre and post point's X or Y coordinates
|
||||
// usage [mix@ proportion] or [mix@ proportion delta]
|
||||
"mix@": (p, delta) => new CMixCoord(p, delta),
|
||||
|
||||
// pre@slope, post@slope: Get the coordiante using the pre/post point's coordinate and a
|
||||
// slope. An optional delta can be added to the result. See the definitions for more
|
||||
// details.
|
||||
"pre@slope": (s, delta) => new CAtSlopePre(s, delta),
|
||||
"post@slope": (s, delta) => new CAtSlopePost(s, delta),
|
||||
|
||||
// Interpolators
|
||||
|
||||
// decor@, decor@@, decor@@@: Add a "delay" to the thing inside when resolving the spiro
|
||||
// controls. This is a very useful utility for coordinate propagation, which allow us to
|
||||
// "skip" the current point and go to the next or previous point. The number of @'s
|
||||
// determines the number of times the delay is applied.
|
||||
"decor@": x => new DecorInterpolator(x),
|
||||
"decor@@": x => new DecorInterpolator(new DecorInterpolator(x)),
|
||||
"decor@@@": x => new DecorInterpolator(new DecorInterpolator(new DecorInterpolator(x))),
|
||||
};
|
||||
}
|
||||
|
||||
class CDeltaPre extends DerivedCoordinateBase {
|
||||
constructor(delta) {
|
||||
super();
|
||||
this.delta = delta;
|
||||
}
|
||||
op_add(delta) {
|
||||
return new CDeltaPre(this.delta + delta);
|
||||
}
|
||||
op_sub(delta) {
|
||||
return new CDeltaPre(this.delta - delta);
|
||||
}
|
||||
getDependencyForX() {
|
||||
return DEP_PRE_X;
|
||||
}
|
||||
getDependencyForY() {
|
||||
return DEP_PRE_Y;
|
||||
}
|
||||
resolveX(pre, curr, post) {
|
||||
return pre.x + this.delta;
|
||||
}
|
||||
resolveY(pre, curr, post) {
|
||||
return pre.y + this.delta;
|
||||
}
|
||||
}
|
||||
|
||||
class CDeltaPost extends DerivedCoordinateBase {
|
||||
constructor(delta) {
|
||||
super();
|
||||
this.delta = delta;
|
||||
}
|
||||
op_add(delta) {
|
||||
return new CDeltaPost(this.delta + delta);
|
||||
}
|
||||
op_sub(delta) {
|
||||
return new CDeltaPost(this.delta - delta);
|
||||
}
|
||||
getDependencyForX() {
|
||||
return DEP_POST_X;
|
||||
}
|
||||
getDependencyForY() {
|
||||
return DEP_POST_Y;
|
||||
}
|
||||
resolveX(pre, curr, post) {
|
||||
return post.x + this.delta;
|
||||
}
|
||||
resolveY(pre, curr, post) {
|
||||
return post.y + this.delta;
|
||||
}
|
||||
}
|
||||
|
||||
export class CMixCoord extends DerivedCoordinateBase {
|
||||
constructor(proportion, delta, mockPre, mockPost) {
|
||||
super();
|
||||
this.proportion = proportion;
|
||||
this.delta = delta || 0;
|
||||
this.mockPre = mockPre || null;
|
||||
this.mockPost = mockPost || null;
|
||||
}
|
||||
getDependencyForX() {
|
||||
let f = 0;
|
||||
if (!this.mockPre) f |= DEP_PRE_X;
|
||||
if (!this.mockPost) f |= DEP_POST_X;
|
||||
return f;
|
||||
}
|
||||
getDependencyForY() {
|
||||
let f = 0;
|
||||
if (!this.mockPre) f |= DEP_PRE_Y;
|
||||
if (!this.mockPost) f |= DEP_POST_Y;
|
||||
return f;
|
||||
}
|
||||
resolveX(_pre, curr, _post) {
|
||||
const pre = this.mockPre || _pre;
|
||||
const post = this.mockPost || _post;
|
||||
return pre.x + this.proportion * (post.x - pre.x) + this.delta;
|
||||
}
|
||||
resolveY(_pre, curr, _post) {
|
||||
const pre = this.mockPre || _pre;
|
||||
const post = this.mockPost || _post;
|
||||
return pre.y + this.proportion * (post.y - pre.y) + this.delta;
|
||||
}
|
||||
}
|
||||
|
||||
class CAtSlopePre extends DerivedCoordinateBase {
|
||||
constructor(slope, delta) {
|
||||
super();
|
||||
this.slope = slope;
|
||||
this.delta = delta || 0;
|
||||
}
|
||||
getDependencyForX() {
|
||||
return DEP_PRE_X | DEP_PRE_Y | DEP_SAME_Y;
|
||||
}
|
||||
getDependencyForY() {
|
||||
return DEP_PRE_X | DEP_PRE_Y | DEP_SAME_X;
|
||||
}
|
||||
resolveY(pre, curr, post) {
|
||||
return pre.y + this.slope * (curr.x - pre.x) + this.delta;
|
||||
}
|
||||
resolveX(pre, curr, post) {
|
||||
return pre.x + (curr.y - pre.y) / this.slope + this.delta;
|
||||
}
|
||||
}
|
||||
|
||||
class CAtSlopePost extends DerivedCoordinateBase {
|
||||
constructor(slope, delta) {
|
||||
super();
|
||||
this.slope = slope;
|
||||
this.delta = delta || 0;
|
||||
}
|
||||
getDependencyForX() {
|
||||
return DEP_POST_X | DEP_POST_Y | DEP_SAME_Y;
|
||||
}
|
||||
getDependencyForY() {
|
||||
return DEP_POST_X | DEP_POST_Y | DEP_SAME_X;
|
||||
}
|
||||
resolveY(pre, curr, post) {
|
||||
return post.y + this.slope * (curr.x - post.x) + this.delta;
|
||||
}
|
||||
resolveX(pre, curr, post) {
|
||||
return post.x + (curr.y - post.y) / this.slope + this.delta;
|
||||
}
|
||||
}
|
|
@ -1,11 +1,6 @@
|
|||
import { DiSpiroGeometry, SpiroGeometry } from "@iosevka/geometry";
|
||||
import {
|
||||
BiKnotCollector,
|
||||
DEP_POST_X,
|
||||
DEP_POST_Y,
|
||||
DEP_PRE_X,
|
||||
DEP_PRE_Y,
|
||||
DerivedCoordinateBase,
|
||||
Interpolator,
|
||||
SpiroFlattener,
|
||||
TerminateInstruction,
|
||||
|
@ -109,40 +104,6 @@ function DirPairImpl(kPre, kCenter, kPost, dirX, dirY, dPre, dPost) {
|
|||
new UserCloseKnotPair(kCenter(x, y, af), tyPre, tyPost, dirX, dirY, dPre, dPost);
|
||||
}
|
||||
|
||||
/// Derivative coordinates
|
||||
class CSameX extends DerivedCoordinateBase {
|
||||
getDependency() {
|
||||
return DEP_PRE_X;
|
||||
}
|
||||
resolve(pre) {
|
||||
return pre.x;
|
||||
}
|
||||
}
|
||||
class CSameY extends DerivedCoordinateBase {
|
||||
getDependency() {
|
||||
return DEP_PRE_Y;
|
||||
}
|
||||
resolve(pre) {
|
||||
return pre.y;
|
||||
}
|
||||
}
|
||||
class CSameXPost extends DerivedCoordinateBase {
|
||||
getDependency() {
|
||||
return DEP_POST_X;
|
||||
}
|
||||
resolve(pre, curr, post) {
|
||||
return post.x;
|
||||
}
|
||||
}
|
||||
class CSameYPost extends DerivedCoordinateBase {
|
||||
getDependency() {
|
||||
return DEP_POST_Y;
|
||||
}
|
||||
resolve(pre, curr, post) {
|
||||
return post.y;
|
||||
}
|
||||
}
|
||||
|
||||
export function SetupBuilders(bindings) {
|
||||
const { Stroke, Superness } = bindings;
|
||||
|
||||
|
@ -193,7 +154,7 @@ export function SetupBuilders(bindings) {
|
|||
{ name: "ld", x: -1, y: -1 },
|
||||
];
|
||||
for (const [sink, kl, kc, kr] of knotTypes) {
|
||||
sink.sl = s => new DirectedKnotPairBuilder(bindings, kl, kc, kr, -1, s);
|
||||
sink.sl = s => new DirectedKnotPairBuilder(bindings, kl, kc, kr, -1, -s);
|
||||
sink.sr = s => new DirectedKnotPairBuilder(bindings, kl, kc, kr, 1, s);
|
||||
sink.dir = (dx, dy) => new DirectedKnotPairBuilder(bindings, kl, kc, kr, dx, dy);
|
||||
for (const d of directions) {
|
||||
|
@ -479,10 +440,5 @@ export function SetupBuilders(bindings) {
|
|||
dispiro,
|
||||
"spiro-outline": spiroOutline,
|
||||
"spiro-collect": spiroCollect,
|
||||
|
||||
"same-x": new CSameX(),
|
||||
"same-y": new CSameY(),
|
||||
"same-x-post": new CSameXPost(),
|
||||
"same-y-post": new CSameYPost(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { mix } from "@iosevka/util";
|
|||
export class Box {
|
||||
constructor(t, b, l, r) {
|
||||
this.top = t;
|
||||
this.bottom = this.bot = b;
|
||||
this.bot = this.bottom = b;
|
||||
this.left = l;
|
||||
this.right = r;
|
||||
this.xMid = this.xMiddle = mix(l, r, 0.5);
|
||||
|
@ -43,6 +43,10 @@ export class Box {
|
|||
this.right,
|
||||
);
|
||||
}
|
||||
|
||||
xp(t) {
|
||||
return this.mixX(t);
|
||||
}
|
||||
mixX(t) {
|
||||
return mix(this.left, this.right, t);
|
||||
}
|
||||
|
@ -52,6 +56,10 @@ export class Box {
|
|||
mixXMidRight(t) {
|
||||
return mix(this.xMid, this.right, t);
|
||||
}
|
||||
|
||||
yp(t) {
|
||||
return this.mixY(t);
|
||||
}
|
||||
mixY(t) {
|
||||
return mix(this.bottom, this.top, t);
|
||||
}
|
||||
|
|
|
@ -182,12 +182,17 @@ class CoordinatePropagator {
|
|||
const stateC = ic ? this.stateY : this.stateX;
|
||||
|
||||
if (stateC[i] === CR_RESOLVED) return;
|
||||
if (stateC[i] === CR_RESOLVING) throw new Error("Circular dependency detected");
|
||||
if (stateC[i] === CR_RESOLVING) {
|
||||
console.log(this);
|
||||
throw new Error("Circular dependency detected");
|
||||
}
|
||||
|
||||
stateC[i] = CR_RESOLVING;
|
||||
|
||||
if (depC[i] & DEP_PRE_X) this.solve(this.cycI(i - 1), 0);
|
||||
if (depC[i] & DEP_PRE_Y) this.solve(this.cycI(i - 1), 1);
|
||||
if (depC[i] & DEP_SAME_X) this.solve(this.cycI(i), 0);
|
||||
if (depC[i] & DEP_SAME_Y) this.solve(this.cycI(i), 1);
|
||||
if (depC[i] & DEP_POST_X) this.solve(this.cycI(i + 1), 0);
|
||||
if (depC[i] & DEP_POST_Y) this.solve(this.cycI(i + 1), 1);
|
||||
|
||||
|
@ -215,8 +220,10 @@ const RES_DEP_STAGE_INTERPOLATION = 2;
|
|||
export const DEP_SKIP = 0x1;
|
||||
export const DEP_PRE_X = 0x2;
|
||||
export const DEP_PRE_Y = 0x4;
|
||||
export const DEP_POST_X = 0x8;
|
||||
export const DEP_POST_Y = 0x10;
|
||||
export const DEP_SAME_X = 0x8;
|
||||
export const DEP_SAME_Y = 0x10;
|
||||
export const DEP_POST_X = 0x20;
|
||||
export const DEP_POST_Y = 0x40;
|
||||
|
||||
const DEP_PRE = DEP_PRE_X | DEP_PRE_Y;
|
||||
const DEP_POST = DEP_POST_X | DEP_POST_Y;
|
||||
|
@ -239,9 +246,9 @@ export class UserControlKnot {
|
|||
getDependency(stage) {
|
||||
switch (stage) {
|
||||
case RES_DEP_STAGE_COORDINATE_PROPOGATION_X:
|
||||
return typeof this.x === "number" ? 0 : this.x.getDependency(stage);
|
||||
return typeof this.x === "number" ? 0 : this.x.getDependencyForX();
|
||||
case RES_DEP_STAGE_COORDINATE_PROPOGATION_Y:
|
||||
return typeof this.y === "number" ? 0 : this.y.getDependency(stage);
|
||||
return typeof this.y === "number" ? 0 : this.y.getDependencyForY();
|
||||
case RES_DEP_STAGE_INTERPOLATION:
|
||||
return 0;
|
||||
default:
|
||||
|
@ -256,10 +263,10 @@ export class UserControlKnot {
|
|||
// console.log(this, ic, pre, post);
|
||||
switch (ic) {
|
||||
case 0:
|
||||
this.x = this.x.resolve(pre, this, post);
|
||||
this.x = this.x.resolveX(pre, this, post);
|
||||
break;
|
||||
case 1:
|
||||
this.y = this.y.resolve(pre, this, post);
|
||||
this.y = this.y.resolveY(pre, this, post);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -292,7 +299,7 @@ export class UserCloseKnotPair {
|
|||
}
|
||||
|
||||
getKernelKnot() {
|
||||
return this.center;
|
||||
return this.center.getKernelKnot();
|
||||
}
|
||||
resolveCoordiantePropogation(ic, pre, post) {
|
||||
this.center.resolveCoordiantePropogation(ic, pre, post);
|
||||
|
@ -333,11 +340,10 @@ export class InterpolatorBase {
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
getKernelKnot() {
|
||||
throw new Error("Unreachable");
|
||||
}
|
||||
resolveCoordiantePropogation(pre, post) {
|
||||
resolveCoordiantePropogation() {
|
||||
throw new Error("Unreachable");
|
||||
}
|
||||
|
||||
|
@ -349,6 +355,16 @@ export class InterpolatorBase {
|
|||
}
|
||||
}
|
||||
|
||||
export class DecorInterpolator extends InterpolatorBase {
|
||||
constructor(items) {
|
||||
super();
|
||||
this.items = items;
|
||||
}
|
||||
resolveInterpolation(pre, post) {
|
||||
return this.items;
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionInterpolator extends InterpolatorBase {
|
||||
constructor(blendFn, extraArgs) {
|
||||
super();
|
||||
|
@ -359,9 +375,45 @@ class FunctionInterpolator extends InterpolatorBase {
|
|||
return this.blendFn(pre, post, this.extraArgs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class denotes an interpolator that has a proxy knot. The proxy could be used in the
|
||||
* coordinate propagation stage to resolve dependencies.
|
||||
*/
|
||||
export class KnotProxyInterpolator extends InterpolatorBase {
|
||||
constructor(proxy, actual) {
|
||||
super();
|
||||
this.knotProxy = proxy;
|
||||
this.actual = actual;
|
||||
}
|
||||
|
||||
getDependency(stage) {
|
||||
switch (stage) {
|
||||
case RES_DEP_STAGE_COORDINATE_PROPOGATION_X:
|
||||
case RES_DEP_STAGE_COORDINATE_PROPOGATION_Y:
|
||||
return this.knotProxy.getDependency(stage);
|
||||
default:
|
||||
return this.actual.getDependency(stage);
|
||||
}
|
||||
}
|
||||
|
||||
getKernelKnot() {
|
||||
return this.knotProxy.getKernelKnot();
|
||||
}
|
||||
resolveCoordiantePropogation(ic, pre, post) {
|
||||
this.knotProxy.resolveCoordiantePropogation(ic, pre, post);
|
||||
}
|
||||
resolveInterpolation(pre, post) {
|
||||
return this.actual.resolveInterpolation(pre, post);
|
||||
}
|
||||
}
|
||||
|
||||
export function Interpolator(blender, restParameters) {
|
||||
return new FunctionInterpolator(blender, restParameters);
|
||||
}
|
||||
export function WithKnotProxy(proxy, actual) {
|
||||
return new KnotProxyInterpolator(proxy, actual);
|
||||
}
|
||||
|
||||
export class TerminateInstruction {
|
||||
constructor(type, af) {
|
||||
|
@ -378,10 +430,16 @@ export class TerminateInstruction {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
export class DerivedCoordinateBase {
|
||||
getDependency() {
|
||||
getDependencyForX() {
|
||||
throw new Error("Unimplemented");
|
||||
}
|
||||
resolve(pre, curr, post) {
|
||||
getDependencyForY() {
|
||||
throw new Error("Unimplemented");
|
||||
}
|
||||
resolveX(pre, curr, post) {
|
||||
throw new Error("Unimplemented");
|
||||
}
|
||||
resolveY(pre, curr, post) {
|
||||
throw new Error("Unimplemented");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue