share connected arc impl (#2403)
This commit is contained in:
parent
016c5494f9
commit
3bd1bfa454
9 changed files with 866 additions and 731 deletions
|
@ -3,3 +3,10 @@ printWidth: 100
|
|||
useTabs: true
|
||||
tabWidth: 4
|
||||
arrowParens: avoid
|
||||
|
||||
overrides:
|
||||
- files: "package.json"
|
||||
options:
|
||||
parser: json
|
||||
tabWidth: 2
|
||||
useTabs: false
|
||||
|
|
1497
package-lock.json
generated
1497
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -15,6 +15,6 @@
|
|||
"dependencies": {
|
||||
"@iosevka/util": "30.3.1",
|
||||
"spiro": "^3.0.1",
|
||||
"typo-geom": "^0.15.1"
|
||||
"typo-geom": "^0.16.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
super(SpiroMeasurer, segments);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return r - 1;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue