New tiling mostly working

This commit is contained in:
Patrick Walton 2018-12-02 12:44:02 -08:00
parent 79e69c6916
commit bc3f6b6ae5
3 changed files with 190 additions and 48 deletions

View File

@ -457,8 +457,8 @@ class Scene {
//console.log("path", pathElementIndex, "svg path", path); //console.log("path", pathElementIndex, "svg path", path);
const tiler = new Tiler(path); const tiler = new Tiler(path);
tiler.tile(); tiler.tile();
tileDebugger.addTiler(tiler, paint); tileDebugger.addTiler(tiler, paint, "" + realPathIndex);
console.log("path", pathElementIndex, "tiles", tiler.getTileStrips()); console.log("path", pathElementIndex, "tiles", tiler.getStrips());
//} //}
const boundingRect = this.boundingRectOfPath(path); const boundingRect = this.boundingRectOfPath(path);
@ -820,7 +820,7 @@ function sampleBezier(from: Point2D, ctrl: Point2D, to: Point2D, t: number): Poi
function main(): void { function main(): void {
window.fetch(SVG).then(svg => { window.fetch(SVG).then(svg => {
svg.text().then(svgText => { svg.text().then(svgText => {
testIntervals(); //testIntervals();
const svg = staticCast((new DOMParser).parseFromString(svgText, 'image/svg+xml'), const svg = staticCast((new DOMParser).parseFromString(svgText, 'image/svg+xml'),
XMLDocument); XMLDocument);

View File

@ -33,6 +33,7 @@ export class Tiler {
private endpoints: SubpathEndpoints[]; private endpoints: SubpathEndpoints[];
private sortedEdges: Edge[]; private sortedEdges: Edge[];
private boundingRect: Rect | null; private boundingRect: Rect | null;
private strips: Strip[];
private tileStrips: TileStrip[]; private tileStrips: TileStrip[];
constructor(path: SVGPath) { constructor(path: SVGPath) {
@ -84,7 +85,7 @@ export class Tiler {
if (this.boundingRect == null) if (this.boundingRect == null)
this.boundingRect = new Rect(endpoint, {width: 0, height: 0}); this.boundingRect = new Rect(endpoint, {width: 0, height: 0});
else else
this.boundingRect.unionWithPoint(endpoint); this.boundingRect = this.boundingRect.unionWithPoint(endpoint);
} }
} }
this.sortedEdges.sort((edgeA, edgeB) => { this.sortedEdges.sort((edgeA, edgeB) => {
@ -102,6 +103,7 @@ export class Tiler {
//console.log("allEndpoints", allEndpoints); //console.log("allEndpoints", allEndpoints);
*/ */
this.strips = [];
this.tileStrips = []; this.tileStrips = [];
} }
@ -109,46 +111,96 @@ export class Tiler {
if (this.boundingRect == null) if (this.boundingRect == null)
return; return;
const activeIntervals = new Intervals(this.boundingRect.maxY());; const activeIntervals = new Intervals(this.boundingRect.maxX());;
let activeEdges: Edge[] = []; let activeEdges: Edge[] = [];
let nextEdgeIndex = 0; let nextEdgeIndex = 0;
this.tileStrips = []; this.strips = [];
let tileTop = this.boundingRect.origin.y - this.boundingRect.origin.y % TILE_SIZE.height; let tileTop = this.boundingRect.origin.y - this.boundingRect.origin.y % TILE_SIZE.height;
while (tileTop < this.boundingRect.maxY()) { while (tileTop < this.boundingRect.maxY()) {
const tileBottom = tileTop + TILE_SIZE.height; const tileBottom = tileTop + TILE_SIZE.height;
// Populate tile strip with active intervals. // Populate tile strip with active intervals.
const tileStrip = new TileStrip(tileTop); const strip = new Strip(tileTop);
/*console.log("tileTop", tileTop,
"intervals", JSON.stringify(activeIntervals.intervalRanges()));*/
for (const interval of activeIntervals.intervalRanges()) { for (const interval of activeIntervals.intervalRanges()) {
if (interval.winding === 0) if (interval.winding === 0)
continue; continue;
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)
tileStrip.pushEdge(new Edge(startPoint, endPoint)); strip.pushEdge(new Edge(startPoint, endPoint));
else else
tileStrip.pushEdge(new Edge(endPoint, startPoint)); strip.pushEdge(new Edge(endPoint, startPoint));
} }
// Populate tile strip with active edges. // Populate tile strip with active edges.
const oldEdges = activeEdges; const oldEdges = activeEdges;
activeEdges = []; activeEdges = [];
for (const activeEdge of oldEdges) for (const activeEdge of oldEdges)
this.processEdge(activeEdge, tileStrip, activeEdges, activeIntervals, tileTop); this.processEdgeY(activeEdge, strip, activeEdges, activeIntervals, tileTop);
while (nextEdgeIndex < this.sortedEdges.length) { while (nextEdgeIndex < this.sortedEdges.length) {
const edge = this.sortedEdges[nextEdgeIndex]; const edge = this.sortedEdges[nextEdgeIndex];
if (edge.from.y > tileBottom && edge.to.y > tileBottom) if (edge.from.y > tileBottom && edge.to.y > tileBottom)
break; break;
this.processEdge(edge, tileStrip, activeEdges, activeIntervals, tileTop); this.processEdgeY(edge, strip, activeEdges, activeIntervals, tileTop);
//console.log("new intervals:", JSON.stringify(activeIntervals));
nextEdgeIndex++; nextEdgeIndex++;
} }
this.tileStrips.push(tileStrip); this.strips.push(strip);
tileTop = tileBottom; tileTop = tileBottom;
} }
// Cut up tile strips.
//console.log("strips count:", this.strips.length);
this.tileStrips = this.strips.map(strip => this.divideStrip(strip));
}
private divideStrip(strip: Strip): TileStrip {
// Sort edges.
const sortedEdges = strip.edges.slice(0);
sortedEdges.sort((edgeA, edgeB) => {
return Math.min(edgeA.from.x, edgeA.to.x) - Math.min(edgeB.from.x, edgeB.to.x);
});
const tileStrip = new TileStrip(strip.tileTop);
const boundingRect = unwrapNull(this.boundingRect);
let tileLeft = boundingRect.origin.x - boundingRect.origin.x % TILE_SIZE.width;
let activeEdges: Edge[] = [];
let nextEdgeIndex = 0;
while (tileLeft < boundingRect.maxX()) {
const tile = new Tile(tileLeft);
const tileRight = tileLeft + TILE_SIZE.width;
// Populate tile with active edges.
const oldEdges = activeEdges;
activeEdges = [];
for (const activeEdge of oldEdges)
this.processEdgeX(activeEdge, tile, activeEdges);
while (nextEdgeIndex < sortedEdges.length) {
const edge = sortedEdges[nextEdgeIndex];
if (edge.from.x > tileRight && edge.to.x > tileRight)
break;
this.processEdgeX(edge, tile, activeEdges);
nextEdgeIndex++;
}
tileStrip.pushTile(tile);
tileLeft = tileRight;
}
return tileStrip;
}
getStrips(): Strip[] {
return this.strips;
} }
getTileStrips(): TileStrip[] { getTileStrips(): TileStrip[] {
@ -168,28 +220,61 @@ export class Tiler {
{width: tileRight - tileLeft, height: tileBottom - tileTop}); {width: tileRight - tileLeft, height: tileBottom - tileTop});
} }
private processEdge(edge: Edge, private processEdgeX(edge: Edge, tile: Tile, activeEdges: Edge[]): void {
tileStrip: TileStrip, const tileRight = tile.tileLeft + TILE_SIZE.width;
activeEdges: Edge[], const clipped = this.clipEdgeX(edge, tileRight);
intervals: Intervals,
tileTop: number): if (clipped.left != null)
void { tile.pushEdge(clipped.left);
if (clipped.right != null)
activeEdges.push(clipped.right);
}
private processEdgeY(edge: Edge,
tileStrip: Strip,
activeEdges: Edge[],
intervals: Intervals,
tileTop: number):
void {
const tileBottom = tileTop + TILE_SIZE.height; const tileBottom = tileTop + TILE_SIZE.height;
const clipped = this.clipEdgeY(edge, tileBottom); const clipped = this.clipEdgeY(edge, tileBottom);
if (clipped.upper != null) { if (clipped.upper != null) {
//console.log("pushing clipped upper edge:", JSON.stringify(clipped.upper));
tileStrip.pushEdge(clipped.upper); tileStrip.pushEdge(clipped.upper);
if (edge.from.x <= edge.to.x) if (clipped.upper.from.x <= clipped.upper.to.x)
intervals.add(new IntervalRange(edge.from.x, edge.to.x, 1)); intervals.add(new IntervalRange(clipped.upper.from.x, clipped.upper.to.x, -1));
else else
intervals.add(new IntervalRange(edge.to.x, edge.from.x, -1)); intervals.add(new IntervalRange(clipped.upper.to.x, clipped.upper.from.x, 1));
} }
if (clipped.lower != null) if (clipped.lower != null)
activeEdges.push(clipped.lower); activeEdges.push(clipped.lower);
} }
private clipEdgeX(edge: Edge, x: number): ClippedEdgesX {
if (edge.from.x < x && edge.to.x < x)
return {left: edge, right: null};
if (edge.from.x > x && edge.to.x > x)
return {left: null, right: edge};
const from = {x: edge.from.x, y: edge.from.y, z: 1.0};
const to = {x: edge.to.x, y: edge.to.y, z: 1.0};
const clipLine = {x: 1.0, y: 0.0, z: -x };
const intersectionHC = cross(cross(from, to), clipLine);
const intersection = new Point2D(intersectionHC.x / intersectionHC.z,
intersectionHC.y / intersectionHC.z);
const fromEdge = new Edge(edge.from, intersection);
const toEdge = new Edge(intersection, edge.to);
if (edge.from.x < x)
return {left: fromEdge, right: toEdge};
return {left: toEdge, right: fromEdge};
}
private clipEdgeY(edge: Edge, y: number): ClippedEdgesY { private clipEdgeY(edge: Edge, y: number): ClippedEdgesY {
if (edge.from.y < y && edge.to.y < y) if (edge.from.y < y && edge.to.y < y)
return {upper: edge, lower: null}; return {upper: edge, lower: null};
@ -283,7 +368,7 @@ class Edge {
} }
} }
class TileStrip { class Strip {
edges: Edge[]; edges: Edge[];
tileTop: number; tileTop: number;
@ -301,6 +386,43 @@ class TileStrip {
} }
} }
class TileStrip {
tiles: Tile[];
tileTop: number;
constructor(tileTop: number) {
this.tiles = [];
this.tileTop = tileTop;
}
pushTile(tile: Tile): void {
this.tiles.push(tile);
}
tileBottom(): number {
return this.tileTop + TILE_SIZE.height;
}
}
class Tile {
edges: Edge[];
tileLeft: number;
constructor(tileLeft: number) {
this.edges = [];
this.tileLeft = tileLeft;
}
pushEdge(edge: Edge): void {
this.edges.push(edge);
}
}
interface ClippedEdgesX {
left: Edge | null;
right: Edge | null;
}
interface ClippedEdgesY { interface ClippedEdgesY {
upper: Edge | null; upper: Edge | null;
lower: Edge | null; lower: Edge | null;
@ -318,6 +440,9 @@ class Intervals {
} }
add(range: IntervalRange): void { add(range: IntervalRange): void {
//console.log("IntervalRange.add(", range, ")");
//console.log("... before ...", JSON.stringify(this));
this.splitAt(range.start); this.splitAt(range.start);
this.splitAt(range.end); this.splitAt(range.end);
@ -334,6 +459,8 @@ class Intervals {
this.ranges[i].winding += range.winding; this.ranges[i].winding += range.winding;
this.mergeAdjacent(); this.mergeAdjacent();
//console.log("... after ...", JSON.stringify(this));
} }
clear(): void { clear(): void {
@ -343,10 +470,10 @@ class Intervals {
private splitAt(value: number): void { private splitAt(value: number): void {
for (let i = 0; i < this.ranges.length; i++) { for (let i = 0; i < this.ranges.length; i++) {
if (this.ranges[i].start < value && value < this.ranges[i].end) { if (this.ranges[i].start < value && value < this.ranges[i].end) {
const firstRange = this.ranges[i]; const oldRange = this.ranges[i];
const secondRange = new IntervalRange(value, firstRange.end, firstRange.winding); const range0 = new IntervalRange(oldRange.start, value, oldRange.winding);
this.ranges.splice(i + 1, 0, secondRange); const range1 = new IntervalRange(value, oldRange.end, oldRange.winding);
firstRange.end = value; this.ranges.splice(i, 1, range0, range1);
break; break;
} }
} }
@ -401,28 +528,38 @@ export class TileDebugger {
this.updateSVGSize(); this.updateSVGSize();
} }
addTiler(tiler: Tiler, fillColor: string): void { addTiler(tiler: Tiler, fillColor: string, id: string): void {
const boundingRect = tiler.getBoundingRect(); const boundingRect = tiler.getBoundingRect();
this.size.width = Math.max(this.size.width, boundingRect.maxX()); this.size.width = Math.max(this.size.width, boundingRect.maxX());
this.size.height = Math.max(this.size.height, boundingRect.maxY()); this.size.height = Math.max(this.size.height, boundingRect.maxY());
for (const tileStrip of tiler.getTileStrips()) { const tileStrips = tiler.getTileStrips();
for (let tileStripIndex = 0; tileStripIndex < tileStrips.length; tileStripIndex++) {
const tileStrip = tileStrips[tileStripIndex];
const tileBottom = tileStrip.tileBottom(); const tileBottom = tileStrip.tileBottom();
let path = "";
for (const edge of tileStrip.edges) {
path += "M " + edge.from.x + " " + edge.from.y + " ";
path += "L " + edge.to.x + " " + edge.to.y + " ";
path += "L " + edge.to.x + " " + tileBottom + " ";
path += "L " + edge.from.x + " " + tileBottom + " ";
path += "Z ";
}
const pathElement = staticCast(document.createElementNS(SVG_NS, 'path'), for (let tileIndex = 0; tileIndex < tileStrip.tiles.length; tileIndex++) {
SVGPathElement); const tile = tileStrip.tiles[tileIndex];
pathElement.setAttribute('d', path);
pathElement.setAttribute('fill', fillColor); let path = "";
pathElement.setAttribute('stroke', "rgb(0, 128.0, 0)"); for (const edge of tile.edges) {
this.svg.appendChild(pathElement); path += "M " + edge.from.x + " " + edge.from.y + " ";
path += "L " + edge.to.x + " " + edge.to.y + " ";
path += "L " + edge.to.x + " " + tileBottom + " ";
path += "L " + edge.from.x + " " + tileBottom + " ";
path += "Z ";
}
const pathElement = staticCast(document.createElementNS(SVG_NS, 'path'),
SVGPathElement);
pathElement.setAttribute('d', path);
pathElement.setAttribute('fill', fillColor);
//pathElement.setAttribute('stroke', "rgb(0, 128.0, 0)");
pathElement.setAttribute('data-tile-id', id);
pathElement.setAttribute('data-tile-index', "" + tileIndex);
pathElement.setAttribute('data-tile-strip-index', "" + tileStripIndex);
this.svg.appendChild(pathElement);
}
} }
this.updateSVGSize(); this.updateSVGSize();

View File

@ -92,11 +92,11 @@ void main() {
vec2 dilation, position; vec2 dilation, position;
bool zeroArea = abs(from.x - to.x) < 0.00001; bool zeroArea = abs(from.x - to.x) < 0.00001;
if (aTessCoord.x < 0.5) { if (aTessCoord.x < 0.5) {
position.x = min(min(from.x, to.x), ctrl.x); position.x = from.x;
dilation.x = zeroArea ? 0.0 : -1.0; dilation.x = zeroArea ? 0.0 : (from.x < to.x ? -1.0 : 1.0);
} else { } else {
position.x = max(max(from.x, to.x), ctrl.x); position.x = to.x;
dilation.x = zeroArea ? 0.0 : 1.0; dilation.x = zeroArea ? 0.0 : (from.x < to.x ? 1.0 : -1.0);
} }
if (aTessCoord.y < 0.5) { if (aTessCoord.y < 0.5) {
position.y = min(min(from.y, to.y), ctrl.y); position.y = min(min(from.y, to.y), ctrl.y);
@ -116,7 +116,12 @@ void main() {
// Finish up. // Finish up.
gl_Position = vec4(offsetPosition, depth, 1.0); gl_Position = vec4(offsetPosition, depth, 1.0);
vFrom = (from - position) * framebufferSizeVector;
vCtrl = (ctrl - position) * framebufferSizeVector; vCtrl = (ctrl - position) * framebufferSizeVector;
vTo = (to - position) * framebufferSizeVector; if (from.x < to.x) {
vFrom = (from - position) * framebufferSizeVector;
vTo = (to - position) * framebufferSizeVector;
} else {
vFrom = (to - position) * framebufferSizeVector;
vTo = (from - position) * framebufferSizeVector;
}
} }