Skip to content

Commit

Permalink
share connected arc impl (#2403)
Browse files Browse the repository at this point in the history
  • Loading branch information
be5invis authored Jul 1, 2024
1 parent 016c549 commit 3bd1bfa
Show file tree
Hide file tree
Showing 9 changed files with 877 additions and 742 deletions.
7 changes: 7 additions & 0 deletions .prettierrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,10 @@ printWidth: 100
useTabs: true
tabWidth: 4
arrowParens: avoid

overrides:
- files: "package.json"
options:
parser: json
tabWidth: 2
useTabs: false
1,519 changes: 845 additions & 674 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/font-glyphs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
"@iosevka/geometry-cache": "30.3.1",
"@iosevka/glyph": "30.3.1",
"@iosevka/util": "30.3.1",
"typo-geom": "^0.15.1"
"typo-geom": "^0.16.1"
}
}
11 changes: 7 additions & 4 deletions packages/font/src/cleanup/glyphs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ import { Transform } from "@iosevka/geometry/transform";

export function finalizeGlyphs(cache, para, glyphStore) {
const skew = Math.tan(((para.slopeAngle || 0) / 180) * Math.PI);
regulateGlyphStore(cache, skew, glyphStore);
regulateGlyphStore(cache, para, skew, glyphStore);
return glyphStore;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

function regulateGlyphStore(cache, skew, glyphStore) {
function regulateGlyphStore(cache, para, skew, glyphStore) {
for (const g of glyphStore.glyphs()) {
if (!(g.geometry.measureComplexity() & Geom.CPLX_NON_EMPTY)) continue;
if (!g.geometry.toReferences()) flattenSimpleGlyph(cache, skew, g);
if (!g.geometry.toReferences()) flattenSimpleGlyph(cache, para, skew, g);
}
}

function flattenSimpleGlyph(cache, skew, g) {
function flattenSimpleGlyph(cache, para, skew, g) {
try {
let gSimplified;
const needsTransform = g.gizmo ? !Transform.isTranslate(g.gizmo) : skew != 0;
Expand All @@ -38,6 +38,9 @@ function flattenSimpleGlyph(cache, skew, g) {
g.includeContours(cs);
} catch (e) {
console.error("Detected broken geometry when processing", g._m_identifier);
console.error(
`${para.naming.family} ${para.naming.weight} ${para.naming.width} ${para.naming.slope}`,
);
g.clearGeometry();
}
}
2 changes: 1 addition & 1 deletion packages/geometry-cache/src/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import zlib from "zlib";
import * as CurveUtil from "@iosevka/geometry/curve-util";
import { encode, decode } from "@msgpack/msgpack";

const Edition = 42;
const Edition = 43;
const MAX_AGE = 16;
class GfEntry {
constructor(age, value) {
Expand Down
2 changes: 1 addition & 1 deletion packages/geometry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
"dependencies": {
"@iosevka/util": "30.3.1",
"spiro": "^3.0.1",
"typo-geom": "^0.15.1"
"typo-geom": "^0.16.1"
}
}
6 changes: 3 additions & 3 deletions packages/geometry/src/curve-util.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ function convertContourToArcs(contour) {
const zc = z;
let zf = contour[(j + 1) % contour.length];
const zfIsCorner = zf.type === Point.Type.contour;
if (!zfIsCorner) zf = Point.from(Point.Type.Corner, zc).mix(0.5, zf);
if (!zfIsCorner) zf = Point.from(Point.Type.Corner, zc).mix(zf, 0.5);
newContour.push(
new TypoGeom.Arcs.Bez3(
z0,
Point.from(Point.Type.CubicStart, z0).mix(2 / 3, zc),
Point.from(Point.Type.CubicEnd, zf).mix(2 / 3, zc),
Point.from(Point.Type.CubicStart, z0).mix(zc, 2 / 3),
Point.from(Point.Type.CubicEnd, zf).mix(zc, 2 / 3),
Point.from(Point.Type.Corner, zf),
),
);
Expand Down
8 changes: 2 additions & 6 deletions packages/geometry/src/point.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,8 @@ export class Point {
addScale(scale, z2) {
return new Point(this.type, this.x + scale * z2.x, this.y + scale * z2.y);
}
mix(scale, z2) {
return new Point(
this.type,
this.x + scale * (z2.x - this.x),
this.y + scale * (z2.y - this.y),
);
mix(z2, t) {
return new Point(this.type, mix(this.x, z2.x, t), mix(this.y, z2.y, t));
}
scale(t) {
return new Point(this.type, t * this.x, t * this.y);
Expand Down
62 changes: 10 additions & 52 deletions packages/geometry/src/spiro-to-outline.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,63 +54,21 @@ class SpiroSimplifier {
if (arc.arcLength > 1e-6) this.combinedArcs.push(arc);
} else {
const combined = new SpiroSequenceArc(this.m_ongoingArcs);
if (combined.totalLength > 1e-6) this.combinedArcs.push(combined);
if (!combined.isEmpty() && combined.totalLength > 1e-6) {
this.combinedArcs.push(combined);
}
}
this.m_ongoingArcs = [];
}
}

class SpiroSequenceArc {
const SpiroMeasurer = {
measureLength(a) {
return a.arcLength;
},
};
class SpiroSequenceArc extends TypoGeom.Arcs.CombinedArc {
constructor(segments) {
// Filter out zero-length segments
let rear = 0;
for (let j = 0; j < segments.length; j++) {
if (segments[j].arcLength > 1e-6) {
segments[rear++] = segments[j];
}
}
segments.length = rear;

// Compute total length and stops
let totalLength = 0;
let stops = [];
for (let j = 0; j < segments.length; j++) {
stops[j] = totalLength;
totalLength += segments[j].arcLength;
}
for (let j = 0; j < segments.length; j++) {
stops[j] = stops[j] / totalLength;
}
this.totalLength = totalLength;
this.m_segments = segments;
this.m_stops = stops;
}

eval(t) {
const j = segTSearch(this.m_stops, t);
const tBefore = this.m_stops[j];
const tNext = j < this.m_stops.length - 1 ? this.m_stops[j + 1] : 1;
const tRelative = (t - tBefore) / (tNext - tBefore);
return this.m_segments[j].eval(tRelative);
}

derivative(t) {
const j = segTSearch(this.m_stops, t);
const tBefore = this.m_stops[j];
const tNext = j < this.m_stops.length - 1 ? this.m_stops[j + 1] : 1;
const tRelative = (t - tBefore) / (tNext - tBefore);
return Vec2.scaleFrom(1 / (tNext - tBefore), this.m_segments[j].derivative(tRelative));
}
}

function segTSearch(stops, t) {
if (t < 0) return 0;
let l = 0,
r = stops.length;
while (l < r) {
let m = (l + r) >>> 1;
if (stops[m] > t) r = m;
else l = m + 1;
super(SpiroMeasurer, segments);
}
return r - 1;
}

0 comments on commit 3bd1bfa

Please sign in to comment.