Do a better job of cubic-to-quadratic conversion

This commit is contained in:
Patrick Walton 2018-12-03 17:50:09 -08:00
parent 2aba5fdcfc
commit 4c772433b0
2 changed files with 26 additions and 21 deletions

View File

@ -22,33 +22,40 @@ export class PathSegment {
for (let i = 1; i < segment.length; i += 2) for (let i = 1; i < segment.length; i += 2)
points.push(new Point2D(parseFloat(segment[i]), parseFloat(segment[i + 1]))); points.push(new Point2D(parseFloat(segment[i]), parseFloat(segment[i + 1])));
this.points = points; this.points = points;
//console.log("PathSegment, segment=", segment, "points=", points);
this.command = segment[0]; this.command = segment[0];
} }
to(): Point2D | null { to(): Point2D | null {
return this.points[this.points.length - 1]; 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 { export function flattenPath(path: SVGPath): SVGPath {
return path.unshort().abs().iterate(segment => { let lastPoint: Point2D | null = null;
if (segment[0] === 'C') { return path.unshort().abs().iterate(segmentPieces => {
const ctrl0 = new Point2D(parseFloat(segment[segment.length - 6]), let segment = new PathSegment(segmentPieces);
parseFloat(segment[segment.length - 5])); if (segment.command === 'C' && lastPoint != null) {
const ctrl1 = new Point2D(parseFloat(segment[segment.length - 4]), const ctrl10 = segment.points[0].scale(3.0).sub(lastPoint).scale(0.5);
parseFloat(segment[segment.length - 3])); const ctrl11 = segment.points[1].scale(3.0).sub(segment.points[2]).scale(0.5);
const to = new Point2D(parseFloat(segment[segment.length - 2]), const to = segment.points[2];
parseFloat(segment[segment.length - 1])); const ctrl = ctrl10.lerp(ctrl11, 0.5);
const ctrl = new Point2D(0.5 * (ctrl0.x + ctrl1.x), 0.5 * (ctrl0.y + ctrl1.y)); segment = new PathSegment(['Q', "" + ctrl.x, "" + ctrl.y, "" + to.x, "" + to.y]);
return [['Q', "" + ctrl.x, "" + ctrl.y, "" + to.x, "" + to.y]];
} }
if (segment[0] === 'A') { lastPoint = segment.to();
const to = new Point2D(parseFloat(segment[segment.length - 2]), return [segment.toStringPieces()];
parseFloat(segment[segment.length - 1]));
return [['L', "" + to.x, "" + to.y]];
}
return [segment];
}); });
} }

View File

@ -27,7 +27,7 @@ const parseColor: (color: string) => any = require('parse-color');
const SVG_NS: string = "http://www.w3.org/2000/svg"; const SVG_NS: string = "http://www.w3.org/2000/svg";
const STENCIL_FRAMEBUFFER_SIZE: Size2D = { const STENCIL_FRAMEBUFFER_SIZE: Size2D = {
width: TILE_SIZE.width * 256, width: TILE_SIZE.width * 128,
height: TILE_SIZE.height * 256, height: TILE_SIZE.height * 256,
}; };
@ -286,9 +286,8 @@ class App {
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, unwrapNull(this.primitiveCount)); gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, unwrapNull(this.primitiveCount));
gl.disable(gl.BLEND); gl.disable(gl.BLEND);
/*
// Read back stencil and dump it. // Read back stencil and dump it.
const totalStencilFramebufferSize = STENCIL_FRAMEBUFFER_SIZE.width * /*const totalStencilFramebufferSize = STENCIL_FRAMEBUFFER_SIZE.width *
STENCIL_FRAMEBUFFER_SIZE.height * 4; STENCIL_FRAMEBUFFER_SIZE.height * 4;
const stencilData = new Float32Array(totalStencilFramebufferSize); const stencilData = new Float32Array(totalStencilFramebufferSize);
gl.readPixels(0, 0, gl.readPixels(0, 0,
@ -578,7 +577,6 @@ class Scene {
outline.stroke(strokeWidth * GLOBAL_TRANSFORM.a); outline.stroke(strokeWidth * GLOBAL_TRANSFORM.a);
const strokedPathString = outline.toSVGPathString(); const strokedPathString = outline.toSVGPathString();
path = SVGPath(strokedPathString); path = SVGPath(strokedPathString);
console.log(path.toString());
} }
paths.push(path); paths.push(path);