Refactor geometry system
This commit is contained in:
parent
855a812758
commit
3fd1ebfec3
11 changed files with 208 additions and 159 deletions
|
@ -16,14 +16,7 @@ function finalizeGlyphs(para, glyphStore) {
|
|||
|
||||
function suppressNaN(glyphStore) {
|
||||
for (const g of glyphStore.glyphs()) {
|
||||
if (!g.contours) continue;
|
||||
for (let k = 0; k < g.contours.length; k++) {
|
||||
let contour = g.contours[k];
|
||||
for (let z of contour) {
|
||||
if (!isFinite(z.x)) z.x = 0;
|
||||
if (!isFinite(z.y)) z.y = 0;
|
||||
}
|
||||
}
|
||||
if (g.geometry) g.geometry.suppressNaN();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,32 +24,38 @@ function suppressNaN(glyphStore) {
|
|||
|
||||
function regulateGlyphStore(skew, glyphStore) {
|
||||
for (const g of glyphStore.glyphs()) {
|
||||
if (!g.semanticInclusions || !g.contours) continue;
|
||||
if (g.isPureComposite()) regulateCompositeGlyph(glyphStore, g);
|
||||
if (g.geometry.isEmpty()) continue;
|
||||
if (!regulateCompositeGlyph(glyphStore, g)) {
|
||||
const cs = g.geometry.asContours();
|
||||
g.clearGeometry();
|
||||
for (const c of cs) g.geometry.addContour(c);
|
||||
}
|
||||
}
|
||||
for (const g of glyphStore.glyphs()) {
|
||||
if (!g.geometry.asReferences()) regulateSimpleGlyph(g, skew);
|
||||
}
|
||||
for (const g of glyphStore.glyphs()) regulateSimpleGlyph(g, skew);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function regulateCompositeGlyph(glyphStore, g) {
|
||||
const references = [];
|
||||
for (const sr of g.semanticInclusions) {
|
||||
const refs = g.geometry.asReferences();
|
||||
if (!refs) return false;
|
||||
for (const sr of refs) {
|
||||
const gn = glyphStore.queryNameOf(sr.glyph);
|
||||
if (!gn || sr.glyph.autoRefPriority < 0) return;
|
||||
references.push({ glyph: gn, x: sr.x, y: sr.y });
|
||||
if (!gn || sr.glyph.autoRefPriority < 0) return false;
|
||||
}
|
||||
|
||||
g.semanticInclusions = [];
|
||||
g.contours = [];
|
||||
g.references = references;
|
||||
return true;
|
||||
}
|
||||
|
||||
function regulateSimpleGlyph(g, skew) {
|
||||
if (!g.contours || !g.contours.length) return;
|
||||
for (const contour of g.contours) for (const z of contour) z.x -= z.y * skew;
|
||||
g.contours = simplifyContours(g.contours);
|
||||
for (const contour of g.contours) for (const z of contour) z.x += z.y * skew;
|
||||
let cs = g.geometry.asContours();
|
||||
for (const contour of cs) for (const z of contour) z.x -= z.y * skew;
|
||||
cs = simplifyContours(cs);
|
||||
for (const contour of cs) for (const z of contour) z.x += z.y * skew;
|
||||
|
||||
g.clearGeometry();
|
||||
for (const c of cs) g.geometry.addContour(c);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -1,48 +1,58 @@
|
|||
const { Ot } = require("ot-builder");
|
||||
const Point = require("../../support/point");
|
||||
|
||||
class NamedGlyphStore {
|
||||
class MappedGlyphStore {
|
||||
constructor() {
|
||||
this.m_nameMapping = new Map();
|
||||
this.m_mapping = new Map();
|
||||
}
|
||||
declare(name) {
|
||||
declare(name, source) {
|
||||
const g = new Ot.Glyph();
|
||||
g.name = name;
|
||||
this.m_mapping.set(name, g);
|
||||
this.m_nameMapping.set(name, g);
|
||||
this.m_mapping.set(source, g);
|
||||
}
|
||||
query(name) {
|
||||
return this.m_mapping.get(name);
|
||||
queryBySourceGlyph(source) {
|
||||
return this.m_mapping.get(source);
|
||||
}
|
||||
queryByName(name) {
|
||||
return this.m_nameMapping.get(name);
|
||||
}
|
||||
|
||||
decideOrder() {
|
||||
const gs = Ot.ListGlyphStoreFactory.createStoreFromList([...this.m_mapping.values()]);
|
||||
return gs.decideOrder();
|
||||
}
|
||||
fill(name, data) {
|
||||
const g = this.query(name);
|
||||
if (!g) return;
|
||||
fill(name, source) {
|
||||
const g = this.queryBySourceGlyph(source);
|
||||
if (!g) throw new Error("Unreachable");
|
||||
|
||||
g.horizontal = { start: 0, end: data.advanceWidth };
|
||||
if (data.references && data.references.length) {
|
||||
this.fillReferences(g, data);
|
||||
} else if (data.contours && data.contours.length) {
|
||||
this.fillContours(g, data);
|
||||
// Fill metrics
|
||||
g.horizontal = { start: 0, end: source.advanceWidth };
|
||||
|
||||
// Fill Geometry
|
||||
if (source.geometry.isEmpty()) return;
|
||||
const rs = source.geometry.asReferences();
|
||||
if (rs) {
|
||||
this.fillReferences(g, rs);
|
||||
} else {
|
||||
this.fillContours(g, source.geometry.asContours());
|
||||
}
|
||||
}
|
||||
|
||||
fillReferences(g, data) {
|
||||
fillReferences(g, rs) {
|
||||
const gl = new Ot.Glyph.GeometryList();
|
||||
for (const ref of data.references) {
|
||||
const target = this.query(ref.glyph);
|
||||
if (!target) continue;
|
||||
for (const ref of rs) {
|
||||
const target = this.queryBySourceGlyph(ref.glyph);
|
||||
if (!target) throw new Error("Unreachable");
|
||||
const tfm = Ot.Glyph.Transform2X3.Translate(ref.x, ref.y);
|
||||
gl.items.push(new Ot.Glyph.TtReference(target, tfm));
|
||||
}
|
||||
g.geometry = gl;
|
||||
}
|
||||
fillContours(g, data) {
|
||||
fillContours(g, contours) {
|
||||
const cs = new Ot.Glyph.ContourSet();
|
||||
for (const c of data.contours) {
|
||||
for (const c of contours) {
|
||||
const c1 = [];
|
||||
for (const z of c) {
|
||||
c1.push(
|
||||
|
@ -67,21 +77,21 @@ function convertGlyphs(gsOrig) {
|
|||
.map(([j, gn, g]) => [j, gn, queryOrderingUnicode(gsOrig, g), g])
|
||||
.sort(byRank);
|
||||
|
||||
const gs = new NamedGlyphStore();
|
||||
const gs = new MappedGlyphStore();
|
||||
const cmap = new Ot.Cmap.Table();
|
||||
|
||||
for (const [origIndex, name, uOrd, g] of sortedEntries) {
|
||||
gs.declare(name);
|
||||
const us = gsOrig.queryUnicodeOf(g);
|
||||
for (const [origIndex, name, uOrd, gSrc] of sortedEntries) {
|
||||
gs.declare(name, gSrc);
|
||||
const us = gsOrig.queryUnicodeOf(gSrc);
|
||||
if (us) {
|
||||
for (const u of us) {
|
||||
if (isFinite(u - 0) && u) {
|
||||
cmap.unicode.set(u, gs.query(name));
|
||||
cmap.unicode.set(u, gs.queryBySourceGlyph(gSrc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const [origIndex, name, uOrd, g] of sortedEntries) gs.fill(name, g);
|
||||
for (const [origIndex, name, uOrd, gSrc] of sortedEntries) gs.fill(name, gSrc);
|
||||
|
||||
return { glyphs: gs, cmap };
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@ const GsubSingleHandler = {
|
|||
fill(dst, src, store) {
|
||||
const st = src.substitutions;
|
||||
for (const k in st) {
|
||||
const from = store.glyphs.query(k);
|
||||
const to = store.glyphs.query(st[k]);
|
||||
const from = store.glyphs.queryByName(k);
|
||||
const to = store.glyphs.queryByName(st[k]);
|
||||
if (from && to) dst.mapping.set(from, to);
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ const GsubMultipleHandler = {
|
|||
fill(dst, src, store) {
|
||||
const st = src.substitutions;
|
||||
for (const k in st) {
|
||||
const from = store.glyphs.query(k);
|
||||
const from = store.glyphs.queryByName(k);
|
||||
const to = mapGlyphListAll(st[k], store);
|
||||
if (!from || !to) continue;
|
||||
dst.mapping.set(from, to);
|
||||
|
@ -68,7 +68,7 @@ const GsubLigatureHandler = {
|
|||
fill(dst, src, store) {
|
||||
const st = src.substitutions;
|
||||
for (const { from: _from, to: _to } of st) {
|
||||
const to = store.glyphs.query(_to);
|
||||
const to = store.glyphs.queryByName(_to);
|
||||
const from = mapGlyphListAll(_from, store);
|
||||
if (!from || !to) continue;
|
||||
dst.mapping.push({ from, to });
|
||||
|
@ -114,7 +114,7 @@ const GsubReverseHandler = {
|
|||
{
|
||||
const m1 = new Set();
|
||||
for (let k = 0; k < st.match[j].length; k++) {
|
||||
const gFrom = store.glyphs.query(st.match[j][k]);
|
||||
const gFrom = store.glyphs.queryByName(st.match[j][k]);
|
||||
if (gFrom) m1.add(gFrom);
|
||||
}
|
||||
if (!m1.size) continue out;
|
||||
|
@ -123,8 +123,8 @@ const GsubReverseHandler = {
|
|||
|
||||
if (j === doSubAt) {
|
||||
for (let k = 0; k < st.match[j].length; k++) {
|
||||
const gFrom = store.glyphs.query(st.match[j][k]);
|
||||
const gTo = store.glyphs.query(st.to[k]);
|
||||
const gFrom = store.glyphs.queryByName(st.match[j][k]);
|
||||
const gTo = store.glyphs.queryByName(st.to[k]);
|
||||
if (!gFrom) continue;
|
||||
if (gTo) {
|
||||
replacement.set(gFrom, gTo);
|
||||
|
@ -142,7 +142,7 @@ const GsubReverseHandler = {
|
|||
function mapGlyphListAll(gl, store) {
|
||||
const out = [];
|
||||
for (const item of gl) {
|
||||
const fg = store.glyphs.query(item);
|
||||
const fg = store.glyphs.queryByName(item);
|
||||
if (!fg) return null;
|
||||
out.push(fg);
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ function mapGlyphListAll(gl, store) {
|
|||
function mapGlyphListSome(gl, store) {
|
||||
const out = [];
|
||||
for (const item of gl) {
|
||||
const fg = store.glyphs.query(item);
|
||||
const fg = store.glyphs.queryByName(item);
|
||||
if (!fg) continue;
|
||||
out.push(fg);
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ function convertMarkRecords(marks, mm, store) {
|
|||
const out = new Map();
|
||||
for (const gn in marks) {
|
||||
const mark = marks[gn];
|
||||
const g = store.glyphs.query(gn);
|
||||
const g = store.glyphs.queryByName(gn);
|
||||
if (!g) continue;
|
||||
let markAnchors = [];
|
||||
markAnchors[mm.get(mark.class)] = { x: mark.x, y: mark.y };
|
||||
|
@ -216,7 +216,7 @@ function convertBaseRecords(bases, mm, store) {
|
|||
const out = new Map();
|
||||
for (const gn in bases) {
|
||||
const baseObj = bases[gn];
|
||||
const g = store.glyphs.query(gn);
|
||||
const g = store.glyphs.queryByName(gn);
|
||||
if (!g) continue;
|
||||
const baseArray = [];
|
||||
for (const bkStr in baseObj) {
|
||||
|
@ -319,7 +319,7 @@ function convertGdef(otdGdef, glyphs) {
|
|||
const gdef = new Ot.Gdef.Table();
|
||||
gdef.glyphClassDef = new Map();
|
||||
for (const gn in otdGdef.glyphClassDef) {
|
||||
const g = glyphs.query(gn);
|
||||
const g = glyphs.queryByName(gn);
|
||||
if (g) gdef.glyphClassDef.set(g, otdGdef.glyphClassDef[gn]);
|
||||
}
|
||||
return gdef;
|
||||
|
|
|
@ -89,14 +89,9 @@ export all : define [buildGlyphs para recursive recursiveCodes] : begin
|
|||
|
||||
define [warnAboutBrokenGlyph g ensuredGlyphName saveGlyphName] : begin
|
||||
local complexity 0
|
||||
local broken false
|
||||
if g.contours : begin
|
||||
foreach [c : items-of g.contours] : foreach [z : items-of c] : begin
|
||||
inc complexity
|
||||
if [not : isFinite z.x] : set broken true
|
||||
if [not : isFinite z.y] : set broken true
|
||||
if g.geometry : set complexity : g.geometry.suppressNaN
|
||||
|
||||
if ([not recursive] && (broken || complexity > 4096)) : begin
|
||||
if ([not recursive] && complexity > 4096) : begin
|
||||
console.log 'Possible broken shape found in' ensuredGlyphName 'Complexity' complexity
|
||||
console.log 'Family' para.naming.family para.naming.weight para.naming.width para.naming.slope
|
||||
if saveGlyphName : throw : new Error "Overcomplicated \(saveGlyphName)"
|
||||
|
|
|
@ -221,7 +221,7 @@ glyph-block Marks : begin
|
|||
|
||||
define cs : new BezToContoursSink
|
||||
ShapeConv.transferGenericShapeAsBezier {{inner outer}} cs GEOMETRY_PRECISION
|
||||
currentGlyph.includeGeometry cs 0 0
|
||||
currentGlyph.includeContours cs.contours 0 0
|
||||
|
||||
create-glyph 'tildeAbove' 0x303 : glyph-proc
|
||||
set-width 0
|
||||
|
|
|
@ -47,7 +47,7 @@ glyph-block Symbol-Geometric-Plain : for-width-kinds WideWidth1
|
|||
begin 0
|
||||
local outlines : glyph-proc : begin
|
||||
set this.gizmo : Translate 0 0
|
||||
foreach c [items-of sh.contours] : foreach j [range 0 c.length] : begin
|
||||
foreach c [items-of : sh.geometry.asContours] : foreach j [range 0 c.length] : begin
|
||||
local a c.[if j (j - 1) (c.length - 1)]
|
||||
local b c.(j)
|
||||
include : dispiro
|
||||
|
|
|
@ -21,7 +21,7 @@ glyph-block Symbol-Math-APL : begin
|
|||
|
||||
local corners : new-glyph : glyph-proc
|
||||
set this.gizmo : Translate 0 0
|
||||
foreach [c : items-of overlay.contours] : foreach [z : items-of c] : do
|
||||
foreach [c : items-of : overlay.geometry.asContours] : foreach [z : items-of c] : do
|
||||
if (z.type === Point.Type.Corner) : begin
|
||||
define x z.x
|
||||
define y z.y
|
||||
|
|
|
@ -29,7 +29,7 @@ export : define [SetupBuilders args] : begin
|
|||
local g1 : new Glyph
|
||||
set g1.gizmo : g.gizmo || GlobalTransform
|
||||
g1.include p
|
||||
return : CurveUtil.convertShapeToArcs g1.contours
|
||||
return : CurveUtil.convertShapeToArcs : g1.geometry.asContours
|
||||
|
||||
define union : Boole TypoGeom.Boolean.ClipType.ctUnion
|
||||
define intersection : Boole TypoGeom.Boolean.ClipType.ctIntersection
|
||||
|
|
|
@ -206,14 +206,14 @@ export : define [SetupBuilders args] : begin
|
|||
set g.knots knots
|
||||
set g.lhsKnots lhs
|
||||
set g.rhsKnots rhs
|
||||
this.includeGeometry g 0 0
|
||||
this.includeContours g.contours 0 0
|
||||
return g
|
||||
|
||||
define [spiro-outline] : let [k : {}.slice.call arguments 0] : lambda [] : begin
|
||||
local g : new CurveUtil.BezToContoursSink (this.gizmo || GlobalTransform)
|
||||
local { .knots knots .closed closed } : prepareSpiroKnots k g
|
||||
convertSpiroToBezier knots closed g
|
||||
this.includeGeometry g 0 0
|
||||
this.includeContours g.contours 0 0
|
||||
return g
|
||||
|
||||
return [object
|
||||
|
|
|
@ -70,7 +70,7 @@ define-macro set-mark-anchor : syntax-rules
|
|||
define-macro set-base-anchor : syntax-rules
|
||||
`[set-base-anchor @::args] {'.syntactic-closure' `[currentGlyph.setBaseAnchor @::args] env}
|
||||
define-macro eject-contour : syntax-rules
|
||||
`[eject-contour @::args] {'.syntactic-closure' `[currentGlyph.ejectContour @::args] env}
|
||||
`[eject-contour @::args] {'.syntactic-closure' `[currentGlyph.geometry.ejectContour @::args] env}
|
||||
|
||||
###### Canvas-based mechanism
|
||||
define-macro new-glyph : syntax-rules
|
||||
|
|
|
@ -4,19 +4,122 @@ const Transform = require("./transform");
|
|||
const Point = require("./point");
|
||||
const Anchor = require("./anchor");
|
||||
|
||||
class GeometryStore {
|
||||
constructor() {
|
||||
this.m_contours = [];
|
||||
this.m_references = [];
|
||||
}
|
||||
|
||||
addContour(c) {
|
||||
this.m_contours.push(c);
|
||||
}
|
||||
addReference(glyph, x, y) {
|
||||
this.m_references.push({ glyph, x, y });
|
||||
}
|
||||
asContours() {
|
||||
let result = [];
|
||||
for (const c of this.m_contours) {
|
||||
const c1 = [...c];
|
||||
if (c.tag) c1.tag = c.tag;
|
||||
result.push(c1);
|
||||
}
|
||||
for (const r of this.m_references) {
|
||||
for (const c of r.glyph.geometry.asContours()) {
|
||||
let c1 = [];
|
||||
for (const z of c) c1.push(Point.fromXY(z.type, z.x + r.x, z.y + r.y));
|
||||
if (c.tag) c1.tag = c.tag;
|
||||
result.push(c1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
asReferences() {
|
||||
if (this.m_contours && this.m_contours.length) return null;
|
||||
if (!this.m_references.length) return null;
|
||||
return this.m_references;
|
||||
}
|
||||
|
||||
applyTranslate(shiftX, shiftY) {
|
||||
for (const c of this.m_contours) {
|
||||
for (let k = 0; k < c.length; k++) {
|
||||
c[k] = Point.translated(c[k], shiftX, shiftY);
|
||||
}
|
||||
}
|
||||
for (const r of this.m_references) {
|
||||
r.x += shiftX;
|
||||
r.y += shiftY;
|
||||
}
|
||||
}
|
||||
applyTransform(tfm) {
|
||||
const cs = this.asContours();
|
||||
for (const c of cs) {
|
||||
for (let k = 0; k < c.length; k++) {
|
||||
c[k] = Point.transformed(tfm, c[k]);
|
||||
}
|
||||
}
|
||||
this.m_contours = cs;
|
||||
this.m_references.length = 0;
|
||||
}
|
||||
|
||||
reTagContour(oldTag, newTag) {
|
||||
for (const c of this.m_contours) {
|
||||
if (c.tag === oldTag) c.tag = newTag;
|
||||
}
|
||||
}
|
||||
ejectContour(tag) {
|
||||
const cs = this.asContours();
|
||||
let i = 0,
|
||||
j = 0;
|
||||
for (; i < cs.length; i++) if (!cs[i].tag || cs[i].tag !== tag) cs[j++] = cs[i];
|
||||
cs.length = j;
|
||||
this.m_contours = cs;
|
||||
this.m_references = [];
|
||||
}
|
||||
|
||||
suppressNaN() {
|
||||
let broken = false,
|
||||
complexity = 0;
|
||||
for (const c of this.m_contours) {
|
||||
for (const z of c) {
|
||||
complexity++;
|
||||
if (!isFinite(z.x)) {
|
||||
broken = true;
|
||||
z.x = 0;
|
||||
}
|
||||
if (!isFinite(z.y)) {
|
||||
broken = true;
|
||||
z.y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return broken ? 0xffff : complexity;
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return !this.m_contours.length && !this.m_references.length;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = class Glyph {
|
||||
constructor(_identifier) {
|
||||
this._m_identifier = _identifier;
|
||||
this.contours = [];
|
||||
this.geometry = new GeometryStore();
|
||||
this.advanceWidth = 500;
|
||||
this.autoRefPriority = 0;
|
||||
this.markAnchors = {};
|
||||
this.baseAnchors = {};
|
||||
this.gizmo = Transform.Id();
|
||||
this.semanticInclusions = [];
|
||||
this.dependencies = [];
|
||||
this.defaultTag = 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");
|
||||
}
|
||||
|
@ -40,20 +143,6 @@ module.exports = class Glyph {
|
|||
if (glyph._m_identifier) this.dependencies.push(glyph._m_identifier);
|
||||
if (glyph.dependencies) for (const dep of glyph.dependencies) this.dependencies.push(dep);
|
||||
}
|
||||
// Contour Tagging
|
||||
reTagContour(oldTag, newTag) {
|
||||
for (const c of this.contours) if (c.tag === oldTag) c.tag = newTag;
|
||||
}
|
||||
ejectContour(tag) {
|
||||
let i = 0,
|
||||
j = 0;
|
||||
for (; i < this.contours.length; i++) {
|
||||
if (!this.contours[i].tag || this.contours[i].tag !== tag)
|
||||
this.contours[j++] = this.contours[i];
|
||||
}
|
||||
this.contours.length = j;
|
||||
this.semanticInclusions = [];
|
||||
}
|
||||
// Inclusion
|
||||
include(component, copyAnchors, copyWidth) {
|
||||
if (!component) {
|
||||
|
@ -105,56 +194,26 @@ module.exports = class Glyph {
|
|||
this.avoidBeingComposite = g.avoidBeingComposite;
|
||||
}
|
||||
|
||||
isPureComposite() {
|
||||
if (!this.semanticInclusions || !this.semanticInclusions.length) return false;
|
||||
const origContourSet = new Set(this.contours);
|
||||
let handledContours = new Set();
|
||||
for (const sr of this.semanticInclusions) {
|
||||
for (const c of sr.contours) {
|
||||
if (!origContourSet.has(c) || handledContours.has(c)) return false;
|
||||
handledContours.add(c);
|
||||
}
|
||||
}
|
||||
for (const c of this.contours) if (!handledContours.has(c)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
includeGlyphImpl(g, shiftX, shiftY) {
|
||||
if (g._m_identifier) {
|
||||
this.includeGlyphComponentImpl(g, shiftX, shiftY);
|
||||
} else if (!g._m_identifier && g.isPureComposite()) {
|
||||
for (const sr of g.semanticInclusions)
|
||||
this.includeGlyphComponentImpl(sr.glyph, sr.x + shiftX, sr.y + shiftY);
|
||||
if (!g.geometry.isEmpty()) this.geometry.addReference(g, shiftX, shiftY);
|
||||
} else if (!g._m_identifier && g.geometry.asReferences()) {
|
||||
for (const sr of g.geometry.asReferences()) {
|
||||
if (!sr.glyph.geometry.isEmpty())
|
||||
this.geometry.addReference(sr.glyph, sr.x + shiftX, sr.y + shiftY);
|
||||
}
|
||||
} else {
|
||||
this.includeGeometry(g, shiftX, shiftY);
|
||||
}
|
||||
}
|
||||
includeGlyphComponentImpl(g, shiftX, shiftY) {
|
||||
const newContours = this.includeGeometry(g, shiftX, shiftY);
|
||||
if (newContours && newContours.length) {
|
||||
this.semanticInclusions.push({
|
||||
glyph: g,
|
||||
x: shiftX,
|
||||
y: shiftY,
|
||||
contours: newContours
|
||||
});
|
||||
this.includeContours(g.geometry.asContours(), shiftX, shiftY);
|
||||
}
|
||||
}
|
||||
|
||||
includeGeometry(geom, shiftX, shiftY) {
|
||||
if (!geom || !geom.contours || !geom.contours.length) return null;
|
||||
return this.includeContours(geom.contours, shiftX, shiftY);
|
||||
}
|
||||
includeContours(contours, shiftX, shiftY) {
|
||||
let newContours = [];
|
||||
for (const contour of contours) {
|
||||
includeContours(cs, shiftX, shiftY) {
|
||||
for (const contour of cs) {
|
||||
let c = [];
|
||||
c.tag = contour.tag || contours.tag || this.defaultTag;
|
||||
c.tag = contour.tag || cs.tag || this.defaultTag;
|
||||
for (const z of contour) c.push(Point.translated(z, shiftX, shiftY));
|
||||
this.contours.push(c);
|
||||
newContours.push(c);
|
||||
this.geometry.addContour(c);
|
||||
}
|
||||
return newContours;
|
||||
}
|
||||
|
||||
combineAnchor(shift, baseThis, markThat, basesThat) {
|
||||
|
@ -175,19 +234,10 @@ module.exports = class Glyph {
|
|||
if (g.baseAnchors) for (const k in g.baseAnchors) this.baseAnchors[k] = g.baseAnchors[k];
|
||||
}
|
||||
applyTransform(tfm, alsoAnchors) {
|
||||
for (const c of this.contours) {
|
||||
for (let k = 0; k < c.length; k++) {
|
||||
c[k] = Point.transformed(tfm, c[k]);
|
||||
}
|
||||
}
|
||||
if (Transform.isTranslate(tfm)) {
|
||||
for (const sr of this.semanticInclusions) {
|
||||
sr.x += tfm.x;
|
||||
sr.y += tfm.y;
|
||||
}
|
||||
this.geometry.applyTranslate(tfm.x, tfm.y);
|
||||
} else {
|
||||
// Applying a non-trivial inclusion will unlink all the SIs
|
||||
this.semanticInclusions = [];
|
||||
this.geometry.applyTransform(tfm);
|
||||
}
|
||||
if (alsoAnchors) {
|
||||
for (const k in this.baseAnchors)
|
||||
|
@ -199,34 +249,29 @@ module.exports = class Glyph {
|
|||
|
||||
tryBecomeMirrorOf(dst, rankSet) {
|
||||
if (rankSet.has(this) || rankSet.has(dst)) return;
|
||||
if (this.contours.length !== dst.contours.length) return;
|
||||
for (let j = 0; j < this.contours.length; j++) {
|
||||
const c1 = this.contours[j],
|
||||
c2 = dst.contours[j];
|
||||
const csThis = this.geometry.asContours();
|
||||
const csDst = dst.geometry.asContours();
|
||||
if (csThis.length !== csDst.length) return;
|
||||
for (let j = 0; j < csThis.length; j++) {
|
||||
const c1 = csThis[j],
|
||||
c2 = csDst[j];
|
||||
if (c1.length !== c2.length) return;
|
||||
}
|
||||
for (let j = 0; j < this.contours.length; j++) {
|
||||
const c1 = this.contours[j],
|
||||
c2 = dst.contours[j];
|
||||
for (let j = 0; j < csThis.length; j++) {
|
||||
const c1 = csThis[j],
|
||||
c2 = csDst[j];
|
||||
for (let k = 0; k < c1.length; k++) {
|
||||
const z1 = c1[k],
|
||||
z2 = c2[k];
|
||||
if (z1.x !== z2.x || z1.y !== z2.y || z1.type !== z2.type) return;
|
||||
}
|
||||
}
|
||||
this.semanticInclusions = [
|
||||
{
|
||||
glyph: dst,
|
||||
x: 0,
|
||||
y: 0,
|
||||
contours: [...this.contours]
|
||||
}
|
||||
];
|
||||
this.geometry = new GeometryStore();
|
||||
this.geometry.addReference(dst, 0, 0);
|
||||
rankSet.add(this);
|
||||
}
|
||||
clearGeometry() {
|
||||
this.contours = [];
|
||||
this.semanticInclusions = [];
|
||||
this.geometry = new GeometryStore();
|
||||
}
|
||||
|
||||
// Anchors
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue