Refactor into geometry tree
This commit is contained in:
parent
f17710b0a4
commit
a81c477fab
6 changed files with 207 additions and 121 deletions
|
@ -1,107 +1,188 @@
|
|||
"use strict";
|
||||
|
||||
const Transform = require("./transform");
|
||||
const Point = require("./point");
|
||||
const Anchor = require("./anchor");
|
||||
const Transform = require("./transform");
|
||||
|
||||
module.exports = class GeometryStore {
|
||||
constructor() {
|
||||
this.m_contours = [];
|
||||
this.m_references = [];
|
||||
class GeometryBase {
|
||||
asContours() {
|
||||
throw new Error("Unimplemented");
|
||||
}
|
||||
asReferences() {
|
||||
throw new Error("Unimplemented");
|
||||
}
|
||||
filterTag(fn) {
|
||||
return this;
|
||||
}
|
||||
isEmpty() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
addContour(c, tag) {
|
||||
if (tag) {
|
||||
const zs = [...c];
|
||||
zs.tag = tag;
|
||||
this.m_contours.push(zs);
|
||||
} else {
|
||||
this.m_contours.push(c);
|
||||
}
|
||||
class ContourGeometry extends GeometryBase {
|
||||
constructor(points) {
|
||||
super();
|
||||
this.m_points = points;
|
||||
}
|
||||
addReference(glyph, x, y) {
|
||||
this.m_references.push({ glyph, x, y });
|
||||
asContours() {
|
||||
if (this.isEmpty()) return [];
|
||||
else return [this.m_points];
|
||||
}
|
||||
asReferences() {
|
||||
return null;
|
||||
}
|
||||
filterTag(fn) {
|
||||
return this;
|
||||
}
|
||||
isEmpty() {
|
||||
return !this.m_points.length;
|
||||
}
|
||||
}
|
||||
|
||||
class ReferenceGeometry extends GeometryBase {
|
||||
constructor(glyph, x, y) {
|
||||
super();
|
||||
if (!glyph || !glyph.geometry) throw new TypeError("Invalid glyph");
|
||||
this.m_glyph = glyph;
|
||||
this.m_x = x;
|
||||
this.m_y = y;
|
||||
}
|
||||
unwrap() {
|
||||
return new TransformedGeometry(
|
||||
this.m_glyph.geometry,
|
||||
Transform.Translate(this.m_x, this.m_y)
|
||||
);
|
||||
}
|
||||
asContours() {
|
||||
if (this.isEmpty()) return [];
|
||||
return this.unwrap().asContours();
|
||||
}
|
||||
asReferences() {
|
||||
if (this.isEmpty()) return [];
|
||||
return [{ glyph: this.m_glyph, x: this.m_x, y: this.m_y }];
|
||||
}
|
||||
filterTag(fn) {
|
||||
if (this.isEmpty()) return null;
|
||||
return this.unwrap().filterTag(fn);
|
||||
}
|
||||
isEmpty() {
|
||||
if (!this.m_glyph || !this.m_glyph.geometry) return true;
|
||||
return this.m_glyph.geometry.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
class TaggedGeometry extends GeometryBase {
|
||||
constructor(g, tag) {
|
||||
super();
|
||||
this.m_geom = g;
|
||||
this.m_tag = tag;
|
||||
}
|
||||
asContours() {
|
||||
return this.m_geom.asContours();
|
||||
}
|
||||
asReferences() {
|
||||
return this.m_geom.asReferences();
|
||||
}
|
||||
filterTag(fn) {
|
||||
if (!fn(this.m_tag)) return null;
|
||||
else return new TaggedGeometry(this.m_geom.filterTag(fn), this.m_tag);
|
||||
}
|
||||
isEmpty() {
|
||||
return this.m_geom.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
class TransformedGeometry extends GeometryBase {
|
||||
constructor(g, tfm) {
|
||||
super();
|
||||
this.m_geom = g;
|
||||
this.m_transform = tfm;
|
||||
}
|
||||
asContours() {
|
||||
let result = [];
|
||||
for (const c of this.m_contours) {
|
||||
const c1 = [...c];
|
||||
if (c.tag) c1.tag = c.tag;
|
||||
for (const c of this.m_geom.asContours()) {
|
||||
let c1 = [];
|
||||
for (const z of c) c1.push(Point.transformed(this.m_transform, z));
|
||||
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;
|
||||
}
|
||||
if (!Transform.isTranslate(this.m_transform)) return null;
|
||||
const rs = this.m_geom.asReferences();
|
||||
if (!rs) return null;
|
||||
|
||||
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;
|
||||
}
|
||||
let result = [];
|
||||
for (const { glyph, x, y } of rs)
|
||||
result.push({ glyph, x: x + this.m_transform.x, y: y + this.m_transform.y });
|
||||
return result;
|
||||
}
|
||||
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;
|
||||
filterTag(fn) {
|
||||
return new TransformedGeometry(this.m_geom.filterTag(fn), this.m_transform);
|
||||
}
|
||||
|
||||
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;
|
||||
return this.m_geom.isEmpty();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class CombineGeometry extends GeometryBase {
|
||||
constructor(parts) {
|
||||
super();
|
||||
this.m_parts = parts || [];
|
||||
}
|
||||
with(g) {
|
||||
if (g instanceof CombineGeometry) {
|
||||
return new CombineGeometry([...this.m_parts, ...g.m_parts]);
|
||||
} else {
|
||||
return new CombineGeometry([...this.m_parts, g]);
|
||||
}
|
||||
}
|
||||
asContours() {
|
||||
let results = [];
|
||||
for (const part of this.m_parts) {
|
||||
for (const c of part.asContours()) {
|
||||
results.push(c);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
asReferences() {
|
||||
let results = [];
|
||||
for (const part of this.m_parts) {
|
||||
const rs = part.asReferences();
|
||||
if (!rs) return null;
|
||||
for (const c of rs) {
|
||||
results.push(c);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
filterTag(fn) {
|
||||
let filtered = [];
|
||||
for (const part of this.m_parts) {
|
||||
const fp = part.filterTag(fn);
|
||||
if (fp) filtered.push(fp);
|
||||
}
|
||||
return new CombineGeometry(filtered);
|
||||
}
|
||||
isEmpty() {
|
||||
for (const part of this.m_parts) if (!part.isEmpty()) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function combineWith(a, b) {
|
||||
if (a instanceof CombineGeometry) {
|
||||
return a.with(b);
|
||||
} else {
|
||||
return new CombineGeometry([a, b]);
|
||||
}
|
||||
}
|
||||
|
||||
exports.GeometryBase = GeometryBase;
|
||||
exports.ContourGeometry = ContourGeometry;
|
||||
exports.ReferenceGeometry = ReferenceGeometry;
|
||||
exports.TaggedGeometry = TaggedGeometry;
|
||||
exports.TransformedGeometry = TransformedGeometry;
|
||||
exports.CombineGeometry = CombineGeometry;
|
||||
|
||||
exports.combineWith = combineWith;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue