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
|
useTabs: true
|
||||||
tabWidth: 4
|
tabWidth: 4
|
||||||
arrowParens: avoid
|
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/geometry-cache": "30.3.1",
|
||||||
"@iosevka/glyph": "30.3.1",
|
"@iosevka/glyph": "30.3.1",
|
||||||
"@iosevka/util": "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) {
|
export function finalizeGlyphs(cache, para, glyphStore) {
|
||||||
const skew = Math.tan(((para.slopeAngle || 0) / 180) * Math.PI);
|
const skew = Math.tan(((para.slopeAngle || 0) / 180) * Math.PI);
|
||||||
regulateGlyphStore(cache, skew, glyphStore);
|
regulateGlyphStore(cache, para, skew, glyphStore);
|
||||||
return glyphStore;
|
return glyphStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function regulateGlyphStore(cache, skew, glyphStore) {
|
function regulateGlyphStore(cache, para, skew, glyphStore) {
|
||||||
for (const g of glyphStore.glyphs()) {
|
for (const g of glyphStore.glyphs()) {
|
||||||
if (!(g.geometry.measureComplexity() & Geom.CPLX_NON_EMPTY)) continue;
|
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 {
|
try {
|
||||||
let gSimplified;
|
let gSimplified;
|
||||||
const needsTransform = g.gizmo ? !Transform.isTranslate(g.gizmo) : skew != 0;
|
const needsTransform = g.gizmo ? !Transform.isTranslate(g.gizmo) : skew != 0;
|
||||||
|
@ -38,6 +38,9 @@ function flattenSimpleGlyph(cache, skew, g) {
|
||||||
g.includeContours(cs);
|
g.includeContours(cs);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Detected broken geometry when processing", g._m_identifier);
|
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();
|
g.clearGeometry();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import zlib from "zlib";
|
||||||
import * as CurveUtil from "@iosevka/geometry/curve-util";
|
import * as CurveUtil from "@iosevka/geometry/curve-util";
|
||||||
import { encode, decode } from "@msgpack/msgpack";
|
import { encode, decode } from "@msgpack/msgpack";
|
||||||
|
|
||||||
const Edition = 42;
|
const Edition = 43;
|
||||||
const MAX_AGE = 16;
|
const MAX_AGE = 16;
|
||||||
class GfEntry {
|
class GfEntry {
|
||||||
constructor(age, value) {
|
constructor(age, value) {
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iosevka/util": "30.3.1",
|
"@iosevka/util": "30.3.1",
|
||||||
"spiro": "^3.0.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;
|
const zc = z;
|
||||||
let zf = contour[(j + 1) % contour.length];
|
let zf = contour[(j + 1) % contour.length];
|
||||||
const zfIsCorner = zf.type === Point.Type.contour;
|
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(
|
newContour.push(
|
||||||
new TypoGeom.Arcs.Bez3(
|
new TypoGeom.Arcs.Bez3(
|
||||||
z0,
|
z0,
|
||||||
Point.from(Point.Type.CubicStart, z0).mix(2 / 3, zc),
|
Point.from(Point.Type.CubicStart, z0).mix(zc, 2 / 3),
|
||||||
Point.from(Point.Type.CubicEnd, zf).mix(2 / 3, zc),
|
Point.from(Point.Type.CubicEnd, zf).mix(zc, 2 / 3),
|
||||||
Point.from(Point.Type.Corner, zf),
|
Point.from(Point.Type.Corner, zf),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -33,12 +33,8 @@ export class Point {
|
||||||
addScale(scale, z2) {
|
addScale(scale, z2) {
|
||||||
return new Point(this.type, this.x + scale * z2.x, this.y + scale * z2.y);
|
return new Point(this.type, this.x + scale * z2.x, this.y + scale * z2.y);
|
||||||
}
|
}
|
||||||
mix(scale, z2) {
|
mix(z2, t) {
|
||||||
return new Point(
|
return new Point(this.type, mix(this.x, z2.x, t), mix(this.y, z2.y, t));
|
||||||
this.type,
|
|
||||||
this.x + scale * (z2.x - this.x),
|
|
||||||
this.y + scale * (z2.y - this.y),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
scale(t) {
|
scale(t) {
|
||||||
return new Point(this.type, t * this.x, t * this.y);
|
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);
|
if (arc.arcLength > 1e-6) this.combinedArcs.push(arc);
|
||||||
} else {
|
} else {
|
||||||
const combined = new SpiroSequenceArc(this.m_ongoingArcs);
|
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 = [];
|
this.m_ongoingArcs = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpiroSequenceArc {
|
const SpiroMeasurer = {
|
||||||
|
measureLength(a) {
|
||||||
|
return a.arcLength;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
class SpiroSequenceArc extends TypoGeom.Arcs.CombinedArc {
|
||||||
constructor(segments) {
|
constructor(segments) {
|
||||||
// Filter out zero-length segments
|
super(SpiroMeasurer, 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;
|
|
||||||
}
|
|
||||||
return r - 1;
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue