Initial support for curves

This commit is contained in:
Patrick Walton 2018-12-03 12:30:12 -08:00
parent 7bf5ac1098
commit 2a07733144
3 changed files with 46 additions and 22 deletions

View File

@ -27,6 +27,10 @@ export class Point2D {
lerp(other: Point2D, t: number): Point2D { lerp(other: Point2D, t: number): Point2D {
return new Point2D(lerp(this.x, other.x, t), lerp(this.y, other.y, t)); return new Point2D(lerp(this.x, other.x, t), lerp(this.y, other.y, t));
} }
translate(x: number, y: number): Point2D {
return new Point2D(this.x + x, this.y + y);
}
} }
export interface Size2D { export interface Size2D {

View File

@ -325,8 +325,11 @@ class App {
for (const tileStrip of pathTileStrips) { for (const tileStrip of pathTileStrips) {
for (const tile of tileStrip.tiles) { for (const tile of tileStrip.tiles) {
for (const edge of tile.edges) { for (const edge of tile.edges) {
const ctrl = new Point2D(lerp(edge.from.x, edge.to.x, 0.5), let ctrl;
lerp(edge.from.y, edge.to.y, 0.5)); if (edge.ctrl == null)
ctrl = edge.from.lerp(edge.to, 0.5);
else
ctrl = edge.ctrl;
stencilVertexPositions.push(edge.from.x, edge.from.y, stencilVertexPositions.push(edge.from.x, edge.from.y,
ctrl.x, ctrl.y, ctrl.x, ctrl.y,
edge.to.x, edge.to.y); edge.to.x, edge.to.y);

View File

@ -52,13 +52,17 @@ export class Tiler {
this.endpoints.push(currentSubpathEndpoints); this.endpoints.push(currentSubpathEndpoints);
currentSubpathEndpoints = new SubpathEndpoints; currentSubpathEndpoints = new SubpathEndpoints;
} }
currentSubpathEndpoints.controlPoints.push(null);
currentSubpathEndpoints.endpoints.push(segment.points[0]); currentSubpathEndpoints.endpoints.push(segment.points[0]);
break; break;
case 'L': case 'L':
case 'S': case 'S':
case 'Q':
case 'C':
// TODO(pcwalton): Canonicalize 'S'. // TODO(pcwalton): Canonicalize 'S'.
currentSubpathEndpoints.controlPoints.push(null);
currentSubpathEndpoints.endpoints.push(unwrapNull(segment.to()));
break;
case 'Q':
currentSubpathEndpoints.controlPoints.push(segment.points[0]);
currentSubpathEndpoints.endpoints.push(unwrapNull(segment.to())); currentSubpathEndpoints.endpoints.push(unwrapNull(segment.to()));
break; break;
case 'Z': case 'Z':
@ -129,9 +133,9 @@ export class Tiler {
const startPoint = new Point2D(interval.start, tileTop); const startPoint = new Point2D(interval.start, tileTop);
const endPoint = new Point2D(interval.end, tileTop); const endPoint = new Point2D(interval.end, tileTop);
if (interval.winding < 0) if (interval.winding < 0)
strip.pushEdge(new Edge(startPoint, endPoint)); strip.pushEdge(new Edge(startPoint, null, endPoint));
else else
strip.pushEdge(new Edge(endPoint, startPoint)); strip.pushEdge(new Edge(endPoint, null, startPoint));
} }
// Populate tile strip with active edges. // Populate tile strip with active edges.
@ -313,24 +317,23 @@ export class Tiler {
return {upper: edges.next, lower: edges.prev}; return {upper: edges.next, lower: edges.prev};
} }
private prevEdgeFromEndpoint(endpointIndex: EndpointIndex): Edge {
const subpathEndpoints = this.endpoints[endpointIndex.subpathIndex];
return new Edge(subpathEndpoints.prevEndpointOf(endpointIndex.endpointIndex),
subpathEndpoints.endpoints[endpointIndex.endpointIndex]);
}
private nextEdgeFromEndpoint(endpointIndex: EndpointIndex): Edge { private nextEdgeFromEndpoint(endpointIndex: EndpointIndex): Edge {
const subpathEndpoints = this.endpoints[endpointIndex.subpathIndex]; const subpathEndpoints = this.endpoints[endpointIndex.subpathIndex];
const nextEndpointIndex =
subpathEndpoints.nextEndpointIndexOf(endpointIndex.endpointIndex);
return new Edge(subpathEndpoints.endpoints[endpointIndex.endpointIndex], return new Edge(subpathEndpoints.endpoints[endpointIndex.endpointIndex],
subpathEndpoints.nextEndpointOf(endpointIndex.endpointIndex)); subpathEndpoints.controlPoints[nextEndpointIndex],
subpathEndpoints.endpoints[nextEndpointIndex]);
} }
} }
class SubpathEndpoints { class SubpathEndpoints {
endpoints: Point2D[]; endpoints: Point2D[];
controlPoints: (Point2D | null)[];
constructor() { constructor() {
this.endpoints = []; this.endpoints = [];
this.controlPoints = [];
} }
isEmpty(): boolean { isEmpty(): boolean {
@ -376,20 +379,32 @@ export class PathSegment {
class Edge { class Edge {
from: Point2D; from: Point2D;
ctrl: Point2D | null;
to: Point2D; to: Point2D;
constructor(from: Point2D, to: Point2D) { constructor(from: Point2D, ctrl: Point2D | null, to: Point2D) {
this.from = from; this.from = from;
this.ctrl = ctrl;
this.to = to; this.to = to;
Object.freeze(this); Object.freeze(this);
} }
subdivideAt(t: number): SubdividedEdges { subdivideAt(t: number): SubdividedEdges {
const mid = this.from.lerp(this.to, t); if (this.ctrl == null) {
const mid = this.from.lerp(this.to, t);
return {
prev: new Edge(this.from, null, mid),
next: new Edge(mid, null, this.to),
};
}
const ctrlA = this.from.lerp(this.ctrl, t);
const ctrlB = this.ctrl.lerp(this.to, t);
const mid = ctrlA.lerp(ctrlB, t);
return { return {
prev: new Edge(this.from, mid), prev: new Edge(this.from, ctrlA, mid),
next: new Edge(mid, this.to), next: new Edge(mid, ctrlB, this.to),
}; }
} }
} }
@ -408,8 +423,9 @@ class Strip {
} }
pushEdge(edge: Edge): void { pushEdge(edge: Edge): void {
this.edges.push(new Edge(new Point2D(edge.from.x, edge.from.y - this.tileTop), this.edges.push(new Edge(edge.from.translate(0, -this.tileTop),
new Point2D(edge.to.x, edge.to.y - this.tileTop))); edge.ctrl == null ? null : edge.ctrl.translate(0, -this.tileTop),
edge.to.translate(0, -this.tileTop)));
} }
tileBottom(): number { tileBottom(): number {
@ -449,8 +465,9 @@ export class Tile {
} }
pushEdge(edge: Edge): void { pushEdge(edge: Edge): void {
this.edges.push(new Edge(new Point2D(edge.from.x - this.tileLeft, edge.from.y), this.edges.push(new Edge(edge.from.translate(-this.tileLeft, 0),
new Point2D(edge.to.x - this.tileLeft, edge.to.y))); edge.ctrl == null ? null : edge.ctrl.translate(-this.tileLeft, 0),
edge.to.translate(-this.tileLeft, 0)));
} }
isEmpty(): boolean { isEmpty(): boolean {