Cache the entire geometry flattener
This commit is contained in:
parent
edbe5ff47e
commit
4c8de9f025
4 changed files with 95 additions and 56 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 });
|
||||||
|
|
|
@ -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,8 +291,23 @@ 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) {
|
||||||
return a.with(b);
|
return a.with(b);
|
||||||
|
@ -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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue