Iosevka/packages/font/src/finalize/glyphs.mjs
2024-03-03 23:41:27 -08:00

83 lines
2.5 KiB
JavaScript

import * as Geom from "@iosevka/geometry";
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);
return glyphStore;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
function regulateGlyphStore(cache, skew, glyphStore) {
const compositeMemo = new Map();
for (const g of glyphStore.glyphs()) {
if (!(g.geometry.measureComplexity() & Geom.CPLX_NON_EMPTY)) continue;
if (!regulateCompositeGlyph(glyphStore, compositeMemo, g)) {
g.geometry = g.geometry.unlinkReferences();
}
}
for (const g of glyphStore.glyphs()) {
if (!compositeMemo.get(g)) flattenSimpleGlyph(cache, skew, g);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
function regulateCompositeGlyph(glyphStore, memo, g) {
if (memo.has(g)) return memo.get(g);
let refs = g.geometry.toReferences();
if (!refs) return memoSet(memo, g, false);
for (const sr of refs) {
const gn = glyphStore.queryNameOf(sr.glyph);
if (!gn) return memoSet(memo, g, false);
}
let refGeometries = [];
for (const sr of refs) refGeometries.push(new Geom.ReferenceGeometry(sr.glyph, sr.x, sr.y));
g.geometry = new Geom.CombineGeometry(refGeometries);
return memoSet(memo, g, true);
}
function memoSet(memo, g, v) {
memo.set(g, v);
return v;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
function flattenSimpleGlyph(cache, skew, g) {
const ck = Geom.hashGeometry(g.geometry);
const cached = cache.getGF(ck);
if (ck && cached) {
g.clearGeometry();
g.includeContours(cached);
cache.refreshGF(ck);
} else {
try {
let gSimplified;
if (skew) {
const tfBack = g.gizmo ? g.gizmo.inverse() : new Transform(1, -skew, 0, 1, 0, 0);
const tfForward = g.gizmo ? g.gizmo : new Transform(1, +skew, 0, 1, 0, 0);
gSimplified = new Geom.TransformedGeometry(
new Geom.SimplifyGeometry(new Geom.TransformedGeometry(g.geometry, tfBack)),
tfForward
);
} else {
gSimplified = new Geom.SimplifyGeometry(g.geometry);
}
const cs = gSimplified.toContours();
g.clearGeometry();
g.includeContours(cs);
if (ck) cache.saveGF(ck, cs);
} catch (e) {
console.error("Detected broken geometry when processing", g._m_identifier);
throw e;
}
}
}