Add frakturs in the Letterlike Symbols (#2438)
* * Add characters: - BLACK-LETTER CAPITAL I (`U+2111`). - BLACK-LETTER CAPITAL R (`U+211C`). * Complete frakturs in Letterlike Symbols block * Update geom cache version
This commit is contained in:
parent
61ad3c365c
commit
bff9e0b1c5
11 changed files with 496 additions and 8 deletions
6
changes/31.0.1.md
Normal file
6
changes/31.0.1.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
* Add characters:
|
||||
- BLACK-LETTER CAPITAL H (`U+210C`).
|
||||
- BLACK-LETTER CAPITAL I (`U+2111`).
|
||||
- BLACK-LETTER CAPITAL R (`U+211C`).
|
||||
- BLACK-LETTER CAPITAL Z (`U+2128`).
|
||||
- BLACK-LETTER CAPITAL C (`U+212D`).
|
|
@ -1,16 +1,14 @@
|
|||
{
|
||||
"name": "@iosevka/monorepo",
|
||||
"version": "31.0.0",
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"tools/*"
|
||||
],
|
||||
"workspaces": ["packages/*", "tools/*"],
|
||||
"scripts": {
|
||||
"build": "verda -f verdafile.mjs",
|
||||
"bump-ver": "node tools/misc/src/update-package-json-version.mjs && npm install && node tools/misc/src/generate-ttfa-ranges.mjs",
|
||||
"clean": "verda -f verdafile.mjs clean",
|
||||
"lint": "eslint",
|
||||
"lint:fix": "eslint --fix",
|
||||
"copy-char-name-to-markdown": "node tools/misc/src/copy-char-name-to-markdown.mjs",
|
||||
"generate-release-sha-file": "node tools/misc/src/generate-release-sha-file.mjs release-archives/*.zip release-archives/SHA-256.txt"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -120,6 +120,7 @@ export : define [buildGlyphs para recursive] : begin
|
|||
|
||||
# Letter-likes
|
||||
run-glyph-module "./letter-like/cursive.mjs"
|
||||
run-glyph-module "./letter-like/fraktur.mjs"
|
||||
|
||||
# Symbols
|
||||
run-glyph-module "./symbol/shared.mjs"
|
||||
|
|
295
packages/font-glyphs/src/letter-like/fraktur.ptl
Normal file
295
packages/font-glyphs/src/letter-like/fraktur.ptl
Normal file
|
@ -0,0 +1,295 @@
|
|||
###### Fraktur letterforms
|
||||
###
|
||||
### This file is used to define the letterforms of the Fraktur style.
|
||||
### For simplicity, the letters here will *NOT* support any variants.
|
||||
###
|
||||
|
||||
$$include '../meta/macros.ptl'
|
||||
|
||||
import [mix fallback] from "@iosevka/util"
|
||||
import [SpiroPenGeometry] from "@iosevka/geometry"
|
||||
import [Vec2] from "@iosevka/geometry/point"
|
||||
|
||||
glyph-module
|
||||
|
||||
glyph-block LetterLike-Fraktur-Shared : begin
|
||||
|
||||
# [fraktur-stroke profile ...] will construct a Fraktur stroke from a pen profile and a list
|
||||
# of control knots. The knots will form a (usually open) spiro path, then the result will be
|
||||
# the area that the pen tip covers when moving along the path.
|
||||
glyph-block-export fraktur-stroke
|
||||
define [fraktur-stroke profile __knots] : begin
|
||||
local knots : {}.slice.call arguments 1
|
||||
return : new FrakturImpl profile knots
|
||||
|
||||
class FrakturImpl
|
||||
public [new profile knots] : begin
|
||||
this.profile = profile
|
||||
this.knots = knots
|
||||
|
||||
public [applyToGlyph glyph] : begin
|
||||
local c : spiro-collect glyph this.knots
|
||||
local geom : new SpiroPenGeometry
|
||||
begin c.gizmo
|
||||
begin c.collector.closed
|
||||
this.profile.getPenShape c.gizmo
|
||||
c.collector.controls.map : function [k] [k.toMono]
|
||||
return : glyph.includeGeometry geom
|
||||
|
||||
|
||||
# A pen profile describes a virtual flat-tip pen. We use a 45-degree arrangement to
|
||||
# simplify the math.
|
||||
class FrakturProfile
|
||||
public [new thick thin] : begin
|
||||
# .thick is the half length of the flat tip, projected to the X/Y axis
|
||||
this.thick = 0.25 * [Math.sqrt 2] * thick
|
||||
# .thin is the half width of the thin tip, projected to the X/Y axis
|
||||
this.thin = 0.25 * [Math.sqrt 2] * thin
|
||||
|
||||
public [getPenShape gizmo] : begin
|
||||
local thickTf : gizmo.applyOffsetXY this.thick this.thick
|
||||
list
|
||||
new Vec2 (thickTf.x - this.thin) (thickTf.y + this.thin)
|
||||
new Vec2 (thickTf.x + this.thin) (thickTf.y + this.thin)
|
||||
new Vec2 (thickTf.x + this.thin) (thickTf.y - this.thin)
|
||||
new Vec2 (-thickTf.x + this.thin) (-thickTf.y - this.thin)
|
||||
new Vec2 (-thickTf.x - this.thin) (-thickTf.y - this.thin)
|
||||
new Vec2 (-thickTf.x - this.thin) (-thickTf.y + this.thin)
|
||||
|
||||
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
|
||||
|
||||
public [yb y] : y + this.thick
|
||||
public [yt y] : y - this.thick
|
||||
public [yp b t p] : mix [this.yb b] [this.yt t] p
|
||||
|
||||
# Connection to another profile's pen tip position
|
||||
public [connL otherProfile x] : x - otherProfile.thick + this.thick
|
||||
public [connR otherProfile x] : x + otherProfile.thick - this.thick
|
||||
public [connB otherProfile y] : y - otherProfile.thick + this.thick
|
||||
public [connT otherProfile y] : y + otherProfile.thick - this.thick
|
||||
|
||||
# Stroke widths
|
||||
define frakThin : 1.0 * [AdviceStroke 8]
|
||||
define frakThick : 1.0 * Stroke
|
||||
define frakFine : 1.0 * [AdviceStroke 4] # For decoration
|
||||
|
||||
glyph-block-export S
|
||||
define S : new FrakturProfile frakThick (0.5 * frakThin)
|
||||
|
||||
glyph-block-export F
|
||||
define F : new FrakturProfile frakFine (0.5 * frakThin)
|
||||
|
||||
glyph-block-export T
|
||||
define T : new FrakturProfile frakThin (0.5 * frakThin)
|
||||
|
||||
glyph-block LetterLike-Fraktur : begin
|
||||
glyph-block-import CommonShapes
|
||||
glyph-block-import LetterLike-Fraktur-Shared : S F T fraktur-stroke
|
||||
|
||||
define DecoSizeX : 0.15 * (RightSB - SB)
|
||||
define DecoSizeY : 0.08 * (RightSB - SB)
|
||||
define FHook : 0.4 * SHook - 0.25 * S.thick
|
||||
define WaveDepth : 0.4 * SHook - 0.25 * S.thick
|
||||
define WaveDepthX : 0.375 * SHook + 0.5 * S.thick
|
||||
define LbFootRise : 0.375 * SHook + 0.375 * S.thick
|
||||
|
||||
define flex-params [HWave] : begin
|
||||
local-parameter pen
|
||||
local-parameter w
|
||||
local-parameter l
|
||||
local-parameter r
|
||||
local-parameter b
|
||||
local-parameter t
|
||||
|
||||
return : fraktur-stroke pen
|
||||
g2 l b
|
||||
g2 (l + TINY) (b + TINY)
|
||||
g2 [mix l r 0.375] [if w (b + w) t]
|
||||
g2 [mix l r 0.625] [if w (t - w) b]
|
||||
g2 (r - TINY) (t - TINY)
|
||||
g2 r t
|
||||
|
||||
define flex-params [VWave] : begin
|
||||
local-parameter pen
|
||||
local-parameter w
|
||||
local-parameter l
|
||||
local-parameter r
|
||||
local-parameter b
|
||||
local-parameter t
|
||||
|
||||
return : fraktur-stroke pen
|
||||
g2 r t
|
||||
g2 (r - TINY) (t - TINY)
|
||||
g2 [if w (r - w) l] [mix b t 0.625]
|
||||
g2 [if w (l + w) r] [mix b t 0.375]
|
||||
g2 (l + TINY) (b + TINY)
|
||||
g2 l b
|
||||
|
||||
define flex-params [VCWave] : begin
|
||||
local-parameter pen
|
||||
local-parameter w
|
||||
local-parameter x
|
||||
local-parameter b
|
||||
local-parameter t
|
||||
|
||||
return : fraktur-stroke pen
|
||||
g2 x t
|
||||
g2 (x - TINY) (t - TINY)
|
||||
g2 [pen.xl (x - 0.5 * w)] [mix b t 0.625]
|
||||
g2 [pen.xr (x + 0.5 * w)] [mix b t 0.375]
|
||||
g2 (x + TINY) (b + TINY)
|
||||
g2 x b
|
||||
|
||||
create-glyph "frak/C" 0x212D : glyph-proc
|
||||
include : MarkSet.capital
|
||||
|
||||
# Top-right stroke
|
||||
include : fraktur-stroke S
|
||||
g2 [S.xr RightSB] [S.yt CAP]
|
||||
g2.left.mid ([S.xp SB RightSB 0.75] + 0.5 * DecoSizeX) ([S.yt CAP] - 0.5 * FHook)
|
||||
corner ([S.xp SB RightSB 0.5] + DecoSizeX) [S.yt CAP]
|
||||
corner [S.xp SB RightSB 0.5] ([S.yt CAP] - DecoSizeX)
|
||||
|
||||
# Left and bottom stroke
|
||||
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 [S.xl SB] ([S.yb 0] + ArchDepthB)
|
||||
~~~ [hookend [S.yb 0] (sw -- S.thick)]
|
||||
g2 [S.xr RightSB] ([S.yb 0] + SHook)
|
||||
|
||||
# A thin connection between the two strokes
|
||||
include : fraktur-stroke T
|
||||
g4.ru.start [T.connR S : S.xp SB RightSB 0.1] [T.connT S : [S.yt CAP] - 0.1 * ArchDepthA]
|
||||
~~~ [arch.lhs [T.yt CAP] 0.6 (sw -- T.thick) (blendPre -- null) (blendPost -- null)]
|
||||
g4.ld.end [S.xp SB RightSB 0.5] ([S.yt CAP] - DecoSizeX)
|
||||
|
||||
# Inner decoration
|
||||
include : VCWave F (WaveDepthX + 0.625 * S.thick)
|
||||
x -- [F.connL S : S.xp SB RightSB 0.5]
|
||||
t -- [F.connB S : [S.yt CAP] - DecoSizeX]
|
||||
b -- (CAP * 0.375)
|
||||
|
||||
create-glyph "frak/H" 0x210C : glyph-proc
|
||||
include : MarkSet.capDesc
|
||||
|
||||
local xExt : mix 0 SB 0.25
|
||||
local xLeftStem : Math.max (xExt + 1.5 * DecoSizeX) [mix SB RightSB 0]
|
||||
local ada : 0.6 * ArchDepthA
|
||||
local adb : 0.6 * ArchDepthB
|
||||
|
||||
# Top and left stroke
|
||||
include : fraktur-stroke S
|
||||
g2 [S.xr RightSB] ([S.yt CAP] - FHook)
|
||||
~~~ [arch.rhs [S.yt CAP] (sw -- S.thick) (blendPre -- null) (blendPost -- null)]
|
||||
corner [S.xl xLeftStem] ([S.yt CAP] - adb)
|
||||
curl [S.xl xLeftStem] [Math.min [S.yt (XH - FHook)] [S.yp 0 CAP 0.5]]
|
||||
corner [S.xl xExt] ([S.yb 0] + LbFootRise)
|
||||
g2c.right.end (Middle - DecoSizeX) [S.yb 0]
|
||||
corner Middle ([S.yb 0] + DecoSizeY)
|
||||
|
||||
# Middle and right stroke
|
||||
include : fraktur-stroke S
|
||||
g2 [S.xl xLeftStem] ([S.yt (XH - FHook)] - FHook)
|
||||
corner [S.xp xLeftStem RightSB 0.625] [S.yt (XH - FHook)]
|
||||
g2.down.mid [S.xr RightSB] [S.yp Descender(XH - FHook) 0.75]
|
||||
~~~ [alsoThru.g2 0.5 0.5]
|
||||
g2.down.mid [S.xp xLeftStem RightSB 0.75] [S.yp Descender(XH - FHook) 0.25]
|
||||
g2 [S.xr RightSB] [S.yb Descender]
|
||||
|
||||
create-glyph "frak/I" 0x2111 : glyph-proc
|
||||
include : MarkSet.capital
|
||||
|
||||
# Top Stroke
|
||||
include : HWave S
|
||||
l -- [S.xl SB]
|
||||
r -- [S.xr RightSB]
|
||||
b -- [S.yt (CAP - WaveDepth)]
|
||||
t -- [S.yt CAP]
|
||||
|
||||
# Deocration at top-left
|
||||
include : VCWave F WaveDepthX
|
||||
x -- [F.connL S : S.xl SB]
|
||||
b -- (CAP * 0.625)
|
||||
t -- [F.connB S : S.yt (CAP - WaveDepth)]
|
||||
|
||||
# Main stroke
|
||||
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]
|
||||
g2 [S.xr RightSB] [S.yb ArchDepthA]
|
||||
~~~ [hookend [S.yb 0] (sw -- S.thick)]
|
||||
g2 [S.xl SB] [S.yb SHook]
|
||||
|
||||
create-glyph "frak/R" 0x211C : glyph-proc
|
||||
include : MarkSet.capital
|
||||
|
||||
local xExt : mix 0 SB 0.25
|
||||
local xLeftStem : Math.max (xExt + 1.5 * DecoSizeX) [mix SB RightSB 0.166]
|
||||
local ltHook : 0.25 * Hook
|
||||
local ada : 0.6 * ArchDepthA
|
||||
local adb : 0.6 * ArchDepthB
|
||||
|
||||
# Deocration at top-left
|
||||
include : VCWave F WaveDepthX
|
||||
x -- [F.connL S : S.xl xExt]
|
||||
b -- (CAP * 0.625)
|
||||
t -- [F.connB S : S.yt (CAP - ltHook)]
|
||||
|
||||
local xMidStrokeL : S.xl xLeftStem
|
||||
local xMidStrokeR : S.xp xLeftStem RightSB 0.625
|
||||
local yMidStrokeL : S.yp 0 CAP 0.48
|
||||
local yMidStrokeR : S.yp 0 CAP 0.55
|
||||
|
||||
# Left stroke
|
||||
include : fraktur-stroke S
|
||||
g2.ru.start [S.xl xExt] [S.yt (CAP - ltHook)]
|
||||
~~~ [arch.rhs [S.yt CAP] 0.6 (blendPre -- null)]
|
||||
flat [S.xl xLeftStem] ([S.yb CAP] - adb)
|
||||
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)
|
||||
|
||||
# Top-right arch
|
||||
include : fraktur-stroke S
|
||||
flat [S.xl xLeftStem] ([S.yb CAP] - adb)
|
||||
corner [S.xp xLeftStem RightSB 0.75] [S.yt CAP]
|
||||
g2.down.mid [S.xr RightSB] [mix yMidStrokeR [S.yt CAP] 0.5]
|
||||
flat xMidStrokeR yMidStrokeR
|
||||
curl xMidStrokeL yMidStrokeL
|
||||
|
||||
local xLegStart : mix xMidStrokeL xMidStrokeR 0.75
|
||||
local yLegStart : mix yMidStrokeL yMidStrokeR 0.75
|
||||
local xLegEnd RightSB
|
||||
local yLegEnd : S.yb 0
|
||||
|
||||
# Leg
|
||||
include : fraktur-stroke S
|
||||
g2 xLegStart yLegStart
|
||||
g2 [mix xLegStart xMidStrokeR 0.001] [mix yLegStart yMidStrokeR 0.001]
|
||||
flat [mix xLegStart xLegEnd 0.75] (yLegStart - adb)
|
||||
curl [mix xLegStart xLegEnd 0.75] (yLegEnd + ada)
|
||||
corner xLegEnd yLegEnd
|
||||
corner (xLegEnd + DecoSizeX) (yLegEnd + DecoSizeY)
|
||||
|
||||
create-glyph "frak/Z" 0x2128 : glyph-proc
|
||||
include : MarkSet.capital
|
||||
|
||||
# Top Stroke
|
||||
include : HWave S
|
||||
l -- [S.xl SB]
|
||||
r -- [S.xr RightSB]
|
||||
b -- [S.yt (CAP - WaveDepth)]
|
||||
t -- [S.yt CAP]
|
||||
|
||||
# Bottom Stroke
|
||||
include : fraktur-stroke S
|
||||
corner [S.xr RightSB] [S.yt CAP]
|
||||
cg2.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]
|
|
@ -279,9 +279,9 @@ define-macro glyph-block : syntax-rules
|
|||
AdviceGlottalStopArchDepth StrokeWidthBlend ArchDepthAOf ArchDepthBOf SmoothAdjust
|
||||
MidJutSide MidJutCenter compositeBaseAnchors YSmoothMidR YSmoothMidL HSwToV
|
||||
NarrowUnicodeT WideUnicodeT VERY-FAR TINY]
|
||||
define spiroFnImports `[g4 g2 corner flat curl close end straight widths
|
||||
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]
|
||||
quadControls archv arcvh dispiro spiro-outline spiro-collect]
|
||||
define booleFnImports `[union intersection difference]
|
||||
|
||||
dirty `[$GlyphBlocks$.push : lambda [$Capture_Ext$] : begin \\
|
||||
|
|
|
@ -89,13 +89,23 @@ export function SetupBuilders(bindings) {
|
|||
const curl = KnotType("right");
|
||||
const close = f => new TerminateInstruction("close", f);
|
||||
const end = f => new TerminateInstruction("end", f);
|
||||
|
||||
const straight = { l: flat, r: curl };
|
||||
const g2c = { l: g2, r: corner };
|
||||
const cg2 = { l: corner, r: g2 };
|
||||
const flatc = { l: flat, r: corner };
|
||||
const ccurl = { l: corner, r: curl };
|
||||
|
||||
{
|
||||
let directions = [
|
||||
{ name: "up", x: 0, y: 1 },
|
||||
{ name: "down", x: 0, y: -1 },
|
||||
{ name: "left", x: -1, y: 0 },
|
||||
{ name: "right", x: 1, y: 0 },
|
||||
{ name: "ru", x: 1, y: 1 },
|
||||
{ name: "rd", x: 1, y: -1 },
|
||||
{ name: "lu", x: -1, y: 1 },
|
||||
{ name: "ld", x: -1, y: -1 },
|
||||
];
|
||||
let adhesions = [
|
||||
{ name: "start", l: 0, r: TINY },
|
||||
|
@ -107,6 +117,10 @@ export function SetupBuilders(bindings) {
|
|||
[g2, g2, g2],
|
||||
[corner, corner, corner],
|
||||
[straight, flat, curl],
|
||||
[g2c, g2, corner],
|
||||
[cg2, corner, g2],
|
||||
[flatc, flat, corner],
|
||||
[ccurl, corner, curl],
|
||||
];
|
||||
for (const [sink, kl, kr] of knotTypes) {
|
||||
for (const d of directions) {
|
||||
|
@ -368,6 +382,10 @@ export function SetupBuilders(bindings) {
|
|||
function spiroOutline(...args) {
|
||||
return new SpiroOutlineImpl(bindings, args);
|
||||
}
|
||||
function spiroCollect(glyph, ...args) {
|
||||
const spb = new SpiroImplBase(bindings, args);
|
||||
return spb.createCollector(glyph);
|
||||
}
|
||||
|
||||
return {
|
||||
g4,
|
||||
|
@ -378,6 +396,10 @@ export function SetupBuilders(bindings) {
|
|||
close,
|
||||
end,
|
||||
straight,
|
||||
g2c,
|
||||
cg2,
|
||||
flatc,
|
||||
ccurl,
|
||||
widths,
|
||||
heading,
|
||||
"disable-contrast": disableContrast,
|
||||
|
@ -391,5 +413,6 @@ export function SetupBuilders(bindings) {
|
|||
arcvh,
|
||||
dispiro,
|
||||
"spiro-outline": spiroOutline,
|
||||
"spiro-collect": spiroCollect,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import zlib from "zlib";
|
|||
import * as CurveUtil from "@iosevka/geometry/curve-util";
|
||||
import { encode, decode } from "@msgpack/msgpack";
|
||||
|
||||
const Edition = 43;
|
||||
const Edition = 44;
|
||||
const MAX_AGE = 16;
|
||||
class GfEntry {
|
||||
constructor(age, value) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import * as CurveUtil from "./curve-util.mjs";
|
|||
import { Point } from "./point.mjs";
|
||||
import { QuadifySink } from "./quadify.mjs";
|
||||
import { SpiroExpander } from "./spiro-expand.mjs";
|
||||
import { createSpiroPenGeometry } from "./spiro-pen-expander.mjs";
|
||||
import { spiroToOutlineWithSimplification } from "./spiro-to-outline.mjs";
|
||||
import { strokeArcs } from "./stroke.mjs";
|
||||
import { Transform } from "./transform.mjs";
|
||||
|
@ -139,6 +140,83 @@ export class SpiroGeometry extends CachedGeometry {
|
|||
}
|
||||
}
|
||||
|
||||
export class SpiroPenGeometry extends CachedGeometry {
|
||||
constructor(gizmo, closed, pen, knots) {
|
||||
super();
|
||||
this.m_gizmo = gizmo;
|
||||
this.m_closed = closed;
|
||||
this.m_knots = knots;
|
||||
this.m_pen = pen;
|
||||
}
|
||||
|
||||
toContoursImpl() {
|
||||
let contours = createSpiroPenGeometry(
|
||||
this.m_gizmo,
|
||||
this.m_closed,
|
||||
this.m_knots,
|
||||
this.m_pen,
|
||||
);
|
||||
|
||||
if (!contours.length) return [];
|
||||
|
||||
let stack = [];
|
||||
for (const [i, c] of contours.entries()) {
|
||||
stack.push({
|
||||
type: "operand",
|
||||
fillType: TypoGeom.Boolean.PolyFillType.pftNonZero,
|
||||
shape: CurveUtil.convertShapeToArcs([c]),
|
||||
});
|
||||
if (i > 0) {
|
||||
stack.push({ type: "operator", operator: TypoGeom.Boolean.ClipType.ctUnion });
|
||||
}
|
||||
}
|
||||
|
||||
const arcs = TypoGeom.Boolean.combineStack(stack, CurveUtil.BOOLE_RESOLUTION);
|
||||
const ctx = new CurveUtil.BezToContoursSink();
|
||||
TypoGeom.ShapeConv.transferBezArcShape(arcs, ctx);
|
||||
return ctx.contours;
|
||||
}
|
||||
|
||||
toReferences() {
|
||||
return null;
|
||||
}
|
||||
getDependencies() {
|
||||
return null;
|
||||
}
|
||||
filterTag(fn) {
|
||||
return this;
|
||||
}
|
||||
|
||||
measureComplexity() {
|
||||
let cplx = CPLX_NON_EMPTY | CPLX_NON_SIMPLE;
|
||||
for (const z of this.m_pen) {
|
||||
if (!isFinite(z.x) || !isFinite(z.y)) cplx |= CPLX_BROKEN;
|
||||
}
|
||||
for (const z of this.m_knots) {
|
||||
if (!isFinite(z.x) || !isFinite(z.y)) cplx |= CPLX_BROKEN;
|
||||
}
|
||||
return cplx;
|
||||
}
|
||||
|
||||
hash(h) {
|
||||
h.beginStruct("SpiroPenGeometry");
|
||||
h.gizmo(this.m_gizmo);
|
||||
h.bool(this.m_closed);
|
||||
|
||||
// Serialize the pen
|
||||
h.beginArray(this.m_pen.length);
|
||||
for (const z of this.m_pen) h.point(z);
|
||||
h.endArray();
|
||||
|
||||
// Serialize the knots
|
||||
h.beginArray(this.m_knots.length);
|
||||
for (const knot of this.m_knots) h.embed(knot);
|
||||
h.endArray();
|
||||
|
||||
h.endStruct();
|
||||
}
|
||||
}
|
||||
|
||||
export class DiSpiroGeometry extends CachedGeometry {
|
||||
constructor(gizmo, contrast, closed, biKnots) {
|
||||
super();
|
||||
|
|
81
packages/geometry/src/spiro-pen-expander.mjs
Normal file
81
packages/geometry/src/spiro-pen-expander.mjs
Normal file
|
@ -0,0 +1,81 @@
|
|||
import * as SpiroJs from "spiro";
|
||||
import * as CurveUtil from "./curve-util.mjs";
|
||||
|
||||
import { Point } from "./point.mjs";
|
||||
|
||||
export function createSpiroPenGeometry(gizmo, closed, knots, pen) {
|
||||
const collector = new ArcCollector(gizmo, pen);
|
||||
SpiroJs.spiroToBezierOnContext(knots, closed, collector, CurveUtil.GEOMETRY_PRECISION);
|
||||
return collector.contoursCollected;
|
||||
}
|
||||
|
||||
class ArcCollector {
|
||||
constructor(gizmo, pen) {
|
||||
this.gizmo = gizmo;
|
||||
this.lastX = 0;
|
||||
this.lastY = 0;
|
||||
this.m_pen = pen;
|
||||
this.contoursCollected = [];
|
||||
}
|
||||
|
||||
beginShape() {}
|
||||
endShape() {}
|
||||
|
||||
moveTo(x, y) {
|
||||
const lastTf = this.gizmo.applyXY(x, y);
|
||||
this.lastX = lastTf.x;
|
||||
this.lastY = lastTf.y;
|
||||
this.addPenProfileAt(this.lastX, this.lastY);
|
||||
}
|
||||
|
||||
lineTo(x1, y1) {
|
||||
const z1 = this.gizmo.applyXY(x1, y1);
|
||||
for (let i = 0; i < this.m_pen.length; i++) {
|
||||
let penPrev = this.m_pen[i];
|
||||
let penNext = this.m_pen[(i + 1) % this.m_pen.length];
|
||||
|
||||
const l1 = new Point(Point.Type.Corner, this.lastX + penPrev.x, this.lastY + penPrev.y);
|
||||
const l2 = new Point(Point.Type.Corner, z1.x + penPrev.x, z1.y + penPrev.y);
|
||||
const r2 = new Point(Point.Type.Corner, z1.x + penNext.x, z1.y + penNext.y);
|
||||
const r1 = new Point(Point.Type.Corner, this.lastX + penNext.x, this.lastY + penNext.y);
|
||||
|
||||
this.contoursCollected.push([l1, l2, r2, r1]);
|
||||
}
|
||||
this.lastX = z1.x;
|
||||
this.lastY = z1.y;
|
||||
this.addPenProfileAt(this.lastX, this.lastY);
|
||||
}
|
||||
|
||||
cubicTo(x2, y2, x3, y3, x4, y4) {
|
||||
const z2 = this.gizmo.applyXY(x2, y2);
|
||||
const z3 = this.gizmo.applyXY(x3, y3);
|
||||
const z4 = this.gizmo.applyXY(x4, y4);
|
||||
for (let i = 0; i < this.m_pen.length; i++) {
|
||||
let penPrev = this.m_pen[i];
|
||||
let penNext = this.m_pen[(i + 1) % this.m_pen.length];
|
||||
|
||||
const l1 = new Point(Point.Type.Corner, this.lastX + penPrev.x, this.lastY + penPrev.y);
|
||||
const l2 = new Point(Point.Type.CubicStart, z2.x + penPrev.x, z2.y + penPrev.y);
|
||||
const l3 = new Point(Point.Type.CubicEnd, z3.x + penPrev.x, z3.y + penPrev.y);
|
||||
const l4 = new Point(Point.Type.Corner, z4.x + penPrev.x, z4.y + penPrev.y);
|
||||
const r4 = new Point(Point.Type.Corner, z4.x + penNext.x, z4.y + penNext.y);
|
||||
const r3 = new Point(Point.Type.CubicStart, z3.x + penNext.x, z3.y + penNext.y);
|
||||
const r2 = new Point(Point.Type.CubicEnd, z2.x + penNext.x, z2.y + penNext.y);
|
||||
const r1 = new Point(Point.Type.Corner, this.lastX + penNext.x, this.lastY + penNext.y);
|
||||
|
||||
this.contoursCollected.push([l1, l2, l3, l4, r4, r3, r2, r1]);
|
||||
}
|
||||
this.lastX = z4.x;
|
||||
this.lastY = z4.y;
|
||||
this.addPenProfileAt(this.lastX, this.lastY);
|
||||
}
|
||||
|
||||
addPenProfileAt(x, y) {
|
||||
let c = [];
|
||||
for (let i = 0; i < this.m_pen.length; i++) {
|
||||
let pen = this.m_pen[i];
|
||||
c.push(new Point(Point.Type.Corner, x + pen.x, y + pen.y));
|
||||
}
|
||||
this.contoursCollected.push(c);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@ import * as SpiroJs from "spiro";
|
|||
import * as TypoGeom from "typo-geom";
|
||||
|
||||
import * as CurveUtil from "./curve-util.mjs";
|
||||
import { Vec2 } from "./point.mjs";
|
||||
|
||||
export function spiroToOutline(knots, fClosed, gizmo) {
|
||||
const s = new CurveUtil.BezToContoursSink(gizmo);
|
||||
|
|
|
@ -13,6 +13,7 @@ const TAG_END_STRUCT_TYPE = 0x12340008;
|
|||
const TAG_TYPED_POINT = 0x12340010;
|
||||
const TAG_GIZMO = 0x12340011;
|
||||
const TAG_LIST_LENGTH = 0x12340012;
|
||||
const POINT = 0x12340013;
|
||||
|
||||
const TAG_EMBED_BEGIN = 0x12340020;
|
||||
const TAG_EMBED_END = 0x12340021;
|
||||
|
@ -88,6 +89,12 @@ export class Hasher {
|
|||
return this;
|
||||
}
|
||||
|
||||
point(z) {
|
||||
this.u32(TAG_TYPED_POINT);
|
||||
this.f64(z.x);
|
||||
this.f64(z.y);
|
||||
return this;
|
||||
}
|
||||
typedPoint(z) {
|
||||
this.u32(TAG_TYPED_POINT);
|
||||
this.u32(z.type);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue