Move boolean composition out of kits.
This commit is contained in:
parent
4cf5faf93c
commit
3ef9aaa2e2
7 changed files with 103 additions and 42 deletions
|
@ -1,38 +1,22 @@
|
|||
import 'typo-geom' as TypoGeom
|
||||
import '../support/curve-util' as CurveUtil
|
||||
import '../support/geometry' as [object BooleanGeometry]
|
||||
|
||||
export : define [SetupBuilders args] : begin
|
||||
define [object Glyph GlobalTransform] args
|
||||
define [Boole operator] : begin
|
||||
define [BooleImplFn] : begin
|
||||
define k : {}.slice.call arguments 0
|
||||
return : lambda [] : begin
|
||||
local contourArcs : getArcsFromProc this k.0
|
||||
if (k.length === 1) : set contourArcs : TypoGeom.Boolean.removeOverlap
|
||||
begin contourArcs
|
||||
begin TypoGeom.Boolean.PolyFillType.pftNonZero
|
||||
begin CurveUtil.BOOLE_RESOLUTION
|
||||
|
||||
foreach [item : items-of : k.slice 1] : begin
|
||||
set contourArcs : TypoGeom.Boolean.combine operator
|
||||
begin contourArcs
|
||||
getArcsFromProc this item
|
||||
begin TypoGeom.Boolean.PolyFillType.pftNonZero
|
||||
begin TypoGeom.Boolean.PolyFillType.pftNonZero
|
||||
begin CurveUtil.BOOLE_RESOLUTION
|
||||
|
||||
local ctx : new CurveUtil.BezToContoursSink
|
||||
TypoGeom.ShapeConv.transferBezArcShape contourArcs ctx
|
||||
this.includeContours ctx.contours 0 0
|
||||
|
||||
define [getArcsFromProc g p] : begin
|
||||
define [impl operator operands] : begin
|
||||
return : function [] : begin
|
||||
local operandGeometries {}
|
||||
foreach operand [items-of operands] : begin
|
||||
local g1 : new Glyph
|
||||
set g1.gizmo : g.gizmo || GlobalTransform
|
||||
g1.include p
|
||||
return : CurveUtil.convertShapeToArcs : g1.geometry.asContours
|
||||
set g1.gizmo : this.gizmo || GlobalTransform
|
||||
g1.include operand
|
||||
operandGeometries.push g1.geometry
|
||||
|
||||
define union : Boole TypoGeom.Boolean.ClipType.ctUnion
|
||||
define intersection : Boole TypoGeom.Boolean.ClipType.ctIntersection
|
||||
define difference : Boole TypoGeom.Boolean.ClipType.ctDifference
|
||||
this.includeGeometry : new BooleanGeometry operator operandGeometries
|
||||
|
||||
define [union] : impl TypoGeom.Boolean.ClipType.ctUnion [{}.slice.call arguments 0]
|
||||
define [intersection] : impl TypoGeom.Boolean.ClipType.ctIntersection [{}.slice.call arguments 0]
|
||||
define [difference] : impl TypoGeom.Boolean.ClipType.ctDifference [{}.slice.call arguments 0]
|
||||
|
||||
return [object union intersection difference]
|
||||
|
|
|
@ -63,9 +63,7 @@ export : define [SetupBuilders args] : begin
|
|||
define [widths.center.heading w d] : lambda [] : begin
|
||||
if this.setWidth : this.setWidth ([fallback w Stroke] / 2) ([fallback w Stroke] / 2)
|
||||
if this.headsTo : this.headsTo d
|
||||
define [unimportant] : begin
|
||||
if (this.points && this.points.length && this.points.(this.points.length - 1)) : this.points.(this.points.length - 1).subdivided = true
|
||||
if (this.controlKnots && this.controlKnots.length && this.controlKnots.(this.controlKnots.length - 1)) : this.controlKnots.(this.controlKnots.length - 1).unimportant = true
|
||||
define [unimportant] : if this.setUnimportant : this.setUnimportant
|
||||
define [important] nothing
|
||||
|
||||
# Interpolation pesudoknots
|
||||
|
@ -175,8 +173,7 @@ export : define [SetupBuilders args] : begin
|
|||
return { .knots knots .closed closed }
|
||||
|
||||
define [convertSpiroToBezier knots closed ctx] : begin
|
||||
define CUBIC false
|
||||
return : SpiroJs.spiroToBezierOnContext knots closed ctx CUBIC CurveUtil.GEOMETRY_PRECISION
|
||||
return : SpiroJs.spiroToBezierOnContext knots closed ctx CurveUtil.SPIRO_PRECISION
|
||||
|
||||
define [dispiro] : let [args : {}.slice.call arguments 0] : lambda [] : begin
|
||||
define CLOSED true
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
"otb-ttc-bundle": "^1.0.0",
|
||||
"patel": "^0.34.0",
|
||||
"semver": "^7.3.4",
|
||||
"spiro": "^2.0.0",
|
||||
"spiro": "^3.0.0",
|
||||
"stylus": "^0.54.8",
|
||||
"toposort": "^2.0.2",
|
||||
"typo-geom": "^0.11.0",
|
||||
"typo-geom": "^0.12.0",
|
||||
"verda": "^1.2.1",
|
||||
"wawoff2": "^1.0.2",
|
||||
"which": "^2.0.2"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue