Move boolean composition out of kits.

This commit is contained in:
be5invis 2021-03-02 18:37:15 -08:00
parent 4cf5faf93c
commit 3ef9aaa2e2
7 changed files with 103 additions and 42 deletions

View file

@ -4,6 +4,7 @@ const TypoGeom = require("typo-geom");
const Point = require("./point");
const Transform = require("./transform");
exports.SPIRO_PRECISION = 1 / 2;
exports.GEOMETRY_PRECISION = 1 / 4;
exports.RECIP_GEOMETRY_PRECISION = 4;
exports.BOOLE_RESOLUTION = 0x4000;
@ -34,6 +35,18 @@ exports.OffsetCurve = class OffsetCurve {
}
};
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);
};

View file

@ -2,6 +2,8 @@
const Point = require("./point");
const Transform = require("./transform");
const CurveUtil = require("./curve-util");
const TypoGeom = require("typo-geom");
class GeometryBase {
asContours() {
@ -132,7 +134,9 @@ class TransformedGeometry extends GeometryBase {
return result;
}
filterTag(fn) {
return new TransformedGeometry(this.m_geom.filterTag(fn), this.m_transform);
const e = this.m_geom.filterTag(fn);
if (!e) return null;
return new TransformedGeometry(e, this.m_transform);
}
isEmpty() {
return this.m_geom.isEmpty();
@ -192,6 +196,62 @@ class CombineGeometry extends GeometryBase {
}
}
class BooleanGeometry extends GeometryBase {
constructor(operator, operands) {
super();
this.m_operator = operator;
this.m_operands = operands;
this.m_resolved = null;
}
asContours() {
if (this.m_resolved) return this.m_resolved;
this.m_resolved = this.asContoursImpl();
return this.m_resolved;
}
asContoursImpl() {
let arcs = CurveUtil.convertShapeToArcs(this.m_operands[0].asContours());
if (this.m_operands.length < 2) {
arcs = TypoGeom.Boolean.removeOverlap(
arcs,
TypoGeom.Boolean.PolyFillType.pftNonZero,
CurveUtil.BOOLE_RESOLUTION
);
}
for (let j = 1; j < this.m_operands.length; j++) {
arcs = TypoGeom.Boolean.combine(
this.m_operator,
arcs,
CurveUtil.convertShapeToArcs(this.m_operands[j].asContours()),
TypoGeom.Boolean.PolyFillType.pftNonZero,
TypoGeom.Boolean.PolyFillType.pftNonZero,
CurveUtil.BOOLE_RESOLUTION
);
}
const ctx = new CurveUtil.BezToContoursSink();
TypoGeom.ShapeConv.transferBezArcShape(arcs, ctx);
return ctx.contours;
}
asReferences() {
return null;
}
filterTag(fn) {
let filtered = [];
for (const operand of this.m_operands) {
const fp = operand.filterTag(fn);
if (fp) filtered.push(fp);
}
return new BooleanGeometry(this.m_operator, filtered);
}
isEmpty() {
for (const operand of this.m_operands) if (!operand.isEmpty()) return false;
return true;
}
measureComplexity() {
let s = 0;
for (const operand of this.m_operands) s += operand.measureComplexity();
}
}
function combineWith(a, b) {
if (a instanceof CombineGeometry) {
return a.with(b);
@ -206,5 +266,6 @@ exports.ReferenceGeometry = ReferenceGeometry;
exports.TaggedGeometry = TaggedGeometry;
exports.TransformedGeometry = TransformedGeometry;
exports.CombineGeometry = CombineGeometry;
exports.BooleanGeometry = BooleanGeometry;
exports.combineWith = combineWith;

View file

@ -95,15 +95,15 @@ module.exports = class Glyph {
this.avoidBeingComposite = g.avoidBeingComposite;
}
combineGeometryImpl(g) {
includeGeometry(g) {
if (this.ctxTag) g = new Geom.TaggedGeometry(g, this.ctxTag);
this.geometry = Geom.combineWith(this.geometry, g);
}
includeGlyphImpl(g, shiftX, shiftY) {
if (g._m_identifier) {
this.combineGeometryImpl(new Geom.ReferenceGeometry(g, shiftX, shiftY));
this.includeGeometry(new Geom.ReferenceGeometry(g, shiftX, shiftY));
} else {
this.combineGeometryImpl(
this.includeGeometry(
new Geom.TransformedGeometry(g.geometry, Transform.Translate(shiftX, shiftY))
);
}
@ -116,7 +116,7 @@ module.exports = class Glyph {
for (const z of contour) c.push(Point.translated(z, shiftX, shiftY));
parts.push(new Geom.ContourGeometry(c));
}
this.combineGeometryImpl(new Geom.CombineGeometry(parts));
this.includeGeometry(new Geom.CombineGeometry(parts));
}
applyTransform(tfm, alsoAnchors) {

View file

@ -53,6 +53,10 @@ module.exports = class SpiroExpansionContext {
const k0 = this.controlKnots[this.controlKnots.length - 1];
if (k0) k0.type = type;
}
setUnimportant() {
const k0 = this.controlKnots[this.controlKnots.length - 1];
if (k0) k0.unimportant = true;
}
expand(contrast) {
if (contrast == null) contrast = 1 / 0.9;
const lhs = [],
@ -100,6 +104,7 @@ module.exports = class SpiroExpansionContext {
rhsAfter = this.gizmo.unapply(rhs[jAfter]);
lhs[j] = {
unimportant: true,
type: knot.type,
...this.gizmo.apply({
x: linreg(knotBefore.x, lhsBefore.x, knotAfter.x, lhsAfter.x, ref.x),
@ -107,6 +112,7 @@ module.exports = class SpiroExpansionContext {
})
};
rhs[j] = {
unimportant: true,
type: reverseKnotType(knot.type),
...this.gizmo.apply({
x: linreg(knotBefore.x, rhsBefore.x, knotAfter.x, rhsAfter.x, ref.x),