New tiling mostly working
This commit is contained in:
parent
79e69c6916
commit
bc3f6b6ae5
|
@ -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);
|
||||||
|
|
183
demo2/tiling.ts
183
demo2/tiling.ts
|
@ -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,8 +220,19 @@ 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;
|
||||||
|
const clipped = this.clipEdgeX(edge, tileRight);
|
||||||
|
|
||||||
|
if (clipped.left != null)
|
||||||
|
tile.pushEdge(clipped.left);
|
||||||
|
|
||||||
|
if (clipped.right != null)
|
||||||
|
activeEdges.push(clipped.right);
|
||||||
|
}
|
||||||
|
|
||||||
|
private processEdgeY(edge: Edge,
|
||||||
|
tileStrip: Strip,
|
||||||
activeEdges: Edge[],
|
activeEdges: Edge[],
|
||||||
intervals: Intervals,
|
intervals: Intervals,
|
||||||
tileTop: number):
|
tileTop: number):
|
||||||
|
@ -178,18 +241,40 @@ export class Tiler {
|
||||||
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,15 +528,21 @@ 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();
|
||||||
|
|
||||||
|
for (let tileIndex = 0; tileIndex < tileStrip.tiles.length; tileIndex++) {
|
||||||
|
const tile = tileStrip.tiles[tileIndex];
|
||||||
|
|
||||||
let path = "";
|
let path = "";
|
||||||
for (const edge of tileStrip.edges) {
|
for (const edge of tile.edges) {
|
||||||
path += "M " + edge.from.x + " " + edge.from.y + " ";
|
path += "M " + edge.from.x + " " + edge.from.y + " ";
|
||||||
path += "L " + edge.to.x + " " + edge.to.y + " ";
|
path += "L " + edge.to.x + " " + edge.to.y + " ";
|
||||||
path += "L " + edge.to.x + " " + tileBottom + " ";
|
path += "L " + edge.to.x + " " + tileBottom + " ";
|
||||||
|
@ -421,9 +554,13 @@ export class TileDebugger {
|
||||||
SVGPathElement);
|
SVGPathElement);
|
||||||
pathElement.setAttribute('d', path);
|
pathElement.setAttribute('d', path);
|
||||||
pathElement.setAttribute('fill', fillColor);
|
pathElement.setAttribute('fill', fillColor);
|
||||||
pathElement.setAttribute('stroke', "rgb(0, 128.0, 0)");
|
//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.svg.appendChild(pathElement);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.updateSVGSize();
|
this.updateSVGSize();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
if (from.x < to.x) {
|
||||||
|
vFrom = (from - position) * framebufferSizeVector;
|
||||||
vTo = (to - position) * framebufferSizeVector;
|
vTo = (to - position) * framebufferSizeVector;
|
||||||
|
} else {
|
||||||
|
vFrom = (to - position) * framebufferSizeVector;
|
||||||
|
vTo = (from - position) * framebufferSizeVector;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue