diff --git a/gen/finalize/index.js b/gen/finalize/index.js index 67996982b..880bd461f 100644 --- a/gen/finalize/index.js +++ b/gen/finalize/index.js @@ -1,12 +1,12 @@ "use strict"; const autoRef = require("./autoref"); -const caryllShapeOps = require("caryll-shapeops"); -const curveUtil = require("../../support/curve-util"); +const TypoGeom = require("typo-geom"); +const CurveUtil = require("../../support/curve-util"); const { fairifyQuad } = require("../../support/fairify"); -const Transform = require("../../support/transform"); const { AnyCv } = require("../../support/gr"); const gcFont = require("./gc"); +const { SpiroContourContext } = require("../../support/spiroexpand"); function regulateGlyph(g, skew) { if (!g.contours) return; @@ -29,18 +29,20 @@ function regulateGlyph(g, skew) { } } -function simplifyContours(contours) { - const gizmo = Transform.Id(); - const source = []; - for (const contour of contours) { - if (contour.length > 2) source.push(curveUtil.convertContourToCubic(contour)); - } - const simplified = caryllShapeOps.removeOverlap(source, 1, 1 << 17, true); +function simplifyContours(source) { + const simplifiedArcs = TypoGeom.Boolean.removeOverlap( + CurveUtil.convertShapeToArcs(source), + TypoGeom.Boolean.PolyFillType.pftNonZero, + 1 << 17 + ); + + const sc = new SpiroContourContext(); + TypoGeom.transferBezArcShape(simplifiedArcs, sc); const result = []; - for (const contour of simplified) { + for (const contour of sc.contours) { if (contour.length <= 2) continue; - result.push(curveUtil.cleanupQuadContour(fairifyQuad(contour, gizmo))); + result.push(CurveUtil.cleanupQuadContour(fairifyQuad(contour))); } return result; } @@ -99,8 +101,8 @@ function extractGlyfCmap(glyphList, font) { const cmap = {}; for (let g of glyphList) { glyf[g.name] = g; - if (!g.unicode) continue; + for (let u of g.unicode) { if (isFinite(u - 0)) cmap[u] = g.name; } diff --git a/glyphs/common-shapes.ptl b/glyphs/common-shapes.ptl index c43030eb4..d7cb093f2 100644 --- a/glyphs/common-shapes.ptl +++ b/glyphs/common-shapes.ptl @@ -730,6 +730,17 @@ glyph-block CommonShapes : begin define overlay : create-glyph : glyph-construction : include fnOverlay AS_BASE ALSO_METRICS define background : create-glyph : glyph-construction : include fnBackground AS_BASE ALSO_METRICS + candidates.push : glyph-construction + set this.gizmo : Translate 0 0 + foreach [c : items-of overlay.contours] : foreach [z : items-of c] : if z.on : do + define x z.x + define y z.y + include : spiro-outline + corner (x - sw) (y - sw) + corner (x + sw) (y - sw) + corner (x + sw) (y + sw) + corner (x - sw) (y + sw) + foreach [r : range (0 - segs) till (segs)] : foreach [c : range (0 - segs) till (segs)] : do define dx : r / segs * sw define dy : c / segs * sw @@ -737,17 +748,6 @@ glyph-block CommonShapes : begin include overlay apply-transform : Translate dx dy - foreach [c : items-of overlay.contours] : foreach [z : items-of c] : if z.on : do - define x z.x - define y z.y - candidates.push : glyph-construction - set this.gizmo : Translate 0 0 - include : spiro-outline - corner (x - sw) (y - sw) - corner (x + sw) (y - sw) - corner (x + sw) (y + sw) - corner (x - sw) (y + sw) - include : difference background [union.apply null candidates] include overlay diff --git a/glyphs/overmarks.ptl b/glyphs/overmarks.ptl index 36ccf6e9a..a581a2955 100644 --- a/glyphs/overmarks.ptl +++ b/glyphs/overmarks.ptl @@ -4,7 +4,7 @@ import '../support/transform' as : Transform && [object [transformPoint tp]] import [curveToContour OffsetCurve] from '../support/curve-util' import [mix linreg clamp fallback] from '../support/utils' import [designParameters] from '../meta/aesthetics' -import [Arc Quadify] from "typo-geom" +import [Arcs Quadify] from "typo-geom" import [TieMark TieGlyph] from "../support/gr" glyph-module @@ -225,7 +225,7 @@ glyph-block Overmarks : begin define z2 : tp currentGlyph.gizmo : object [x : mix leftEnd rightEnd tildeWaveX] [y : mix tbot ttop tildeWave] define z3 : tp currentGlyph.gizmo : object [x : mix leftEnd rightEnd (1 - tildeWaveX)] [y : mix tbot ttop (1 - tildeWave)] define z4 : tp currentGlyph.gizmo : object [x rightEnd] [y ttop] - define bone : new Arc.Bez3 z1 z2 z3 z4 + define bone : new Arcs.Bez3 z1 z2 z3 z4 define inner : curveToContour [new OffsetCurve bone (+hs) HVContrast] 32 define outer : curveToContour [new OffsetCurve bone (-hs) HVContrast] 32 diff --git a/package.json b/package.json index 91d2acd3d..40e822016 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "install": "node utility/check-env" }, "dependencies": { - "caryll-shapeops": "^0.3.1", "cldr": "^5.5.4", "ejs": "^3.1.3", "fs-extra": "^9.0.0", @@ -21,7 +20,7 @@ "topsort": "^0.0.2", "ttf2woff": "^2.0.1", "ttf2woff2": "^3.0.0", - "typo-geom": "^0.6.0", + "typo-geom": "^0.7.0", "unicode-13.0.0": "^0.8.0", "unorm": "^1.6.0", "verda": "^1.0.1", diff --git a/support/boole-kit.ptl b/support/boole-kit.ptl index 827b963f8..26c69c44e 100644 --- a/support/boole-kit.ptl +++ b/support/boole-kit.ptl @@ -1,5 +1,6 @@ -import 'caryll-shapeops' as ShapeOps +import 'typo-geom' as TypoGeom import './curve-util' as CurveUtil +import './spiroexpand' as [object SpiroContourContext] export : define [SetupBuilders args] : begin define [object Glyph globalTransform] args @@ -15,13 +16,15 @@ export : define [SetupBuilders args] : begin set g1.gizmo : this.gizmo || globalTransform g1.include item set g1.contours : g1.contours.map CurveUtil.convertContourToCubic - local c1 : ShapeOps.boole operator g.contours g1.contours ShapeOps.fillRules.nonzero ShapeOps.fillRules.nonzero 16384 - set g.contours : c1.map CurveUtil.convertContourToCubicRev + local c1 : TypoGeom.Boolean.combine operator [CurveUtil.convertShapeToArcs g.contours] [CurveUtil.convertShapeToArcs g1.contours] TypoGeom.Boolean.PolyFillType.pftNonZero TypoGeom.Boolean.PolyFillType.pftNonZero 16384 + local ctx : new SpiroContourContext + TypoGeom.transferBezArcShape c1 ctx + set g.contours ctx.contours this.includeGlyph g return g - define union : Boole ShapeOps.ops.union - define intersection : Boole ShapeOps.ops.intersection - define difference : Boole ShapeOps.ops.difference + define union : Boole TypoGeom.Boolean.ClipType.ctUnion + define intersection : Boole TypoGeom.Boolean.ClipType.ctIntersection + define difference : Boole TypoGeom.Boolean.ClipType.ctDifference return [object union intersection difference] \ No newline at end of file diff --git a/support/curve-util.js b/support/curve-util.js index c9ffc250d..a8c55f9fc 100644 --- a/support/curve-util.js +++ b/support/curve-util.js @@ -1,6 +1,6 @@ "use strict"; -const typoGeom = require("typo-geom"); +const TypoGeom = require("typo-geom"); const Point = require("./point"); const { mix } = require("./utils"); @@ -182,7 +182,7 @@ function autoCubify(arc, err) { if (s > 0) offPoints.push(z0); offPoints.push(z1, z2); - const bezArc = new typoGeom.Arc.Bez3(z0, z1, z2, z3); + const bezArc = new TypoGeom.Arcs.Bez3(z0, z1, z2, z3); for (let k = 1; k < perSegHits; k++) { const tk = k / perSegHits; @@ -215,8 +215,65 @@ function fixedCubify(arc, nSeg) { return offPoints; } +function convertContourToArcs(contour) { + if (!contour || !contour.length) return []; + + const newContour = []; + let z0 = Point.cornerFrom(contour[0]); + + for (let j = 1; j < contour.length; j++) { + const z = contour[j]; + if (z.on) { + newContour.push( + TypoGeom.Arcs.Bez3.fromStraightSegment( + new TypoGeom.Arcs.StraightSegment(z0, Point.cornerFrom(z)) + ) + ); + z0 = z; + } else if (z.cubic) { + const z1 = z; + const z2 = contour[j + 1]; + const z3 = contour[j + 2]; + newContour.push( + new TypoGeom.Arcs.Bez3( + z0, + Point.cubicOffFrom(z1), + Point.cubicOffFrom(z2), + Point.cornerFrom(z3) + ) + ); + z0 = z3; + j += 2; + } else { + const zc = z; + let zf = contour[j + 1] || contour[0]; + const zfIsCorner = zf.on; + if (!zfIsCorner) zf = Point.cornerFrom(zc).mix(0.5, zf); + + newContour.push( + new TypoGeom.Arcs.Bez3( + z0, + Point.cubicOffFrom(z0).mix(2 / 3, zc), + Point.cubicOffFrom(zf).mix(2 / 3, zc), + Point.cornerFrom(zf) + ) + ); + + z0 = zf; + if (zfIsCorner) j++; + } + } + + return newContour; +} + +function convertShapeToArcs(shape) { + return shape.map(convertContourToArcs); +} + exports.cleanupQuadContour = cleanupQuadContour; exports.convertContourToCubic = convertContourToCubic; exports.convertContourToCubicRev = convertContourToCubicRev; exports.autoCubify = autoCubify; exports.fixedCubify = fixedCubify; +exports.convertShapeToArcs = convertShapeToArcs; diff --git a/support/fairify.js b/support/fairify.js index 6d47d568a..3c08084cb 100644 --- a/support/fairify.js +++ b/support/fairify.js @@ -1,9 +1,7 @@ "use strict"; -const Transform = require("./transform"); const typoGeom = require("typo-geom"); const Point = require("./point"); -const curveUtil = require("./curve-util"); const SMALL = 1e-6; @@ -201,7 +199,7 @@ class BezierCurveCluster { if (zs[j].on) { const z1 = last, z4 = zs[j]; - const seg = new typoGeom.Arc.StraightSegment(z1, z4); + const seg = new typoGeom.Arcs.StraightSegment(z1, z4); segments.push(seg); lengths.push(this.measureLength(seg)); last = z4; @@ -210,7 +208,7 @@ class BezierCurveCluster { z2 = zs[j], z3 = zs[j + 1], z4 = zs[j + 2]; - const seg = new typoGeom.Arc.Bez3(z1, z2, z3, z4); + const seg = new typoGeom.Arcs.Bez3(z1, z2, z3, z4); segments.push(seg); lengths.push(this.measureLength(seg)); last = z4; @@ -288,15 +286,15 @@ class BezierCurveCluster { } const QuadBuilder = { - corner(sink, gizmo, z) { - sink.push(Transform.transformPoint(gizmo, Point.cornerFrom(z)).round(1024)); + corner(sink, z) { + sink.push(Point.cornerFrom(z).round(1024)); }, - arc(sink, gizmo, arc) { + arc(sink, arc) { if (arc.isAlmostLinear(1 / 4)) return; const offPoints = typoGeom.Quadify.auto(arc, 1 / 4); if (!offPoints) return; for (const z of offPoints) { - sink.push(Transform.transformPoint(gizmo, Point.offFrom(z)).round(1024)); + sink.push(Point.offFrom(z).round(1024)); } }, split: true, @@ -304,55 +302,31 @@ const QuadBuilder = { duplicateStart: true }; -const SpiroBuilder = { - corner(sink, gizmo, z) { - sink.push(Transform.transformPoint(gizmo, Point.cornerFrom(z))); - }, - arc(sink, gizmo, arc) { - if (arc.isAlmostLinear(1 / 4)) return; - const offPoints = curveUtil.fixedCubify(arc, 12); - for (const z of offPoints) { - sink.push(Transform.transformPoint(gizmo, z)); - } - }, - split: true, - canonicalStart: false, - duplicateStart: false -}; - -function buildCurve(curve, gizmo, builder) { +function buildCurve(curve, builder) { let sink = []; for (let j = 0; j < curve.length; j++) { if (!curve[j].mark) continue; - builder.corner(sink, gizmo, curve[j]); + builder.corner(sink, curve[j]); let k = j; for (; k < curve.length && (k === j || !curve[k].mark); k++); const pts = curve.slice(j, k + 1); - if (pts.length > 1) builder.arc(sink, gizmo, new BezierCurveCluster(pts)); + if (pts.length > 1) builder.arc(sink, new BezierCurveCluster(pts)); j = k - 1; } return sink; } -function fairifyImpl(sourceCubicContour, gizmo, builder) { - for (let j = 0; j < sourceCubicContour.length; j++) { - if (!isFinite(sourceCubicContour[j].x)) sourceCubicContour[j].x = 0; - if (!isFinite(sourceCubicContour[j].y)) sourceCubicContour[j].y = 0; - sourceCubicContour[j] = Transform.unTransform(gizmo, sourceCubicContour[j]); - } +function fairifyImpl(sourceCubicContour, builder) { let splitContour = toSpansForm(sourceCubicContour, builder.split); markCorners(splitContour); if (builder.canonicalStart) { splitContour = canonicalStart(splitContour); markCorners(splitContour); } - return buildCurve(splitContour, gizmo, builder); + return buildCurve(splitContour, builder); } -exports.fairifyQuad = function (sourceCubicContour, gizmo) { - return fairifyImpl(sourceCubicContour, gizmo, QuadBuilder); -}; -exports.fairifySpiro = function (sourceCubicContour, gizmo) { - return fairifyImpl(sourceCubicContour, gizmo, SpiroBuilder); +exports.fairifyQuad = function (sourceCubicContour) { + return fairifyImpl(sourceCubicContour, QuadBuilder); }; diff --git a/support/spiroexpand.ptl b/support/spiroexpand.ptl index 484aec4e3..69f9492b9 100644 --- a/support/spiroexpand.ptl +++ b/support/spiroexpand.ptl @@ -25,6 +25,9 @@ class SpiroExpansionContext set this.defaultd1 0 set this.defaultd2 0 + public [beginShape] : begin + public [endShape] : begin + public [moveTo x y unimportant] : begin if unimportant : return nothing # Transform incoming knots using gizmo @@ -156,6 +159,10 @@ class SpiroContourContext set this.contours { } set this.defaultTag null + public [beginShape] : begin + + public [endShape] : begin + public [moveTo x y] : begin local contour {[Point.transformed this.gizmo x y true]} set contour.tag this.defaultTag