Skip to content

Commit 3bd1bfa

Browse files
authored
share connected arc impl (be5invis#2403)
1 parent 016c549 commit 3bd1bfa

File tree

9 files changed

+877
-742
lines changed

9 files changed

+877
-742
lines changed

.prettierrc.yaml

+7
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,10 @@ printWidth: 100
33
useTabs: true
44
tabWidth: 4
55
arrowParens: avoid
6+
7+
overrides:
8+
- files: "package.json"
9+
options:
10+
parser: json
11+
tabWidth: 2
12+
useTabs: false

package-lock.json

+845-674
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/font-glyphs/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
"@iosevka/geometry-cache": "30.3.1",
1414
"@iosevka/glyph": "30.3.1",
1515
"@iosevka/util": "30.3.1",
16-
"typo-geom": "^0.15.1"
16+
"typo-geom": "^0.16.1"
1717
}
1818
}

packages/font/src/cleanup/glyphs.mjs

+7-4
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@ import { Transform } from "@iosevka/geometry/transform";
55

66
export function finalizeGlyphs(cache, para, glyphStore) {
77
const skew = Math.tan(((para.slopeAngle || 0) / 180) * Math.PI);
8-
regulateGlyphStore(cache, skew, glyphStore);
8+
regulateGlyphStore(cache, para, skew, glyphStore);
99
return glyphStore;
1010
}
1111

1212
///////////////////////////////////////////////////////////////////////////////////////////////////
1313

