Initial support for curves
This commit is contained in:
parent
7bf5ac1098
commit
2a07733144
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,21 +379,33 @@ 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 {
|
||||||
|
if (this.ctrl == null) {
|
||||||
const mid = this.from.lerp(this.to, t);
|
const mid = this.from.lerp(this.to, t);
|
||||||
return {
|
return {
|
||||||
prev: new Edge(this.from, mid),
|
prev: new Edge(this.from, null, mid),
|
||||||
next: new Edge(mid, this.to),
|
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 {
|
||||||
|
prev: new Edge(this.from, ctrlA, mid),
|
||||||
|
next: new Edge(mid, ctrlB, this.to),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SubdividedEdges {
|
interface SubdividedEdges {
|
||||||
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue