Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new spiro control flattener, and a new mechanism to propagate coordinates through the spiro construction #2442

Merged
merged 1 commit into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/font-glyphs/src/letter-like/fraktur.ptl
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,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 [S.xl SB] ([S.yb 0] + ArchDepthB)
curl same-x ([S.yb 0] + ArchDepthB)
~~~ [hookend [S.yb 0] (sw -- S.thick)]
g2 [S.xr RightSB] ([S.yb 0] + SHook)

Expand All @@ -151,7 +151,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 [F.connL S xCenter] (CAP * 0.375)
g2.ld.end same-x (CAP * 0.375)

create-glyph "frak/H" 0x210C : glyph-proc
include : MarkSet.capDesc
Expand Down
3 changes: 1 addition & 2 deletions packages/font-glyphs/src/number/0.ptl
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ glyph-block Digits-Zero : begin
local pcy 0.1

return : sink
g2.right.mid.post mxb (b + O) [widths.lhs fine]
g2.right.mid mxb (b + O) [widths.lhs fine]
alsoThru.g2 pcx pcy
flat [mix mxb r p1] [mix b myr p2] [widths.lhs sw1]
curl [mix mxb r (1 - p4)] [mix b myr (1 - p3)] [widths.lhs sw2]
Expand All @@ -65,7 +65,6 @@ glyph-block Digits-Zero : begin
flat [mix l mxb p4] [mix myl b p3] [widths.lhs sw2]
curl [mix l mxb (1 - p1)] [mix myl b (1 - p2)] [widths.lhs sw1]
alsoThru.g2 (1 - pcx) (1 - pcy)
g2.right.mid.pre mxb (b + O) [widths.lhs fine]
close

define [ZeroShapeBase shapeT] : namespace
Expand Down
113 changes: 81 additions & 32 deletions packages/font-kits/src/spiro-kit.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { DiSpiroGeometry, SpiroGeometry } from "@iosevka/geometry";
import {
BiKnotCollector,
DEP_POST_X,
DEP_POST_Y,
DEP_PRE_X,
DEP_PRE_Y,
DerivedCoordinateBase,
Interpolator,
SpiroFlattener,
TerminateInstruction,
UserCloseKnotPair,
UserControlKnot,
} from "@iosevka/geometry/spiro-control";
import { bez3, fallback, mix } from "@iosevka/util";
Expand All @@ -18,9 +25,12 @@ class SpiroImplBase {
createCollector(glyph) {
const gizmo = glyph.gizmo || this.bindings.GlobalTransform;

const flattener = new SpiroFlattener();
for (const control of this.args) flattener.add(control);
flattener.flatten();

const collector = new BiKnotCollector(this.bindings.Contrast);
for (const control of this.args) collector.add(control);
collector.unwrap();
flattener.pipe(collector);

return { gizmo, collector };
}
Expand Down Expand Up @@ -73,31 +83,64 @@ class DiSpiroProxy {
}
}

/// The builder for directed knot pairs
function KnotType(type) {
return (x, y, f) => {
if (!UserControlKnot.isCoordinateValid(x)) throw new TypeError("NaN detected for X");
if (!UserControlKnot.isCoordinateValid(y)) throw new TypeError("NaN detected for Y");
return new UserControlKnot(type, x, y, f);
};
}

/// The builder for directed knot pairs
class DirectedKnotPairBuilder {
constructor(bindings, prevKnotType, nextKnotType, deltaX, deltaY) {
constructor(bindings, kPre, kCenter, kPost, deltaX, deltaY) {
const { TINY } = bindings;
this.start = DirPairImpl(prevKnotType, nextKnotType, deltaX, deltaY, 0, TINY);
this.mid = DirPairImpl(prevKnotType, nextKnotType, deltaX, deltaY, -0.5 * TINY, 0.5 * TINY);
this.end = DirPairImpl(prevKnotType, nextKnotType, deltaX, deltaY, -TINY, 0);
this.start = DirPairImpl(kPre, kCenter, kPost, deltaX, deltaY, 0, TINY);
this.mid = DirPairImpl(kPre, kCenter, kPost, deltaX, deltaY, -0.5 * TINY, 0.5 * TINY);
this.end = DirPairImpl(kPre, kCenter, kPost, deltaX, deltaY, -TINY, 0);
}
}

function DirPairImpl(prevKnotType, nextKnotType, dirX, dirY, distPre, distPost) {
const fnPre = (x, y, af) => prevKnotType(x + dirX * distPre, y + dirY * distPre, af);
const fnPost = (x, y, af) => nextKnotType(x + dirX * distPost, y + dirY * distPost, af);
let buildFn = (x, y, af) => [fnPre(x, y, af), fnPost(x, y, af)];
buildFn.pre = fnPre;
buildFn.post = fnPost;
return buildFn;
function DirPairImpl(kPre, kCenter, kPost, dirX, dirY, dPre, dPost) {
let tyPre = kPre(0, 0).type;
let tyPost = kPost(0, 0).type;
return (x, y, af) =>
new UserCloseKnotPair(kCenter(x, y, af), tyPre, tyPost, dirX, dirY, dPre, dPost);
}

function KnotType(type) {
return (x, y, f) => {
if (!isFinite(x)) throw new TypeError("NaN detected for X");
if (!isFinite(y)) throw new TypeError("NaN detected for Y");
return new UserControlKnot(type, x, y, f);
};
/// 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) {
Expand All @@ -121,15 +164,16 @@ export function SetupBuilders(bindings) {

// Add the directed/heading knot builders
{
// prettier-ignore
let knotTypes = [
[g4, g4, g4],
[g2, g2, g2],
[corner, corner, corner],
[straight, flat, curl],
[g2c, g2, corner],
[cg2, corner, g2],
[flatc, flat, corner],
[ccurl, corner, curl],
[ g4, g4, g4, g4 ],
[ g2, g2, g2, g2 ],
[ corner, corner, corner, corner ],
[ straight, flat, g2, curl ],
[ g2c, g2, corner, corner ],
[ cg2, corner, corner, g2 ],
[ flatc, flat, corner, corner ],
[ ccurl, corner, corner, curl ],
];
let directions = [
// Straights
Expand All @@ -148,12 +192,12 @@ export function SetupBuilders(bindings) {
{ name: "lu", x: -1, y: 1 },
{ name: "ld", x: -1, y: -1 },
];
for (const [sink, kl, kr] of knotTypes) {
sink.sl = s => new DirectedKnotPairBuilder(bindings, kl, kr, -1, s);
sink.sr = s => new DirectedKnotPairBuilder(bindings, kl, kr, 1, s);
sink.dir = (dx, dy) => new DirectedKnotPairBuilder(bindings, kl, kr, dx, dy);
for (const [sink, kl, kc, kr] of knotTypes) {
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) {
sink[d.name] = new DirectedKnotPairBuilder(bindings, kl, kr, d.x, d.y);
sink[d.name] = new DirectedKnotPairBuilder(bindings, kl, kc, kr, d.x, d.y);
}
}
}
Expand Down Expand Up @@ -435,5 +479,10 @@ 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(),
};
}
Loading
Loading