Initial work of ESM transformation
This commit is contained in:
parent
2472c9cff2
commit
b8205a63aa
303 changed files with 1959 additions and 2450 deletions
|
@ -1,6 +1,4 @@
|
|||
"use strict";
|
||||
|
||||
exports.Anchor = class Anchor {
|
||||
export class Anchor {
|
||||
constructor(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
@ -13,4 +11,4 @@ exports.Anchor = class Anchor {
|
|||
const y = a.x * tfm.xy + a.y * tfm.yy + tfm.y;
|
||||
return new Anchor(x, y);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
"use strict";
|
||||
|
||||
const { mix } = require("../utils");
|
||||
import { mix } from "../utils.mjs";
|
||||
|
||||
class Box {
|
||||
constructor(t, b, l, r) {
|
||||
|
@ -11,7 +9,6 @@ class Box {
|
|||
this.xMid = this.xMiddle = mix(l, r, 0.5);
|
||||
this.yMid = this.yMiddle = mix(b, t, 0.5);
|
||||
}
|
||||
|
||||
withTop(t) {
|
||||
return new Box(t, this.bottom, this.left, this.right);
|
||||
}
|
||||
|
@ -24,14 +21,12 @@ class Box {
|
|||
withRight(r) {
|
||||
return new Box(this.top, this.bottom, this.left, r);
|
||||
}
|
||||
|
||||
withXPadding(d) {
|
||||
return new Box(this.top, this.bottom, this.left + d, this.right - d);
|
||||
}
|
||||
withYPadding(d) {
|
||||
return new Box(this.top - d, this.bottom + d, this.left, this.right);
|
||||
}
|
||||
|
||||
mixX(t) {
|
||||
return mix(this.left, this.right, t);
|
||||
}
|
||||
|
@ -45,5 +40,4 @@ class Box {
|
|||
return mix(this.bottom, this.top, t);
|
||||
}
|
||||
}
|
||||
|
||||
exports.Box = Box;
|
||||
export { Box };
|
|
@ -1,80 +1,21 @@
|
|||
"use strict";
|
||||
|
||||
const TypoGeom = require("typo-geom");
|
||||
const { Point } = require("./point");
|
||||
const { Transform } = require("./transform");
|
||||
|
||||
exports.SPIRO_PRECISION = 1 / 2;
|
||||
exports.OCCURRENT_PRECISION = 1 / 16;
|
||||
exports.GEOMETRY_PRECISION = 1 / 4;
|
||||
exports.BOOLE_RESOLUTION = 0x4000;
|
||||
|
||||
exports.OffsetCurve = class OffsetCurve {
|
||||
constructor(bone, offset, contrast) {
|
||||
this.bone = bone;
|
||||
this.offset = offset;
|
||||
this.contrast = contrast;
|
||||
}
|
||||
eval(t) {
|
||||
const c = this.bone.eval(t);
|
||||
const d = this.bone.derivative(t);
|
||||
const absD = Math.hypot(d.x, d.y);
|
||||
return {
|
||||
x: c.x - (d.y / absD) * this.offset * this.contrast,
|
||||
y: c.y + (d.x / absD) * this.offset
|
||||
};
|
||||
}
|
||||
derivative(t) {
|
||||
const DELTA = 1 / 0x10000;
|
||||
const forward = this.eval(t + DELTA);
|
||||
const backward = this.eval(t - DELTA);
|
||||
return {
|
||||
x: (forward.x - backward.x) / (2 * DELTA),
|
||||
y: (forward.y - backward.y) / (2 * DELTA)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
exports.ReverseCurve = class ReverseCurve {
|
||||
constructor(original) {
|
||||
this.m_original = original;
|
||||
}
|
||||
eval(t) {
|
||||
return this.m_original.eval(1 - t);
|
||||
}
|
||||
derivative(t) {
|
||||
return -this.m_original.derivative(1 - t);
|
||||
}
|
||||
};
|
||||
|
||||
exports.convertShapeToArcs = function convertShapeToArcs(shape) {
|
||||
return shape.map(convertContourToArcs);
|
||||
};
|
||||
import * as TypoGeom from "typo-geom";
|
||||
import { Point } from "./point.mjs";
|
||||
import { Transform } from "./transform.mjs";
|
||||
|
||||
function contourToRep(contour) {
|
||||
let c = [];
|
||||
for (const z of contour) c.push({ type: z.type, x: z.x, y: z.y });
|
||||
return c;
|
||||
}
|
||||
exports.shapeToRep = function (shape) {
|
||||
return shape.map(contourToRep);
|
||||
};
|
||||
|
||||
function repToContour(contourRep) {
|
||||
let c = [];
|
||||
for (const z of contourRep) c.push(Point.fromXY(z.type, z.x, z.y));
|
||||
return c;
|
||||
}
|
||||
exports.repToShape = function (shapeRep) {
|
||||
return shapeRep.map(repToContour);
|
||||
};
|
||||
|
||||
function convertContourToArcs(contour) {
|
||||
if (!contour || !contour.length) return [];
|
||||
|
||||
const newContour = [];
|
||||
let z0 = Point.from(Point.Type.Corner, contour[0]);
|
||||
|
||||
for (let j = 1; j < contour.length; j++) {
|
||||
const z = contour[j];
|
||||
switch (z.type) {
|
||||
|
@ -99,7 +40,6 @@ function convertContourToArcs(contour) {
|
|||
let zf = contour[j + 1] || contour[0];
|
||||
const zfIsCorner = zf.type === Point.Type.contour;
|
||||
if (!zfIsCorner) zf = Point.from(Point.Type.Corner, zc).mix(0.5, zf);
|
||||
|
||||
newContour.push(
|
||||
new TypoGeom.Arcs.Bez3(
|
||||
z0,
|
||||
|
@ -108,7 +48,6 @@ function convertContourToArcs(contour) {
|
|||
Point.from(Point.Type.Corner, zf)
|
||||
)
|
||||
);
|
||||
|
||||
z0 = zf;
|
||||
if (zfIsCorner) j++;
|
||||
break;
|
||||
|
@ -124,11 +63,58 @@ function convertContourToArcs(contour) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newContour;
|
||||
}
|
||||
|
||||
exports.BezToContoursSink = class BezToContoursSink {
|
||||
export const SPIRO_PRECISION = 1 / 2;
|
||||
export const OCCURRENT_PRECISION = 1 / 16;
|
||||
export const GEOMETRY_PRECISION = 1 / 4;
|
||||
export const BOOLE_RESOLUTION = 0x4000;
|
||||
export class OffsetCurve {
|
||||
constructor(bone, offset, contrast) {
|
||||
this.bone = bone;
|
||||
this.offset = offset;
|
||||
this.contrast = contrast;
|
||||
}
|
||||
eval(t) {
|
||||
const c = this.bone.eval(t);
|
||||
const d = this.bone.derivative(t);
|
||||
const absD = Math.hypot(d.x, d.y);
|
||||
return {
|
||||
x: c.x - (d.y / absD) * this.offset * this.contrast,
|
||||
y: c.y + (d.x / absD) * this.offset
|
||||
};
|
||||
}
|
||||
derivative(t) {
|
||||
const DELTA = 1 / 0x10000;
|
||||
const forward = this.eval(t + DELTA);
|
||||
const backward = this.eval(t - DELTA);
|
||||
return {
|
||||
x: (forward.x - backward.x) / (2 * DELTA),
|
||||
y: (forward.y - backward.y) / (2 * DELTA)
|
||||
};
|
||||
}
|
||||
}
|
||||
export class ReverseCurve {
|
||||
constructor(original) {
|
||||
this.m_original = original;
|
||||
}
|
||||
eval(t) {
|
||||
return this.m_original.eval(1 - t);
|
||||
}
|
||||
derivative(t) {
|
||||
return -this.m_original.derivative(1 - t);
|
||||
}
|
||||
}
|
||||
export function convertShapeToArcs(shape) {
|
||||
return shape.map(convertContourToArcs);
|
||||
}
|
||||
export const shapeToRep = function (shape) {
|
||||
return shape.map(contourToRep);
|
||||
};
|
||||
export const repToShape = function (shapeRep) {
|
||||
return shapeRep.map(repToContour);
|
||||
};
|
||||
export class BezToContoursSink {
|
||||
constructor(gizmo) {
|
||||
this.gizmo = gizmo || Transform.Id();
|
||||
this.contours = [];
|
||||
|
@ -157,4 +143,4 @@ exports.BezToContoursSink = class BezToContoursSink {
|
|||
this.lastContour.push(Point.transformedXY(this.gizmo, Point.Type.CubicEnd, x2, y2));
|
||||
this.lastContour.push(Point.transformedXY(this.gizmo, Point.Type.Corner, x, y));
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,14 +1,11 @@
|
|||
"use strict";
|
||||
|
||||
const crypto = require("crypto");
|
||||
const TypoGeom = require("typo-geom");
|
||||
const SpiroJs = require("spiro");
|
||||
|
||||
const CurveUtil = require("./curve-util");
|
||||
const Format = require("../util/formatter");
|
||||
const { Point } = require("./point");
|
||||
const { Transform } = require("./transform");
|
||||
const { SpiroExpander } = require("./spiro-expand");
|
||||
import crypto from "crypto";
|
||||
import * as TypoGeom from "typo-geom";
|
||||
import * as SpiroJs from "spiro";
|
||||
import * as CurveUtil from "./curve-util.mjs";
|
||||
import * as Format from "../util/formatter.mjs";
|
||||
import { Point } from "./point.mjs";
|
||||
import { Transform } from "./transform.mjs";
|
||||
import { SpiroExpander } from "./spiro-expand.mjs";
|
||||
|
||||
class GeometryBase {
|
||||
asContours() {
|
||||
|
@ -33,7 +30,6 @@ class GeometryBase {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class ContourGeometry extends GeometryBase {
|
||||
constructor(points) {
|
||||
super();
|
||||
|
@ -67,7 +63,6 @@ class ContourGeometry extends GeometryBase {
|
|||
return Format.struct(`ContourGeometry`, Format.list(this.m_points.map(Format.typedPoint)));
|
||||
}
|
||||
}
|
||||
|
||||
class SpiroGeometry extends GeometryBase {
|
||||
constructor(gizmo, closed, knots) {
|
||||
super();
|
||||
|
@ -110,28 +105,22 @@ class SpiroGeometry extends GeometryBase {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DiSpiroGeometry extends GeometryBase {
|
||||
constructor(gizmo, contrast, closed, biKnots) {
|
||||
super();
|
||||
|
||||
this.m_biKnots = [];
|
||||
for (const k of biKnots) this.m_biKnots.push(k.clone());
|
||||
|
||||
this.m_closed = closed;
|
||||
this.m_gizmo = gizmo;
|
||||
this.m_contrast = contrast;
|
||||
|
||||
this.m_cachedExpansionResults = null;
|
||||
this.m_cachedContours = null;
|
||||
}
|
||||
|
||||
asContours() {
|
||||
if (this.m_cachedContours) return this.m_cachedContours;
|
||||
const expandResult = this.expand();
|
||||
const lhs = [...expandResult.lhs];
|
||||
const rhs = [...expandResult.rhs];
|
||||
|
||||
let rawGeometry;
|
||||
if (this.m_closed) {
|
||||
rawGeometry = new CombineGeometry([
|
||||
|
@ -161,7 +150,6 @@ class DiSpiroGeometry extends GeometryBase {
|
|||
this.m_cachedExpansionResults = expander.expand();
|
||||
return this.m_cachedExpansionResults;
|
||||
}
|
||||
|
||||
asReferences() {
|
||||
return null;
|
||||
}
|
||||
|
@ -177,7 +165,6 @@ class DiSpiroGeometry extends GeometryBase {
|
|||
}
|
||||
return this.m_biKnots.length;
|
||||
}
|
||||
|
||||
toShapeStringOrNull() {
|
||||
return Format.struct(
|
||||
"DiSpiroGeometry",
|
||||
|
@ -188,7 +175,6 @@ class DiSpiroGeometry extends GeometryBase {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ReferenceGeometry extends GeometryBase {
|
||||
constructor(glyph, x, y) {
|
||||
super();
|
||||
|
@ -231,7 +217,6 @@ class ReferenceGeometry extends GeometryBase {
|
|||
return Format.struct("ReferenceGeometry", sTarget, Format.n(this.m_x), Format.n(this.m_y));
|
||||
}
|
||||
}
|
||||
|
||||
class TaggedGeometry extends GeometryBase {
|
||||
constructor(g, tag) {
|
||||
super();
|
||||
|
@ -261,7 +246,6 @@ class TaggedGeometry extends GeometryBase {
|
|||
return this.m_geom.toShapeStringOrNull();
|
||||
}
|
||||
}
|
||||
|
||||
class TransformedGeometry extends GeometryBase {
|
||||
constructor(g, tfm) {
|
||||
super();
|
||||
|
@ -281,7 +265,6 @@ class TransformedGeometry extends GeometryBase {
|
|||
if (!Transform.isTranslate(this.m_transform)) return null;
|
||||
const rs = this.m_geom.asReferences();
|
||||
if (!rs) return null;
|
||||
|
||||
let result = [];
|
||||
for (const { glyph, x, y } of rs)
|
||||
result.push({ glyph, x: x + this.m_transform.x, y: y + this.m_transform.y });
|
||||
|
@ -324,7 +307,6 @@ class TransformedGeometry extends GeometryBase {
|
|||
return Format.struct("TransformedGeometry", sTarget, Format.gizmo(this.m_transform));
|
||||
}
|
||||
}
|
||||
|
||||
class CombineGeometry extends GeometryBase {
|
||||
constructor(parts) {
|
||||
super();
|
||||
|
@ -373,7 +355,6 @@ class CombineGeometry extends GeometryBase {
|
|||
let s = 0;
|
||||
for (const part of this.m_parts) s += part.measureComplexity();
|
||||
}
|
||||
|
||||
unlinkReferences() {
|
||||
let parts = [];
|
||||
for (const part of this.m_parts) {
|
||||
|
@ -396,7 +377,6 @@ class CombineGeometry extends GeometryBase {
|
|||
return Format.struct("CombineGeometry", Format.list(sParts));
|
||||
}
|
||||
}
|
||||
|
||||
class BooleanGeometry extends GeometryBase {
|
||||
constructor(operator, operands) {
|
||||
super();
|
||||
|
@ -411,7 +391,6 @@ class BooleanGeometry extends GeometryBase {
|
|||
}
|
||||
asContoursImpl() {
|
||||
if (this.m_operands.length === 0) return [];
|
||||
|
||||
let arcs = CurveUtil.convertShapeToArcs(this.m_operands[0].asContours());
|
||||
if (this.m_operands.length === 1) {
|
||||
arcs = TypoGeom.Boolean.removeOverlap(
|
||||
|
@ -456,7 +435,6 @@ class BooleanGeometry extends GeometryBase {
|
|||
unlinkReferences() {
|
||||
if (this.m_operands.length === 0) return new CombineGeometry([]);
|
||||
if (this.m_operands.length === 1) return this.m_operands[0].unlinkReferences();
|
||||
|
||||
let operands = [];
|
||||
for (const operand of this.m_operands) {
|
||||
operands.push(operand.unlinkReferences());
|
||||
|
@ -473,13 +451,6 @@ class BooleanGeometry extends GeometryBase {
|
|||
return Format.struct("BooleanGeometry", this.m_operator, Format.list(sParts));
|
||||
}
|
||||
}
|
||||
|
||||
exports.hashGeometry = function (geom) {
|
||||
const s = geom.toShapeStringOrNull();
|
||||
if (!s) return null;
|
||||
return crypto.createHash("sha256").update(s).digest("hex");
|
||||
};
|
||||
|
||||
function combineWith(a, b) {
|
||||
if (a instanceof CombineGeometry) {
|
||||
return a.with(b);
|
||||
|
@ -487,15 +458,18 @@ function combineWith(a, b) {
|
|||
return new CombineGeometry([a, b]);
|
||||
}
|
||||
}
|
||||
|
||||
exports.GeometryBase = GeometryBase;
|
||||
exports.SpiroGeometry = SpiroGeometry;
|
||||
exports.DiSpiroGeometry = DiSpiroGeometry;
|
||||
exports.ContourGeometry = ContourGeometry;
|
||||
exports.ReferenceGeometry = ReferenceGeometry;
|
||||
exports.TaggedGeometry = TaggedGeometry;
|
||||
exports.TransformedGeometry = TransformedGeometry;
|
||||
exports.CombineGeometry = CombineGeometry;
|
||||
exports.BooleanGeometry = BooleanGeometry;
|
||||
|
||||
exports.combineWith = combineWith;
|
||||
export const hashGeometry = function (geom) {
|
||||
const s = geom.toShapeStringOrNull();
|
||||
if (!s) return null;
|
||||
return crypto.createHash("sha256").update(s).digest("hex");
|
||||
};
|
||||
export { GeometryBase };
|
||||
export { SpiroGeometry };
|
||||
export { DiSpiroGeometry };
|
||||
export { ContourGeometry };
|
||||
export { ReferenceGeometry };
|
||||
export { TaggedGeometry };
|
||||
export { TransformedGeometry };
|
||||
export { CombineGeometry };
|
||||
export { BooleanGeometry };
|
||||
export { combineWith };
|
|
@ -1,5 +1,3 @@
|
|||
"use strict";
|
||||
|
||||
class Point {
|
||||
constructor(type, x, y) {
|
||||
this.type = type;
|
||||
|
@ -31,7 +29,6 @@ class Point {
|
|||
round(d) {
|
||||
return new Point(this.type, Math.round(d * this.x) / d, Math.round(d * this.y) / d);
|
||||
}
|
||||
|
||||
static from(type, z) {
|
||||
return new Point(type, z.x || 0, z.y || 0);
|
||||
}
|
||||
|
@ -61,12 +58,10 @@ class Point {
|
|||
return new Point(z.type, z.x + dx || 0, z.y + dy || 0);
|
||||
}
|
||||
}
|
||||
|
||||
Point.Type = {
|
||||
Corner: 0,
|
||||
CubicStart: 1,
|
||||
CubicEnd: 2,
|
||||
Quadratic: 3
|
||||
};
|
||||
|
||||
exports.Point = Point;
|
||||
export { Point };
|
|
@ -1,9 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
const SpiroJs = require("spiro");
|
||||
|
||||
const { linreg } = require("../utils");
|
||||
const Format = require("../util/formatter");
|
||||
import * as SpiroJs from "spiro";
|
||||
import { linreg } from "../utils.mjs";
|
||||
import * as Format from "../util/formatter.mjs";
|
||||
|
||||
class BiKnot {
|
||||
constructor(type, x, y, d1, d2) {
|
||||
|
@ -23,7 +20,6 @@ class BiKnot {
|
|||
k1.unimportant = this.unimportant;
|
||||
return k1;
|
||||
}
|
||||
|
||||
toShapeString() {
|
||||
return Format.tuple(
|
||||
this.type,
|
||||
|
@ -41,17 +37,14 @@ class BiKnot {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BiKnotCollector {
|
||||
constructor(gizmo, contrast) {
|
||||
this.gizmo = gizmo;
|
||||
this.contrast = contrast;
|
||||
|
||||
this.controlKnots = [];
|
||||
this.defaultD1 = 0;
|
||||
this.defaultD2 = 0;
|
||||
}
|
||||
|
||||
pushKnot(type, x, y) {
|
||||
const tfZ = this.gizmo.applyXY(x, y);
|
||||
const k0 = this.controlKnots[this.controlKnots.length - 1];
|
||||
|
@ -61,7 +54,6 @@ class BiKnotCollector {
|
|||
this.controlKnots.push(new BiKnot(type, tfZ.x, tfZ.y, this.defaultD1, this.defaultD2));
|
||||
}
|
||||
}
|
||||
|
||||
setWidth(l, r) {
|
||||
const k0 = this.controlKnots[this.controlKnots.length - 1];
|
||||
if (k0) {
|
||||
|
@ -83,7 +75,6 @@ class BiKnotCollector {
|
|||
if (k0) k0.unimportant = 1;
|
||||
}
|
||||
}
|
||||
|
||||
class SpiroExpander {
|
||||
constructor(gizmo, contrast, closed, cks) {
|
||||
this.gizmo = gizmo;
|
||||
|
@ -91,12 +82,10 @@ class SpiroExpander {
|
|||
this.closed = closed;
|
||||
this.controlKnots = cks;
|
||||
}
|
||||
|
||||
initializeNormals() {
|
||||
const normalRectifier = new NormalRectifier(this.controlKnots, this.gizmo);
|
||||
SpiroJs.spiroToArcsOnContext(this.controlKnots, this.closed, normalRectifier);
|
||||
}
|
||||
|
||||
iterateNormals() {
|
||||
const centerBone = this.getPass2Knots();
|
||||
const normalRectifier = new NormalRectifier(this.controlKnots, this.gizmo);
|
||||
|
@ -117,7 +106,6 @@ class SpiroExpander {
|
|||
}
|
||||
return middles;
|
||||
}
|
||||
|
||||
expand() {
|
||||
const lhs = [],
|
||||
rhs = [];
|
||||
|
@ -137,12 +125,10 @@ class SpiroExpander {
|
|||
y: 0
|
||||
};
|
||||
}
|
||||
|
||||
// Create important knots
|
||||
for (let j = 0; j < this.controlKnots.length; j++) {
|
||||
const knot = this.controlKnots[j];
|
||||
if (knot.unimportant) continue;
|
||||
|
||||
let dx, dy;
|
||||
if (knot.proposedNormal) {
|
||||
dx = knot.proposedNormal.x;
|
||||
|
@ -153,14 +139,12 @@ class SpiroExpander {
|
|||
}
|
||||
lhs[j].x = knot.x + knot.d1 * dx;
|
||||
lhs[j].y = knot.y + knot.d1 * dy;
|
||||
|
||||
rhs[j].x = knot.x - knot.d2 * dx;
|
||||
rhs[j].y = knot.y - knot.d2 * dy;
|
||||
}
|
||||
this.interpolateUnimportantKnots(lhs, rhs);
|
||||
return { lhs, rhs };
|
||||
}
|
||||
|
||||
interpolateUnimportantKnots(lhs, rhs) {
|
||||
for (let j = 0; j < this.controlKnots.length; j++) {
|
||||
const knot = this.controlKnots[j];
|
||||
|
@ -168,7 +152,6 @@ class SpiroExpander {
|
|||
let jBefore, jAfter;
|
||||
for (jBefore = j - 1; cyNth(this.controlKnots, jBefore).unimportant; jBefore--);
|
||||
for (jAfter = j + 1; cyNth(this.controlKnots, jAfter).unimportant; jAfter++);
|
||||
|
||||
const knotBefore = this.gizmo.unapply(cyNth(this.controlKnots, jBefore)),
|
||||
knotAfter = this.gizmo.unapply(cyNth(this.controlKnots, jAfter)),
|
||||
ref = this.gizmo.unapply(knot),
|
||||
|
@ -176,7 +159,6 @@ class SpiroExpander {
|
|||
lhsAfter = this.gizmo.unapply(cyNth(lhs, jAfter)),
|
||||
rhsBefore = this.gizmo.unapply(cyNth(rhs, jBefore)),
|
||||
rhsAfter = this.gizmo.unapply(cyNth(rhs, jAfter));
|
||||
|
||||
const lhsTf = this.gizmo.applyXY(
|
||||
linreg(knotBefore.x, lhsBefore.x, knotAfter.x, lhsAfter.x, ref.x),
|
||||
linreg(knotBefore.y, lhsBefore.y, knotAfter.y, lhsAfter.y, ref.y)
|
||||
|
@ -185,20 +167,17 @@ class SpiroExpander {
|
|||
linreg(knotBefore.x, rhsBefore.x, knotAfter.x, rhsAfter.x, ref.x),
|
||||
linreg(knotBefore.y, rhsBefore.y, knotAfter.y, rhsAfter.y, ref.y)
|
||||
);
|
||||
|
||||
(lhs[j].x = lhsTf.x), (lhs[j].y = lhsTf.y);
|
||||
(rhs[j].x = rhsTf.x), (rhs[j].y = rhsTf.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NormalRectifier {
|
||||
constructor(stage1ControlKnots, gizmo) {
|
||||
this.gizmo = gizmo;
|
||||
this.controlKnots = stage1ControlKnots;
|
||||
this.nKnotsProcessed = 0;
|
||||
}
|
||||
|
||||
beginShape() {}
|
||||
endShape() {}
|
||||
moveTo(x, y) {
|
||||
|
@ -224,7 +203,6 @@ class NormalRectifier {
|
|||
this.nKnotsProcessed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
function isTangentValid(d) {
|
||||
return isFinite(d.x) && isFinite(d.y);
|
||||
}
|
||||
|
@ -240,6 +218,5 @@ function reverseKnotType(ty) {
|
|||
function cyNth(a, j) {
|
||||
return a[j % a.length];
|
||||
}
|
||||
|
||||
exports.BiKnotCollector = BiKnotCollector;
|
||||
exports.SpiroExpander = SpiroExpander;
|
||||
export { BiKnotCollector };
|
||||
export { SpiroExpander };
|
|
@ -1,6 +1,4 @@
|
|||
"use strict";
|
||||
|
||||
exports.Transform = class Transform {
|
||||
export class Transform {
|
||||
constructor(xx, yx, xy, yy, x, y) {
|
||||
this.xx = xx;
|
||||
this.yx = yx;
|
||||
|
@ -9,15 +7,12 @@ exports.Transform = class Transform {
|
|||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
static Id() {
|
||||
return new Transform(1, 0, 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
static Translate(x, y) {
|
||||
return new Transform(1, 0, 0, 1, x, y);
|
||||
}
|
||||
|
||||
apply(pt) {
|
||||
return this.applyXY(pt.x, pt.y);
|
||||
}
|
||||
|
@ -56,11 +51,9 @@ exports.Transform = class Transform {
|
|||
-(-this.x * this.xy + this.y * this.xx) / denom
|
||||
);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `[[${this.xx} ${this.xy}] [${this.yx} ${this.yy}]] + [[${this.x}] [${this.y}]]`;
|
||||
}
|
||||
|
||||
static isTranslate(tfm) {
|
||||
return tfm.xx === 1 && tfm.yy === 1 && tfm.xy === 0 && tfm.yx === 0;
|
||||
}
|
||||
|
@ -85,4 +78,4 @@ exports.Transform = class Transform {
|
|||
z00.y
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,18 +1,14 @@
|
|||
"use strict";
|
||||
|
||||
exports.GlyphBlock = class GlyphBlock {
|
||||
export class GlyphBlock {
|
||||
constructor(capture, blockName, body) {
|
||||
this.capture = capture;
|
||||
this.blockName = blockName;
|
||||
this.body = body;
|
||||
|
||||
this.resolved = false;
|
||||
this.exports = {};
|
||||
}
|
||||
resolve() {
|
||||
if (this.resolved) return this.exports;
|
||||
this.resolved = true;
|
||||
|
||||
const pendingApplications = [];
|
||||
const ExportCapture = fnObj => {
|
||||
pendingApplications.push(() => {
|
||||
|
@ -21,9 +17,8 @@ exports.GlyphBlock = class GlyphBlock {
|
|||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.body(this.capture, ExportCapture);
|
||||
for (const f of pendingApplications) f();
|
||||
return this.exports;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,13 +1,10 @@
|
|||
"use strict";
|
||||
|
||||
exports.GlyphStore = class GlyphStore {
|
||||
export class GlyphStore {
|
||||
constructor() {
|
||||
this.nameForward = new Map();
|
||||
this.nameBackward = new Map();
|
||||
this.encodingForward = new Map();
|
||||
this.encodingBackward = new Map();
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.nameForward.size;
|
||||
}
|
||||
|
@ -20,13 +17,11 @@ exports.GlyphStore = class GlyphStore {
|
|||
encodedEntries() {
|
||||
return this.encodingForward.entries();
|
||||
}
|
||||
|
||||
*flattenCodes(g, flatteners) {
|
||||
{
|
||||
const codes = this.encodingBackward.get(g);
|
||||
if (codes) for (const c of codes) yield c;
|
||||
}
|
||||
|
||||
for (const gr of flatteners) {
|
||||
const gn = gr.get(g);
|
||||
if (!gn) continue;
|
||||
|
@ -36,7 +31,6 @@ exports.GlyphStore = class GlyphStore {
|
|||
if (codes2) for (const c of codes2) yield c;
|
||||
}
|
||||
}
|
||||
|
||||
addGlyph(name, g) {
|
||||
this.nameForward.set(name, g);
|
||||
this.nameBackward.set(g, name);
|
||||
|
@ -57,7 +51,6 @@ exports.GlyphStore = class GlyphStore {
|
|||
queryNameOf(g) {
|
||||
return this.nameBackward.get(g);
|
||||
}
|
||||
|
||||
deleteGlyph(g) {
|
||||
const name = this.nameBackward.get(g);
|
||||
this.nameBackward.delete(g);
|
||||
|
@ -72,7 +65,6 @@ exports.GlyphStore = class GlyphStore {
|
|||
this.deleteUnicodeAssignmentsOf(g);
|
||||
}
|
||||
}
|
||||
|
||||
encodeGlyph(u, g) {
|
||||
this.encodingForward.set(u, g);
|
||||
let s = this.encodingBackward.get(g);
|
||||
|
@ -108,7 +100,6 @@ exports.GlyphStore = class GlyphStore {
|
|||
if (s) for (const u of s) this.encodingForward.delete(u);
|
||||
this.encodingBackward.delete(g);
|
||||
}
|
||||
|
||||
filterByName(nameSet) {
|
||||
const gs1 = new GlyphStore();
|
||||
for (const [name, g] of this.nameForward) {
|
||||
|
@ -119,4 +110,4 @@ exports.GlyphStore = class GlyphStore {
|
|||
}
|
||||
return gs1;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,41 +1,33 @@
|
|||
"use strict";
|
||||
import { Transform } from "../geometry/transform.mjs";
|
||||
import { Point } from "../geometry/point.mjs";
|
||||
import { Anchor } from "../geometry/anchor.mjs";
|
||||
import * as Geom from "../geometry/index.mjs";
|
||||
|
||||
const { Transform } = require("../geometry/transform");
|
||||
const { Point } = require("../geometry/point");
|
||||
const { Anchor } = require("../geometry/anchor");
|
||||
const Geom = require("../geometry");
|
||||
|
||||
exports.Glyph = class Glyph {
|
||||
export class Glyph {
|
||||
constructor(_identifier) {
|
||||
this._m_identifier = _identifier;
|
||||
|
||||
// Ranks
|
||||
this.glyphRank = 0;
|
||||
this.grRank = 0;
|
||||
this.codeRank = 0xffffffff;
|
||||
this.subRank = 0xffffffff;
|
||||
|
||||
// Geometry
|
||||
this.geometry = new Geom.CombineGeometry();
|
||||
this.gizmo = Transform.Id();
|
||||
|
||||
// Metrics
|
||||
this.advanceWidth = 500;
|
||||
this.markAnchors = {};
|
||||
this.baseAnchors = {};
|
||||
|
||||
// Tracking
|
||||
this.dependencies = [];
|
||||
this.ctxTag = null;
|
||||
}
|
||||
|
||||
get contours() {
|
||||
throw new TypeError("Glyph::contours has been deprecated");
|
||||
}
|
||||
get semanticInclusions() {
|
||||
throw new TypeError("Glyph::semanticInclusions has been deprecated");
|
||||
}
|
||||
|
||||
get name() {
|
||||
throw new TypeError("Glyph::name has been deprecated");
|
||||
}
|
||||
|
@ -77,11 +69,9 @@ exports.Glyph = class Glyph {
|
|||
}
|
||||
includeGlyph(g, copyAnchors, copyWidth) {
|
||||
if (g instanceof Function) throw new Error("Unreachable");
|
||||
|
||||
// Combine anchors and get offset
|
||||
let shift = { x: 0, y: 0 };
|
||||
this.combineMarks(g, shift);
|
||||
|
||||
this.includeGlyphImpl(g, shift.x, shift.y);
|
||||
if (g.isMarkSet) throw new Error("Invalid component to be introduced.");
|
||||
if (copyAnchors) this.copyAnchors(g);
|
||||
|
@ -100,7 +90,6 @@ exports.Glyph = class Glyph {
|
|||
this.glyphRank = g.glyphRank;
|
||||
this.avoidBeingComposite = g.avoidBeingComposite;
|
||||
}
|
||||
|
||||
includeGeometry(g) {
|
||||
if (this.ctxTag) g = new Geom.TaggedGeometry(g, this.ctxTag);
|
||||
this.geometry = Geom.combineWith(this.geometry, g);
|
||||
|
@ -114,7 +103,6 @@ exports.Glyph = class Glyph {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
includeContours(cs, shiftX, shiftY) {
|
||||
let parts = [];
|
||||
for (const contour of cs) {
|
||||
|
@ -124,7 +112,6 @@ exports.Glyph = class Glyph {
|
|||
}
|
||||
this.includeGeometry(new Geom.CombineGeometry(parts));
|
||||
}
|
||||
|
||||
applyTransform(tfm, alsoAnchors) {
|
||||
this.geometry = new Geom.TransformedGeometry(this.geometry, tfm);
|
||||
if (alsoAnchors) {
|
||||
|
@ -134,7 +121,6 @@ exports.Glyph = class Glyph {
|
|||
this.markAnchors[k] = Anchor.transform(tfm, this.markAnchors[k]);
|
||||
}
|
||||
}
|
||||
|
||||
tryBecomeMirrorOf(dst, rankSet) {
|
||||
if (rankSet.has(this) || rankSet.has(dst)) return;
|
||||
const csThis = this.geometry.unlinkReferences().toShapeStringOrNull();
|
||||
|
@ -144,14 +130,12 @@ exports.Glyph = class Glyph {
|
|||
rankSet.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
clearGeometry() {
|
||||
this.geometry = new Geom.CombineGeometry();
|
||||
}
|
||||
ejectTagged(tag) {
|
||||
this.geometry = this.geometry.filterTag(t => tag !== t);
|
||||
}
|
||||
|
||||
// Anchors
|
||||
combineMarks(g, shift) {
|
||||
if (!g.markAnchors) return;
|
||||
|
@ -198,4 +182,4 @@ exports.Glyph = class Glyph {
|
|||
deleteMarkAnchor(id) {
|
||||
delete this.markAnchors[id];
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
"use strict";
|
||||
|
||||
const crypto = require("crypto");
|
||||
import crypto from "crypto";
|
||||
|
||||
const Dotless = {
|
||||
tag: "dtls",
|
||||
|
@ -17,7 +15,6 @@ const Dotless = {
|
|||
return name + ".dotless";
|
||||
}
|
||||
};
|
||||
|
||||
function SimpleProp(key) {
|
||||
return {
|
||||
get(glyph) {
|
||||
|
@ -31,11 +28,9 @@ function SimpleProp(key) {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
const LowerYDotAtBelow = SimpleProp("LowerYDotAtBelow");
|
||||
const RightDependentTrigger = SimpleProp("RightDependentTrigger");
|
||||
const MathSansSerif = SimpleProp("MathSansSerif");
|
||||
|
||||
function DependentLinkProp(key) {
|
||||
return {
|
||||
get(glyph, subKey) {
|
||||
|
@ -57,13 +52,10 @@ function DependentLinkProp(key) {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
const RightDependentLink = DependentLinkProp("RightDependentLink");
|
||||
|
||||
function OtlTaggedProp(key, otlTag) {
|
||||
return { ...SimpleProp(key), otlTag };
|
||||
}
|
||||
|
||||
const Nwid = OtlTaggedProp("Nwid", "NWID");
|
||||
const Wwid = OtlTaggedProp("Wwid", "WWID");
|
||||
const Lnum = OtlTaggedProp("Lnum", "lnum");
|
||||
|
@ -71,7 +63,6 @@ const Onum = OtlTaggedProp("Onum", "onum");
|
|||
const AplForm = OtlTaggedProp("AplForm", "APLF");
|
||||
const NumeratorForm = OtlTaggedProp("Numerator", "numr");
|
||||
const DenominatorForm = OtlTaggedProp("Denominator", "dnom");
|
||||
|
||||
function DecompositionProp(key) {
|
||||
return {
|
||||
get(glyph) {
|
||||
|
@ -85,11 +76,9 @@ function DecompositionProp(key) {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
const CvDecompose = DecompositionProp("CvDecompose");
|
||||
const PseudoCvDecompose = DecompositionProp("PseudoCvDecompose");
|
||||
const CcmpDecompose = DecompositionProp("CcmpDecompose");
|
||||
|
||||
const TieMark = {
|
||||
tag: "TMRK",
|
||||
get(glyph) {
|
||||
|
@ -108,7 +97,6 @@ const TieMark = {
|
|||
return name + ".tieMark";
|
||||
}
|
||||
};
|
||||
|
||||
const TieGlyph = {
|
||||
get(glyph) {
|
||||
if (glyph && glyph.related) return glyph.related.TieGlyph;
|
||||
|
@ -120,7 +108,6 @@ const TieGlyph = {
|
|||
Joining.or(glyph, Joining.Classes.Mid);
|
||||
}
|
||||
};
|
||||
|
||||
const Radical = {
|
||||
get(glyph) {
|
||||
if (glyph && glyph.related) return !!glyph.related.radical;
|
||||
|
@ -131,7 +118,6 @@ const Radical = {
|
|||
glyph.related.radical = true;
|
||||
}
|
||||
};
|
||||
|
||||
const RequireCcmpDecompose = {
|
||||
get(glyph) {
|
||||
if (glyph && glyph.related) return !!glyph.related.RequireCcmpDecompose;
|
||||
|
@ -142,7 +128,6 @@ const RequireCcmpDecompose = {
|
|||
glyph.related.RequireCcmpDecompose = true;
|
||||
}
|
||||
};
|
||||
|
||||
const Joining = {
|
||||
get(glyph) {
|
||||
if (glyph && glyph.related) return glyph.related.joining || 0;
|
||||
|
@ -173,7 +158,6 @@ const Joining = {
|
|||
Mid: 3
|
||||
}
|
||||
};
|
||||
|
||||
const CvTagCache = new Map();
|
||||
function Cv(tag, rank) {
|
||||
const key = tag + "#" + rank;
|
||||
|
@ -213,14 +197,12 @@ function Cv(tag, rank) {
|
|||
CvTagCache.set(key, rel);
|
||||
return rel;
|
||||
}
|
||||
|
||||
const DotlessOrNot = {
|
||||
query(glyph) {
|
||||
if (Dotless.get(glyph)) return [Dotless];
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const AnyCv = {
|
||||
query(glyph) {
|
||||
let ret = [];
|
||||
|
@ -235,7 +217,6 @@ const AnyCv = {
|
|||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
const AnyDerivingCv = {
|
||||
query(glyph) {
|
||||
let ret = [];
|
||||
|
@ -261,14 +242,11 @@ const AnyDerivingCv = {
|
|||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
function getGrTreeImpl(gid, grSetList, fnGidToGlyph, sink) {
|
||||
if (!grSetList.length) return;
|
||||
const g = fnGidToGlyph(gid);
|
||||
if (!g) return;
|
||||
|
||||
const grs = grSetList[0].query(g);
|
||||
|
||||
getGrTreeImpl(gid, grSetList.slice(1), fnGidToGlyph, sink);
|
||||
if (grs && grs.length) {
|
||||
for (const gr of grs) {
|
||||
|
@ -283,7 +261,6 @@ function getGrTree(gid, grSetList, fnGidToGlyph) {
|
|||
getGrTreeImpl(gid, grSetList, fnGidToGlyph, sink);
|
||||
return sink;
|
||||
}
|
||||
|
||||
function gidListSame(a, b) {
|
||||
for (let j = 0; j < a.length; j++) {
|
||||
if (a[j] !== b[j]) return false;
|
||||
|
@ -303,7 +280,6 @@ function gidListMap(gidList, gr, fnGidToGlyph) {
|
|||
if (effective) return gidList1;
|
||||
else return null;
|
||||
}
|
||||
|
||||
function collectGidLists(gidListOrig, gidList, grl, excluded, fnGidToGlyph, sink) {
|
||||
if (!grl.length) {
|
||||
sink.push(gidList);
|
||||
|
@ -319,16 +295,13 @@ function collectGidLists(gidListOrig, gidList, grl, excluded, fnGidToGlyph, sink
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getGrMesh(gidList, grq, fnGidToGlyph) {
|
||||
if (typeof gidList === "string" || !Array.isArray(gidList))
|
||||
throw new TypeError(`glyphs must be a glyph array!`);
|
||||
|
||||
const allGrSet = new Set();
|
||||
for (const g of gidList) {
|
||||
for (const gr of grq.query(fnGidToGlyph(g))) allGrSet.add(gr);
|
||||
}
|
||||
|
||||
const allGrList = Array.from(allGrSet);
|
||||
let ret = [];
|
||||
for (const gr of allGrList) {
|
||||
|
@ -342,20 +315,16 @@ function getGrMesh(gidList, grq, fnGidToGlyph) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function createGrDisplaySheet(glyphStore, gid) {
|
||||
const glyph = glyphStore.queryByName(gid);
|
||||
if (!glyph) return [];
|
||||
|
||||
// Query selected typographic features -- mostly NWID and WWID
|
||||
let typographicFeatures = [];
|
||||
displayQueryPairFeatures(glyphStore, gid, Nwid, Wwid, typographicFeatures);
|
||||
displayQueryPairFeatures(glyphStore, gid, Lnum, Onum, typographicFeatures);
|
||||
displayQuerySingleFeature(glyphStore, gid, AplForm, typographicFeatures);
|
||||
|
||||
let charVariantFeatures = [];
|
||||
const decomposition = CvDecompose.get(glyph) || PseudoCvDecompose.get(glyph);
|
||||
if (decomposition) {
|
||||
|
@ -368,7 +337,6 @@ function createGrDisplaySheet(glyphStore, gid) {
|
|||
} else {
|
||||
queryCvFeatureTagsOf(charVariantFeatures, gid, glyph, null);
|
||||
}
|
||||
|
||||
return [typographicFeatures, charVariantFeatures];
|
||||
}
|
||||
function displayQueryPairFeatures(gs, gid, grCis, grTrans, sink) {
|
||||
|
@ -389,7 +357,6 @@ function displayQuerySingleFeature(gs, gid, grCis, sink) {
|
|||
sink.push(`'${grCis.otlTag}' 0`, `'${grCis.otlTag}' 1`);
|
||||
}
|
||||
}
|
||||
|
||||
function byTagPreference(a, b) {
|
||||
const ua = a.tag.toUpperCase(),
|
||||
ub = b.tag.toUpperCase();
|
||||
|
@ -407,13 +374,11 @@ function queryCvFeatureTagsOf(sink, gid, glyph, variantAssignmentSet) {
|
|||
if (target === gid) continue;
|
||||
if (existingGlyphs.has(target)) continue;
|
||||
existingGlyphs.add(target);
|
||||
|
||||
let g = m.get(tag);
|
||||
if (!g) {
|
||||
g = [];
|
||||
m.set(tag, g);
|
||||
}
|
||||
|
||||
const assignCss = `'${tag}' ${gr.rank}`;
|
||||
if (!variantAssignmentSet) {
|
||||
g.push(assignCss);
|
||||
|
@ -424,7 +389,6 @@ function queryCvFeatureTagsOf(sink, gid, glyph, variantAssignmentSet) {
|
|||
}
|
||||
for (const g of m.values()) if (g.length) sink.push(g);
|
||||
}
|
||||
|
||||
function linkSuffixGr(gs, suffix, gr) {
|
||||
const reSuffix = new RegExp("\\." + suffix + "$");
|
||||
for (const [gnSuffixed, gSuffixed] of gs.namedEntries()) {
|
||||
|
@ -448,7 +412,6 @@ function linkSuffixPairGr(gs, tagCis, tagTrans, grCis, grTrans) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hashCv(g) {
|
||||
const hasher = crypto.createHash("sha256");
|
||||
for (const gr of AnyCv.query(g)) {
|
||||
|
@ -456,37 +419,34 @@ function hashCv(g) {
|
|||
}
|
||||
return hasher.digest("hex");
|
||||
}
|
||||
|
||||
exports.Dotless = Dotless;
|
||||
exports.LowerYDotAtBelow = LowerYDotAtBelow;
|
||||
exports.Cv = Cv;
|
||||
exports.AnyCv = AnyCv;
|
||||
exports.DotlessOrNot = DotlessOrNot;
|
||||
exports.getGrTree = getGrTree;
|
||||
exports.getGrMesh = getGrMesh;
|
||||
exports.TieMark = TieMark;
|
||||
exports.TieGlyph = TieGlyph;
|
||||
exports.Radical = Radical;
|
||||
exports.RequireCcmpDecompose = RequireCcmpDecompose;
|
||||
exports.Joining = Joining;
|
||||
exports.AnyDerivingCv = AnyDerivingCv;
|
||||
exports.CcmpDecompose = CcmpDecompose;
|
||||
exports.CvDecompose = CvDecompose;
|
||||
exports.PseudoCvDecompose = PseudoCvDecompose;
|
||||
exports.RightDependentLink = RightDependentLink;
|
||||
exports.RightDependentTrigger = RightDependentTrigger;
|
||||
exports.MathSansSerif = MathSansSerif;
|
||||
exports.Nwid = Nwid;
|
||||
exports.Wwid = Wwid;
|
||||
exports.Lnum = Lnum;
|
||||
exports.Onum = Onum;
|
||||
exports.AplForm = AplForm;
|
||||
exports.NumeratorForm = NumeratorForm;
|
||||
exports.DenominatorForm = DenominatorForm;
|
||||
exports.hashCv = hashCv;
|
||||
|
||||
exports.createGrDisplaySheet = createGrDisplaySheet;
|
||||
exports.linkSuffixGr = linkSuffixGr;
|
||||
exports.linkSuffixPairGr = linkSuffixPairGr;
|
||||
|
||||
exports.SvInheritableRelations = [RightDependentLink, RightDependentTrigger, Joining];
|
||||
export const SvInheritableRelations = [RightDependentLink, RightDependentTrigger, Joining];
|
||||
export { Dotless };
|
||||
export { LowerYDotAtBelow };
|
||||
export { Cv };
|
||||
export { AnyCv };
|
||||
export { DotlessOrNot };
|
||||
export { getGrTree };
|
||||
export { getGrMesh };
|
||||
export { TieMark };
|
||||
export { TieGlyph };
|
||||
export { Radical };
|
||||
export { RequireCcmpDecompose };
|
||||
export { Joining };
|
||||
export { AnyDerivingCv };
|
||||
export { CcmpDecompose };
|
||||
export { CvDecompose };
|
||||
export { PseudoCvDecompose };
|
||||
export { RightDependentLink };
|
||||
export { RightDependentTrigger };
|
||||
export { MathSansSerif };
|
||||
export { Nwid };
|
||||
export { Wwid };
|
||||
export { Lnum };
|
||||
export { Onum };
|
||||
export { AplForm };
|
||||
export { NumeratorForm };
|
||||
export { DenominatorForm };
|
||||
export { hashCv };
|
||||
export { createGrDisplaySheet };
|
||||
export { linkSuffixGr };
|
||||
export { linkSuffixPairGr };
|
|
@ -1,10 +1,15 @@
|
|||
"use strict";
|
||||
import * as Parameters from "./parameters.mjs";
|
||||
|
||||
const Parameters = require("./parameters");
|
||||
|
||||
exports.applyLigationData = function (data, para, argv) {
|
||||
function createBuildup(simple, buildup) {
|
||||
let ligSet = new Set();
|
||||
for (const s of buildup) {
|
||||
if (!simple[s]) throw new Error("Cannot find simple ligation group " + s);
|
||||
ligSet.add(simple[s].ligGroup);
|
||||
}
|
||||
return Array.from(ligSet);
|
||||
}
|
||||
export const applyLigationData = function (data, para, argv) {
|
||||
const defaultBuildup = {};
|
||||
|
||||
const hives = {};
|
||||
hives["default"] = { caltBuildup: [] };
|
||||
for (const gr in data.simple) {
|
||||
|
@ -14,12 +19,10 @@ exports.applyLigationData = function (data, para, argv) {
|
|||
for (const gr in data.composite) {
|
||||
const comp = data.composite[gr];
|
||||
if (!comp.tag) continue;
|
||||
|
||||
const ligSets = createBuildup(data.simple, comp.buildup);
|
||||
defaultBuildup[comp.tag] = ligSets;
|
||||
hives[`ligset-inherit-${gr}`] = { caltBuildup: ligSets };
|
||||
}
|
||||
|
||||
para.ligation = {
|
||||
defaultBuildup,
|
||||
caltBuildup: []
|
||||
|
@ -41,12 +44,3 @@ exports.applyLigationData = function (data, para, argv) {
|
|||
);
|
||||
}
|
||||
};
|
||||
|
||||
function createBuildup(simple, buildup) {
|
||||
let ligSet = new Set();
|
||||
for (const s of buildup) {
|
||||
if (!simple[s]) throw new Error("Cannot find simple ligation group " + s);
|
||||
ligSet.add(simple[s].ligGroup);
|
||||
}
|
||||
return Array.from(ligSet);
|
||||
}
|
|
@ -1,8 +1,5 @@
|
|||
"use strict";
|
||||
import { monotonicInterpolate } from "./util/monotonic-interpolate.mjs";
|
||||
|
||||
const { monotonicInterpolate } = require("./util/monotonic-interpolate");
|
||||
|
||||
exports.applyMetricOverride = applyMetricOverride;
|
||||
function applyMetricOverride(para, mo, argv) {
|
||||
const bindings = initBindings(para, argv);
|
||||
for (const [field, expr] of Object.entries(mo)) {
|
||||
|
@ -22,7 +19,6 @@ function applyMetricOverride(para, mo, argv) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const validMetricOverrideFields = new Set([
|
||||
"cap",
|
||||
"ascender",
|
||||
|
@ -51,10 +47,8 @@ const validMetricOverrideFields = new Set([
|
|||
"archDepth",
|
||||
"smallArchDepth"
|
||||
]);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Function bindings
|
||||
|
||||
function initBindings(para, argv) {
|
||||
const valueBindings = new Map();
|
||||
for (const k of validMetricOverrideFields) {
|
||||
|
@ -63,12 +57,10 @@ function initBindings(para, argv) {
|
|||
valueBindings.set("weight", argv.shape.weight);
|
||||
valueBindings.set("width", argv.shape.width);
|
||||
valueBindings.set("slopeAngle", argv.shape.slopeAngle);
|
||||
|
||||
const functionBindings = new Map();
|
||||
functionBindings.set("blend", blend);
|
||||
return { val: valueBindings, functions: functionBindings };
|
||||
}
|
||||
|
||||
function blend(against, ...pairs) {
|
||||
const xs = [],
|
||||
ys = [];
|
||||
|
@ -77,7 +69,6 @@ function blend(against, ...pairs) {
|
|||
}
|
||||
return monotonicInterpolate(xs, ys)(against);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Simple expression parser
|
||||
class State {
|
||||
|
@ -108,7 +99,6 @@ class State {
|
|||
throw new SyntaxError("Failed to parse expression: " + this.input + "@" + this.cp);
|
||||
}
|
||||
}
|
||||
|
||||
function RootExpression(state, bindings) {
|
||||
const e = Expression(state, bindings);
|
||||
state.expectEnd();
|
||||
|
@ -120,7 +110,6 @@ function Expression(state, bindings) {
|
|||
skipSpaces(state);
|
||||
return e;
|
||||
}
|
||||
|
||||
function Sum(state, bindings) {
|
||||
let f = Term(state, bindings);
|
||||
skipSpaces(state);
|
||||
|
@ -159,7 +148,6 @@ function Term(state, bindings) {
|
|||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
function Factor(state, bindings) {
|
||||
if (state.test("+")) {
|
||||
state.advance();
|
||||
|
@ -173,7 +161,6 @@ function Factor(state, bindings) {
|
|||
return Primitive(state, bindings);
|
||||
}
|
||||
}
|
||||
|
||||
function Primitive(state, bindings) {
|
||||
if (state.testCk(isDigit)) return Lit(state, bindings);
|
||||
if (state.testCk(isAlpha)) return BindingOrCall(state, bindings);
|
||||
|
@ -181,7 +168,6 @@ function Primitive(state, bindings) {
|
|||
if (state.test("[")) return List("[", "]", state, bindings);
|
||||
state.fail();
|
||||
}
|
||||
|
||||
function Lit(state, bindings) {
|
||||
let integerPart = 0;
|
||||
let fractionPart = 0;
|
||||
|
@ -200,7 +186,6 @@ function Lit(state, bindings) {
|
|||
}
|
||||
return integerPart + fractionPart;
|
||||
}
|
||||
|
||||
function BindingOrCall(state, bindings) {
|
||||
let symbolName = "";
|
||||
while (state.testCk(isAlpha)) symbolName += state.advance();
|
||||
|
@ -213,7 +198,6 @@ function BindingOrCall(state, bindings) {
|
|||
else throw new TypeError(`Unknown identifier ${symbolName}.`);
|
||||
}
|
||||
}
|
||||
|
||||
function Group(state, bindings) {
|
||||
state.expectAndAdvance("(");
|
||||
skipSpaces(state);
|
||||
|
@ -237,11 +221,9 @@ function List(start, end, state, bindings) {
|
|||
state.expectAndAdvance(end);
|
||||
return results;
|
||||
}
|
||||
|
||||
function skipSpaces(state) {
|
||||
while (state.testCk(isSpace)) state.advance();
|
||||
}
|
||||
|
||||
function isSpace(ch) {
|
||||
return ch === " " || ch === "\t";
|
||||
}
|
||||
|
@ -251,3 +233,4 @@ function isDigit(ch) {
|
|||
function isAlpha(ch) {
|
||||
return (ch >= "A" && ch <= "Z") || (ch >= "a" && ch <= "z") || ch === "_";
|
||||
}
|
||||
export { applyMetricOverride };
|
|
@ -1,24 +1,18 @@
|
|||
"use strict";
|
||||
import { monotonicInterpolate } from "./util/monotonic-interpolate.mjs";
|
||||
|
||||
const { monotonicInterpolate } = require("./util/monotonic-interpolate");
|
||||
|
||||
exports.init = initPara;
|
||||
function initPara(data, argv) {
|
||||
let para = {};
|
||||
apply(para, data, ["iosevka"]);
|
||||
if (argv.shape.serifs) apply(para, data, ["serifs-" + argv.shape.serifs]);
|
||||
if (argv.shape.spacing) apply(para, data, ["spacing-" + argv.shape.spacing]);
|
||||
|
||||
applyBlendingParam(argv, para, data, "shapeWeight", "weight");
|
||||
applyBlendingParam(argv, para, data, "shapeWidth", "width");
|
||||
applyBlendingParam(argv, para, data, "shapeSlopeAngle", "slopeAngle");
|
||||
applyAlternatesParam(argv, para, data, "slope", "slope");
|
||||
|
||||
if (argv.featureControl.noCvSs) para.enableCvSs = false;
|
||||
if (argv.featureControl.noLigation) para.enableLigation = false;
|
||||
return para;
|
||||
}
|
||||
|
||||
function applyBlendingParam(argv, para, data, key, keyArgv) {
|
||||
applySingleBlendingParam(argv, para, data, key, keyArgv);
|
||||
if (argv.shape.serifs)
|
||||
|
@ -35,19 +29,15 @@ function applyAlternatesParam(argv, para, data, key, keyArgv) {
|
|||
if (argv.shape.serifs) apply(para, data, [`${kBase}-serifs-${argv.shape.serifs}`]);
|
||||
if (argv.shape.spacing) apply(para, data, [`${kBase}-spacing-${argv.shape.spacing}`]);
|
||||
}
|
||||
|
||||
exports.apply = apply;
|
||||
function apply(sink, parametersData, styles, blendArgs) {
|
||||
if (!styles) return;
|
||||
for (const item of styles) intro(parametersData, item, blendArgs, sink);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
function intro(source, style, blendArgs, sink) {
|
||||
let hive = source[style];
|
||||
if (!hive) return;
|
||||
hive = { ...hive };
|
||||
|
||||
if (hive.inherits) {
|
||||
for (const hn of hive.inherits) intro(source, hn, blendArgs, sink);
|
||||
delete hive.inherits;
|
||||
|
@ -75,24 +65,18 @@ function intro(source, style, blendArgs, sink) {
|
|||
}
|
||||
delete hive.removes;
|
||||
}
|
||||
|
||||
hive = hiveBlend(hive, getBlendArg(blendArgs, style));
|
||||
for (const k in hive) sink[k] = hive[k];
|
||||
}
|
||||
|
||||
function getBlendArg(blendArgs, style) {
|
||||
if (!blendArgs) return undefined;
|
||||
return blendArgs[style];
|
||||
}
|
||||
|
||||
function hiveBlend(hive, value) {
|
||||
if (!hive || !hive.blend || value == null) return hive;
|
||||
|
||||
const block = hive.blend;
|
||||
delete hive.blend;
|
||||
|
||||
const generatedHive = { ...hive };
|
||||
|
||||
let keys = new Set();
|
||||
for (const grade in block) {
|
||||
if (!isFinite(parseFloat(grade))) continue;
|
||||
|
@ -101,7 +85,6 @@ function hiveBlend(hive, value) {
|
|||
keys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
let xs = [],
|
||||
ys = [];
|
||||
|
@ -115,3 +98,5 @@ function hiveBlend(hive, value) {
|
|||
}
|
||||
return generatedHive;
|
||||
}
|
||||
export { initPara as init };
|
||||
export { apply };
|
|
@ -1,5 +1,3 @@
|
|||
"use strict";
|
||||
|
||||
function struct(leader, ...items) {
|
||||
return "" + leader + "(" + items.join(";") + ")";
|
||||
}
|
||||
|
@ -9,21 +7,18 @@ function tuple(...items) {
|
|||
function list(items) {
|
||||
return "{" + items.join(";") + "}";
|
||||
}
|
||||
|
||||
function n(x) {
|
||||
return String(Math.round(x * 0x10000));
|
||||
}
|
||||
|
||||
function typedPoint(z) {
|
||||
return tuple(z.type, n(z.x), n(z.y));
|
||||
}
|
||||
function gizmo(g) {
|
||||
return tuple(n(g.xx), n(g.xy), n(g.yx), n(g.yy), n(g.x), n(g.y));
|
||||
}
|
||||
|
||||
exports.struct = struct;
|
||||
exports.tuple = tuple;
|
||||
exports.list = list;
|
||||
exports.n = n;
|
||||
exports.typedPoint = typedPoint;
|
||||
exports.gizmo = gizmo;
|
||||
export { struct };
|
||||
export { tuple };
|
||||
export { list };
|
||||
export { n };
|
||||
export { typedPoint };
|
||||
export { gizmo };
|
|
@ -1,14 +1,10 @@
|
|||
"use strict";
|
||||
|
||||
exports.maskBit = function maskBit(x, y) {
|
||||
return x & (1 << y);
|
||||
};
|
||||
|
||||
exports.maskBits = function maskBits(x, y) {
|
||||
return x & y;
|
||||
};
|
||||
|
||||
const pcNibbleLookup = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];
|
||||
exports.popCountByte = function (x) {
|
||||
export function maskBit(x, y) {
|
||||
return x & (1 << y);
|
||||
}
|
||||
export function maskBits(x, y) {
|
||||
return x & y;
|
||||
}
|
||||
export const popCountByte = function (x) {
|
||||
return pcNibbleLookup[x & 0x0f] + pcNibbleLookup[(x >>> 4) & 0x0f];
|
||||
};
|
|
@ -1,9 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
exports.monotonicInterpolate = function (xs, ys) {
|
||||
export const monotonicInterpolate = function (xs, ys) {
|
||||
let i,
|
||||
length = xs.length;
|
||||
|
||||
// Deal with length issues
|
||||
if (length != ys.length) {
|
||||
throw "Need an equal count of xs and ys.";
|
||||
|
@ -21,7 +18,6 @@ exports.monotonicInterpolate = function (xs, ys) {
|
|||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
// Rearrange xs and ys so that xs is sorted
|
||||
let indexes = [];
|
||||
for (i = 0; i < length; i++) {
|
||||
|
@ -40,7 +36,6 @@ exports.monotonicInterpolate = function (xs, ys) {
|
|||
xs.push(+oldXs[indexes[i]]);
|
||||
ys.push(+oldYs[indexes[i]]);
|
||||
}
|
||||
|
||||
// Get consecutive differences and slopes
|
||||
let dys = [],
|
||||
dxs = [],
|
||||
|
@ -52,7 +47,6 @@ exports.monotonicInterpolate = function (xs, ys) {
|
|||
dys.push(dy);
|
||||
ms.push(dy / dx);
|
||||
}
|
||||
|
||||
// Get degree-1 coefficients
|
||||
let c1s = [ms[0]];
|
||||
for (i = 0; i < dxs.length - 1; i++) {
|
||||
|
@ -68,7 +62,6 @@ exports.monotonicInterpolate = function (xs, ys) {
|
|||
}
|
||||
}
|
||||
c1s.push(ms[ms.length - 1]);
|
||||
|
||||
// Get degree-2 and degree-3 coefficients
|
||||
let c2s = [],
|
||||
c3s = [];
|
||||
|
@ -80,7 +73,6 @@ exports.monotonicInterpolate = function (xs, ys) {
|
|||
c2s.push((m - c1 - common) * invDx);
|
||||
c3s.push(common * invDx * invDx);
|
||||
}
|
||||
|
||||
// Return interpolant function
|
||||
return function (x) {
|
||||
// The rightmost point in the dataset should give an exact result
|
||||
|
@ -88,7 +80,6 @@ exports.monotonicInterpolate = function (xs, ys) {
|
|||
if (x == xs[i]) {
|
||||
return ys[i];
|
||||
}
|
||||
|
||||
// Search for the interval x is in, returning the corresponding y if x is one of the original xs
|
||||
let low = 0,
|
||||
mid,
|
||||
|
@ -105,7 +96,6 @@ exports.monotonicInterpolate = function (xs, ys) {
|
|||
}
|
||||
}
|
||||
i = Math.max(0, high);
|
||||
|
||||
// Interpolate
|
||||
let diff = x - xs[i],
|
||||
diffSq = diff * diff;
|
|
@ -1,5 +1,3 @@
|
|||
"use strict";
|
||||
|
||||
function mix(a, b, p) {
|
||||
return a + (b - a) * p;
|
||||
}
|
||||
|
@ -33,13 +31,12 @@ function bez3(a, b, c, d, t) {
|
|||
t * t * t * d
|
||||
);
|
||||
}
|
||||
|
||||
exports.mix = mix;
|
||||
exports.barmixL = barmixL;
|
||||
exports.barmixM = barmixM;
|
||||
exports.barmixR = barMixR;
|
||||
exports.linreg = linreg;
|
||||
exports.clamp = clamp;
|
||||
exports.fallback = fallback;
|
||||
exports.bez2 = bez2;
|
||||
exports.bez3 = bez3;
|
||||
export { mix };
|
||||
export { barmixL };
|
||||
export { barmixM };
|
||||
export { barMixR as barmixR };
|
||||
export { linreg };
|
||||
export { clamp };
|
||||
export { fallback };
|
||||
export { bez2 };
|
||||
export { bez3 };
|
|
@ -1,6 +1,3 @@
|
|||
"use strict";
|
||||
|
||||
exports.apply = applyVariantData;
|
||||
function applyVariantData(data, para, argv) {
|
||||
const parsed = parseVariantsData(data, argv);
|
||||
let tagSet = new Set();
|
||||
|
@ -9,7 +6,6 @@ function applyVariantData(data, para, argv) {
|
|||
if (!tagSet.has(prime.tag)) tagSet.add(prime.tag);
|
||||
else throw new Error(`CV tag conflict: ${prime.tag}`);
|
||||
}
|
||||
|
||||
const variantSelector = {};
|
||||
parsed.defaultComposite.resolve(para, parsed.selectorTree, parsed.composites, variantSelector);
|
||||
if (argv.shape.serifs === "slab") {
|
||||
|
@ -27,8 +23,6 @@ function applyVariantData(data, para, argv) {
|
|||
};
|
||||
para.variantSelector = variantSelector;
|
||||
}
|
||||
|
||||
exports.parse = parseVariantsData;
|
||||
function parseVariantsData(data, argv) {
|
||||
const primes = new Map();
|
||||
const selectorTree = new SelectorTree();
|
||||
|
@ -37,7 +31,6 @@ function parseVariantsData(data, argv) {
|
|||
p.register(selectorTree);
|
||||
primes.set(k, p);
|
||||
}
|
||||
|
||||
const defaultComposite = new Composite("{default}", data.default);
|
||||
const composites = new Map();
|
||||
for (const k in data.composite) {
|
||||
|
@ -53,7 +46,6 @@ function parseVariantsData(data, argv) {
|
|||
}
|
||||
return { selectorTree: selectorTree, primes, composites, defaultComposite };
|
||||
}
|
||||
|
||||
class SelectorTree {
|
||||
constructor() {
|
||||
this.m_mapping = new Map();
|
||||
|
@ -70,7 +62,6 @@ class SelectorTree {
|
|||
for (const m of this.m_mapping.values()) yield* m.values();
|
||||
}
|
||||
}
|
||||
|
||||
class Prime {
|
||||
constructor(key, cfg) {
|
||||
if (!cfg.variants) throw new Error(`Missing variants in ${key}`);
|
||||
|
@ -98,7 +89,6 @@ class Prime {
|
|||
for (const v of this.variants.values()) if (v.rank) tree.set(this.tag, v.rank, this, v);
|
||||
}
|
||||
}
|
||||
|
||||
toJson() {
|
||||
const gr = {
|
||||
key: this.key,
|
||||
|
@ -126,7 +116,6 @@ class Prime {
|
|||
return gr;
|
||||
}
|
||||
}
|
||||
|
||||
class PrimeVariant {
|
||||
constructor(key, tag, cfg) {
|
||||
this.key = key;
|
||||
|
@ -147,7 +136,6 @@ class PrimeVariant {
|
|||
Object.assign(vs, this.selector);
|
||||
}
|
||||
}
|
||||
|
||||
class Composite {
|
||||
constructor(key, cfg) {
|
||||
this.key = key;
|
||||
|
@ -158,7 +146,6 @@ class Composite {
|
|||
this.upright = cfg.upright || cfg["upright-oblique"];
|
||||
this.oblique = cfg.oblique || cfg["upright-oblique"];
|
||||
this.italic = cfg.italic;
|
||||
|
||||
const slabOverrideCfg = cfg["slab-override"] || {};
|
||||
this.slabOverride = {
|
||||
design: slabOverrideCfg.design,
|
||||
|
@ -167,7 +154,6 @@ class Composite {
|
|||
italic: slabOverrideCfg.italic
|
||||
};
|
||||
}
|
||||
|
||||
decompose(para, selTree) {
|
||||
const ans = [];
|
||||
const cfg = Object.assign(
|
||||
|
@ -199,3 +185,5 @@ class Composite {
|
|||
}
|
||||
}
|
||||
}
|
||||
export { applyVariantData as apply };
|
||||
export { parseVariantsData as parse };
|
Loading…
Add table
Add a link
Reference in a new issue