Cache the entire geometry flattener

This commit is contained in:
be5invis 2021-04-17 15:42:07 -07:00
parent edbe5ff47e
commit 4c8de9f025
4 changed files with 95 additions and 56 deletions

View file

@ -3,39 +3,38 @@
const fs = require("fs-extra"); const fs = require("fs-extra");
const zlib = require("zlib"); const zlib = require("zlib");
const EDITION = "Iosevka.PTCache.1";
class Cache { class Cache {
constructor() { constructor() {
this.ptSource = {}; this.gfSource = {};
this.ptSink = {}; this.gfSink = {};
} }
loadRep(rep) { loadRep(version, rep) {
if (!rep || rep.edition !== EDITION) return; if (!rep || rep.version !== version) return;
this.ptSource = rep.pt; this.gfSource = rep.gf || {};
} }
toRep() { toRep(version) {
return { edition: EDITION, pt: this.ptSink }; return { version, gf: this.gfSink };
} }
// Path conversion cache
getPT(ck) { // Geometry flattening conversion cache
return this.ptSource[ck]; getGF(ck) {
return this.gfSource[ck];
} }
savePT(ck, val) { saveGF(ck, val) {
this.ptSink[ck] = val; this.gfSink[ck] = val;
} }
} }
exports.load = async function (argv) { exports.load = async function (argv) {
let cache = new Cache(); let cache = new Cache();
if (argv.oCache && fs.existsSync(argv.oCache)) { if (argv.oCache && fs.existsSync(argv.oCache)) {
cache.loadRep(unzip(await fs.readFile(argv.oCache))); cache.loadRep(argv.menu.version, unzip(await fs.readFile(argv.oCache)));
} }
return cache; return cache;
}; };
exports.save = async function savePTCache(argv, cache) { exports.save = async function savePTCache(argv, cache) {
if (argv.oCache) await fs.writeFile(argv.oCache, zip(cache.toRep())); if (argv.oCache) await fs.writeFile(argv.oCache, zip(cache.toRep(argv.menu.version)));
}; };
function unzip(buf) { function unzip(buf) {

View file

@ -1,6 +1,7 @@
"use strict"; "use strict";
const TypoGeom = require("typo-geom"); const TypoGeom = require("typo-geom");
const Geom = require("../../support/geometry");
const Point = require("../../support/point"); const Point = require("../../support/point");
const CurveUtil = require("../../support/curve-util"); const CurveUtil = require("../../support/curve-util");
const util = require("util"); const util = require("util");
@ -17,14 +18,7 @@ function finalizeGlyphs(cache, para, glyphStore) {
function regulateGlyphStore(cache, skew, glyphStore) { function regulateGlyphStore(cache, skew, glyphStore) {
for (const g of glyphStore.glyphs()) { for (const g of glyphStore.glyphs()) {
if (g.geometry.isEmpty()) continue; if (g.geometry.isEmpty()) continue;
if (!regulateCompositeGlyph(glyphStore, g)) { if (!regulateCompositeGlyph(glyphStore, g)) flattenSimpleGlyph(cache, skew, g);
const cs = g.geometry.asContours();
g.clearGeometry();
g.includeContours(cs, 0, 0);
}
}
for (const g of glyphStore.glyphs()) {
if (!g.geometry.asReferences()) regulateSimpleGlyph(cache, skew, g);
} }
} }
@ -40,26 +34,28 @@ function regulateCompositeGlyph(glyphStore, g) {
return true; return true;
} }
function regulateSimpleGlyph(cache, skew, g) { function flattenSimpleGlyph(cache, skew, g) {
const ck = Geom.hashGeometry(g.geometry);
const cached = cache.getGF(ck);
if (ck && cached) {
g.clearGeometry();
g.includeContours(CurveUtil.repToShape(cached), 0, 0);
cache.saveGF(ck, cached);
} else {
let cs = g.geometry.asContours(); let cs = g.geometry.asContours();
for (const contour of cs) for (const z of contour) z.x -= z.y * skew; for (const contour of cs) for (const z of contour) z.x -= z.y * skew;
cs = simplifyContours(cache, cs); cs = simplifyContours(cs);
for (const contour of cs) for (const z of contour) z.x += z.y * skew; for (const contour of cs) for (const z of contour) z.x += z.y * skew;
g.clearGeometry(); g.clearGeometry();
g.includeContours(cs, 0, 0); g.includeContours(cs, 0, 0);
if (ck) cache.saveGF(ck, CurveUtil.shapeToRep(cs));
}
} }
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
function simplifyContours(cache, source) { function simplifyContours(source) {
const ck = CurveUtil.hashShape(source);
const cached = cache.getPT(ck);
if (cached) {
cache.savePT(ck, cached);
return CurveUtil.repToShape(cached);
}
const sink = new FairizedShapeSink(); const sink = new FairizedShapeSink();
TypoGeom.ShapeConv.transferGenericShape( TypoGeom.ShapeConv.transferGenericShape(
TypoGeom.Fairize.fairizeBezierShape( TypoGeom.Fairize.fairizeBezierShape(
@ -72,8 +68,6 @@ function simplifyContours(cache, source) {
sink, sink,
CurveUtil.GEOMETRY_PRECISION CurveUtil.GEOMETRY_PRECISION
); );
cache.savePT(ck, CurveUtil.shapeToRep(sink.contours));
return sink.contours; return sink.contours;
} }

View file

@ -1,7 +1,5 @@
"use strict"; "use strict";
const crypto = require("crypto");
const TypoGeom = require("typo-geom"); const TypoGeom = require("typo-geom");
const Point = require("./point"); const Point = require("./point");
const Transform = require("./transform"); const Transform = require("./transform");
@ -53,18 +51,6 @@ exports.convertShapeToArcs = function convertShapeToArcs(shape) {
return shape.map(convertContourToArcs); return shape.map(convertContourToArcs);
}; };
exports.hashShape = function (shape) {
let s = "";
for (const c of shape) {
s += "[";
for (const z of c) {
s += `[${z.type};${Math.round(z.x * 0x10000)};${Math.round(z.y * 0x10000)}]`;
}
s += "]";
}
return crypto.createHash("sha256").update(s).digest();
};
function contourToRep(contour) { function contourToRep(contour) {
let c = []; let c = [];
for (const z of contour) c.push({ type: z.type, x: z.x, y: z.y }); for (const z of contour) c.push({ type: z.type, x: z.x, y: z.y });

View file

@ -1,9 +1,11 @@
"use strict"; "use strict";
const crypto = require("crypto");
const TypoGeom = require("typo-geom");
const Point = require("./point"); const Point = require("./point");
const Transform = require("./transform"); const Transform = require("./transform");
const CurveUtil = require("./curve-util"); const CurveUtil = require("./curve-util");
const TypoGeom = require("typo-geom");
class GeometryBase { class GeometryBase {
asContours() { asContours() {
@ -21,6 +23,9 @@ class GeometryBase {
measureComplexity() { measureComplexity() {
return 0; return 0;
} }
toShapeStringOrNull() {
return null;
}
} }
class ContourGeometry extends GeometryBase { class ContourGeometry extends GeometryBase {
@ -47,6 +52,14 @@ class ContourGeometry extends GeometryBase {
measureComplexity() { measureComplexity() {
return this.m_points.length; return this.m_points.length;
} }
toShapeStringOrNull() {
let s = "ContourGeometry{";
for (const z of this.m_points) {
s += `(${z.type};${formatN(z.x)};${formatN(z.y)})`;
}
s += "}";
return s;
}
} }
class ReferenceGeometry extends GeometryBase { class ReferenceGeometry extends GeometryBase {
@ -82,6 +95,11 @@ class ReferenceGeometry extends GeometryBase {
measureComplexity() { measureComplexity() {
return this.m_glyph.geometry.measureComplexity(); return this.m_glyph.geometry.measureComplexity();
} }
toShapeStringOrNull() {
let sTarget = this.m_glyph.geometry.toShapeStringOrNull();
if (!sTarget) return null;
return `ReferenceGeometry{${sTarget};${formatN(this.m_x)};${formatN(this.m_y)}}`;
}
} }
class TaggedGeometry extends GeometryBase { class TaggedGeometry extends GeometryBase {
@ -106,6 +124,9 @@ class TaggedGeometry extends GeometryBase {
measureComplexity() { measureComplexity() {
return this.m_geom.measureComplexity(); return this.m_geom.measureComplexity();
} }
toShapeStringOrNull() {
return this.m_geom.toShapeStringOrNull();
}
} }
class TransformedGeometry extends GeometryBase { class TransformedGeometry extends GeometryBase {
@ -144,6 +165,17 @@ class TransformedGeometry extends GeometryBase {
measureComplexity() { measureComplexity() {
return this.m_geom.measureComplexity(); return this.m_geom.measureComplexity();
} }
toShapeStringOrNull() {
const sTarget = this.m_geom.toShapeStringOrNull();
if (!sTarget) return null;
return (
`TransformedGeometry{${sTarget};` +
`${formatN(this.m_transform.xx)},${formatN(this.m_transform.xy)},` +
`${formatN(this.m_transform.yx)},${formatN(this.m_transform.yy)},` +
`${formatN(this.m_transform.x)},${formatN(this.m_transform.y)}` +
`}`
);
}
} }
class CombineGeometry extends GeometryBase { class CombineGeometry extends GeometryBase {
@ -194,6 +226,15 @@ class CombineGeometry extends GeometryBase {
let s = 0; let s = 0;
for (const part of this.m_parts) s += part.measureComplexity(); for (const part of this.m_parts) s += part.measureComplexity();
} }
toShapeStringOrNull() {
let sParts = [];
for (const item of this.m_parts) {
const sPart = item.toShapeStringOrNull();
if (!sPart) return null;
sParts.push(sPart);
}
return `CombineGeometry{${sParts.join(",")}}`;
}
} }
class BooleanGeometry extends GeometryBase { class BooleanGeometry extends GeometryBase {
@ -250,7 +291,22 @@ class BooleanGeometry extends GeometryBase {
let s = 0; let s = 0;
for (const operand of this.m_operands) s += operand.measureComplexity(); for (const operand of this.m_operands) s += operand.measureComplexity();
} }
toShapeStringOrNull() {
let sParts = [];
for (const item of this.m_operands) {
const sPart = item.toShapeStringOrNull();
if (!sPart) return null;
sParts.push(sPart);
} }
return `BooleanGeometry{${this.m_operator};${sParts.join(",")}}`;
}
}
exports.hashGeometry = function (geom) {
const s = geom.toShapeStringOrNull();
if (!s) return null;
return crypto.createHash("sha256").update(s).digest();
};
function combineWith(a, b) { function combineWith(a, b) {
if (a instanceof CombineGeometry) { if (a instanceof CombineGeometry) {
@ -260,6 +316,10 @@ function combineWith(a, b) {
} }
} }
function formatN(x) {
return `${Math.round(x * 0x10000)}`;
}
exports.GeometryBase = GeometryBase; exports.GeometryBase = GeometryBase;
exports.ContourGeometry = ContourGeometry; exports.ContourGeometry = ContourGeometry;
exports.ReferenceGeometry = ReferenceGeometry; exports.ReferenceGeometry = ReferenceGeometry;