14-
function regulateGlyphStore(cache, skew, glyphStore) {
14+
function regulateGlyphStore(cache, para, skew, glyphStore) {
1515
for (const g of glyphStore.glyphs()) {
1616
if (!(g.geometry.measureComplexity() & Geom.CPLX_NON_EMPTY)) continue;
17-
if (!g.geometry.toReferences()) flattenSimpleGlyph(cache, skew, g);
17+
if (!g.geometry.toReferences()) flattenSimpleGlyph(cache, para, skew, g);
1818
}
1919
}
2020

21-
function flattenSimpleGlyph(cache, skew, g) {
21+
function flattenSimpleGlyph(cache, para, skew, g) {
2222
try {
2323
let gSimplified;
2424
const needsTransform = g.gizmo ? !Transform.isTranslate(g.gizmo) : skew != 0;
@@ -38,6 +38,9 @@ function flattenSimpleGlyph(cache, skew, g) {
3838
g.includeContours(cs);
3939
} catch (e) {
4040
console.error("Detected broken geometry when processing", g._m_identifier);
41+
console.error(
42+
`${para.naming.family} ${para.naming.weight} ${para.naming.width} ${para.naming.slope}`,
43+
);
4144
g.clearGeometry();
4245
}
4346
}

packages/geometry-cache/src/index.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import zlib from "zlib";
55
import * as CurveUtil from "@iosevka/geometry/curve-util";
66
import { encode, decode } from "@msgpack/msgpack";
77

8-
const Edition = 42;
8+
const Edition = 43;
99
const MAX_AGE = 16;
1010
class GfEntry {
1111
constructor(age, value) {

packages/geometry/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515
"dependencies": {
1616
"@iosevka/util": "30.3.1",
1717
"spiro": "^3.0.1",
18-
"typo-geom": "^0.15.1"
18+
"typo-geom": "^0.16.1"
1919
}
2020
}

packages/geometry/src/curve-util.mjs

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ function convertContourToArcs(contour) {
4141
const zc = z;
4242
let zf = contour[(j + 1) % contour.length];
4343
const zfIsCorner = zf.type === Point.Type.contour;
44-
if (!zfIsCorner) zf = Point.from(Point.Type.Corner, zc).mix(0.5, zf);
44+
if (!zfIsCorner) zf = Point.from(Point.Type.Corner, zc).mix(zf, 0.5);
4545
newContour.push(
4646
new TypoGeom.Arcs.Bez3(
4747
z0,
48-
Point.from(Point.Type.CubicStart, z0).mix(2 / 3, zc),
49-
Point.from(Point.Type.CubicEnd, zf).mix(2 / 3, zc),
48+
Point.from(Point.Type.CubicStart, z0).mix(zc, 2 / 3),
49+
Point.from(Point.Type.CubicEnd, zf).mix(zc, 2 / 3),
5050
Point.from(Point.Type.Corner, zf),
5151
),
5252
);

packages/geometry/src/point.mjs

+2-6
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,8 @@ export class Point {
3333
addScale(scale, z2) {
3434
return new Point(this.type, this.x + scale * z2.x, this.y + scale * z2.y);
3535
}
36-
mix(scale, z2) {
37-
return new Point(
38-
this.type,
39-
this.x + scale * (z2.x - this.x),
40-
this.y + scale * (z2.y - this.y),
41-
);
36+
mix(z2, t) {
37+
return new Point(this.type, mix(this.x, z2.x, t), mix(this.y, z2.y, t));
4238
}
4339
scale(t) {
4440
return new Point(this.type, t * this.x, t * this.y);

packages/geometry/src/spiro-to-outline.mjs

+10-52
Original file line numberDiff line numberDiff line change
@@ -54,63 +54,21 @@ class SpiroSimplifier {
5454
if (arc.arcLength > 1e-6) this.combinedArcs.push(arc);
5555
} else {
5656
const combined = new SpiroSequenceArc(this.m_ongoingArcs);
57-
if (combined.totalLength > 1e-6) this.combinedArcs.push(combined);
57+
if (!combined.isEmpty() && combined.totalLength > 1e-6) {
58+
this.combinedArcs.push(combined);
59+
}
5860
}
5961
this.m_ongoingArcs = [];
6062
}
6163
}
6264

63-
class SpiroSequenceArc {
65+
const SpiroMeasurer = {
66+
measureLength(a) {
67+
return a.arcLength;
68+
},
69+
};
70+
class SpiroSequenceArc extends TypoGeom.Arcs.CombinedArc {
6471
constructor(segments) {
65-
// Filter out zero-length segments
66-
let rear = 0;
67-
for (let j = 0; j < segments.length; j++) {
68-
if (segments[j].arcLength > 1e-6) {
69-
segments[rear++] = segments[j];
70-
}
71-
}
72-
segments.length = rear;
73-
74-
// Compute total length and stops
75-
let totalLength = 0;
76-
let stops = [];
77-
for (let j = 0; j < segments.length; j++) {
78-
stops[j] = totalLength;
79-
totalLength += segments[j].arcLength;
80-
}
81-
for (let j = 0; j < segments.length; j++) {
82-
stops[j] = stops[j] / totalLength;
83-
}
84-
this.totalLength = totalLength;
85-
this.m_segments = segments;
86-
this.m_stops = stops;
87-
}
88-
89-
eval(t) {
90-
const j = segTSearch(this.m_stops, t);
91-
const tBefore = this.m_stops[j];
92-
const tNext = j < this.m_stops.length - 1 ? this.m_stops[j + 1] : 1;
93-
const tRelative = (t - tBefore) / (tNext - tBefore);
94-
return this.m_segments[j].eval(tRelative);
95-
}
96-
97-
derivative(t) {
98-
const j = segTSearch(this.m_stops, t);
99-
const tBefore = this.m_stops[j];
100-
const tNext = j < this.m_stops.length - 1 ? this.m_stops[j + 1] : 1;
101-
const tRelative = (t - tBefore) / (tNext - tBefore);
102-
return Vec2.scaleFrom(1 / (tNext - tBefore), this.m_segments[j].derivative(tRelative));
103-
}
104-
}
105-
106-
function segTSearch(stops, t) {
107-
if (t < 0) return 0;
108-
let l = 0,
109-
r = stops.length;
110-
while (l < r) {
111-
let m = (l + r) >>> 1;
112-
if (stops[m] > t) r = m;
113-
else l = m + 1;
72+
super(SpiroMeasurer, segments);
11473
}
115-
return r - 1;
11674
}

0 commit comments

Comments
 (0)