Iosevka/font-src/support/geometry.js
2021-02-27 21:01:20 -08:00

188 lines
3.9 KiB
JavaScript

"use strict";
const Point = require("./point");
const Transform = require("./transform");
class GeometryBase {
asContours() {
throw new Error("Unimplemented");
}
asReferences() {
throw new Error("Unimplemented");
}
filterTag(fn) {
return this;
}
isEmpty() {
return true;
}
}
class ContourGeometry extends GeometryBase {
constructor(points) {
super();
this.m_points = points;
}
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_geom.asContours()) {
let c1 = [];
for (const z of c) c1.push(Point.transformed(this.m_transform, z));
result.push(c1);
}
return result;
}
asReferences() {
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 });
return result;
}
filterTag(fn) {
return new TransformedGeometry(this.m_geom.filterTag(fn), this.m_transform);
}
isEmpty() {
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;