From 4c772433b071073bc6b564d8b500bd05b5a52eed Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 3 Dec 2018 17:50:09 -0800 Subject: [PATCH] Do a better job of cubic-to-quadratic conversion --- demo2/path-utils.ts | 41 ++++++++++++++++++++++++----------------- demo2/pathfinder.ts | 6 ++---- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/demo2/path-utils.ts b/demo2/path-utils.ts index f4eb8f5e..bbd771ee 100644 --- a/demo2/path-utils.ts +++ b/demo2/path-utils.ts @@ -22,33 +22,40 @@ export class PathSegment { for (let i = 1; i < segment.length; i += 2) points.push(new Point2D(parseFloat(segment[i]), parseFloat(segment[i + 1]))); this.points = points; - //console.log("PathSegment, segment=", segment, "points=", points); this.command = segment[0]; } to(): Point2D | null { return this.points[this.points.length - 1]; } + + toStringPieces(): string[] { + const pieces = [this.command]; + for (const point of this.points) { + pieces.push(" " + point.x); + pieces.push(" " + point.y); + } + return pieces; + } + + toString(): string { + return this.toStringPieces().join(" "); + } } export function flattenPath(path: SVGPath): SVGPath { - return path.unshort().abs().iterate(segment => { - if (segment[0] === 'C') { - const ctrl0 = new Point2D(parseFloat(segment[segment.length - 6]), - parseFloat(segment[segment.length - 5])); - const ctrl1 = new Point2D(parseFloat(segment[segment.length - 4]), - parseFloat(segment[segment.length - 3])); - const to = new Point2D(parseFloat(segment[segment.length - 2]), - parseFloat(segment[segment.length - 1])); - const ctrl = new Point2D(0.5 * (ctrl0.x + ctrl1.x), 0.5 * (ctrl0.y + ctrl1.y)); - return [['Q', "" + ctrl.x, "" + ctrl.y, "" + to.x, "" + to.y]]; + let lastPoint: Point2D | null = null; + return path.unshort().abs().iterate(segmentPieces => { + let segment = new PathSegment(segmentPieces); + if (segment.command === 'C' && lastPoint != null) { + const ctrl10 = segment.points[0].scale(3.0).sub(lastPoint).scale(0.5); + const ctrl11 = segment.points[1].scale(3.0).sub(segment.points[2]).scale(0.5); + const to = segment.points[2]; + const ctrl = ctrl10.lerp(ctrl11, 0.5); + segment = new PathSegment(['Q', "" + ctrl.x, "" + ctrl.y, "" + to.x, "" + to.y]); } - if (segment[0] === 'A') { - const to = new Point2D(parseFloat(segment[segment.length - 2]), - parseFloat(segment[segment.length - 1])); - return [['L', "" + to.x, "" + to.y]]; - } - return [segment]; + lastPoint = segment.to(); + return [segment.toStringPieces()]; }); } diff --git a/demo2/pathfinder.ts b/demo2/pathfinder.ts index 0e0a7a9d..0f60aeb5 100644 --- a/demo2/pathfinder.ts +++ b/demo2/pathfinder.ts @@ -27,7 +27,7 @@ const parseColor: (color: string) => any = require('parse-color'); const SVG_NS: string = "http://www.w3.org/2000/svg"; const STENCIL_FRAMEBUFFER_SIZE: Size2D = { - width: TILE_SIZE.width * 256, + width: TILE_SIZE.width * 128, height: TILE_SIZE.height * 256, }; @@ -286,9 +286,8 @@ class App { gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, unwrapNull(this.primitiveCount)); gl.disable(gl.BLEND); - /* // Read back stencil and dump it. - const totalStencilFramebufferSize = STENCIL_FRAMEBUFFER_SIZE.width * + /*const totalStencilFramebufferSize = STENCIL_FRAMEBUFFER_SIZE.width * STENCIL_FRAMEBUFFER_SIZE.height * 4; const stencilData = new Float32Array(totalStencilFramebufferSize); gl.readPixels(0, 0, @@ -578,7 +577,6 @@ class Scene { outline.stroke(strokeWidth * GLOBAL_TRANSFORM.a); const strokedPathString = outline.toSVGPathString(); path = SVGPath(strokedPathString); - console.log(path.toString()); } paths.push(path);