Skip to content

Commit

Permalink
classify rings; switch to earcut only for tesselation
Browse files Browse the repository at this point in the history
  • Loading branch information
mourner committed Jan 20, 2015
1 parent 6a1444c commit df78343
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 59 deletions.
80 changes: 23 additions & 57 deletions js/data/fill_bucket.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
'use strict';

var ElementGroups = require('./element_groups');
var libtess = require('libtess');

var tesselator = initTesselator();
var earcut = require('earcut');
var classifyRings = require('../util/classify_rings');

module.exports = FillBucket;

Expand All @@ -15,79 +13,47 @@ function FillBucket(layoutProperties, buffers, placement, elementGroups) {
}

FillBucket.prototype.addFeatures = function() {
var features = this.features;
var fillVertex = this.buffers.fillVertex;
var fillElement = this.buffers.fillElement;
tesselator.gluTessCallback(libtess.gluEnum.GLU_TESS_VERTEX_DATA, addVertex);

var n = 0;
var elementGroups = this.elementGroups;
var fillVertex = this.buffers.fillVertex,
fillElement = this.buffers.fillElement;

var start = self.performance.now();
self.tesselateTime = self.tesselateTime || 0;

var vertices, m;

for (var i = features.length - 1; i >= 0; i--) {
var feature = features[i];
var lines = feature.loadGeometry();

if (lines.length > 1) {
tesselator.gluTessBeginPolygon();
for (var k = 0; k < lines.length; k++) {
vertices = lines[k];

tesselator.gluTessBeginContour();
for (m = 0; m < vertices.length; m++) {
var coords = [vertices[m].x, vertices[m].y, 0];
tesselator.gluTessVertex(coords, coords);
}
tesselator.gluTessEndContour();
}
tesselator.gluTessEndPolygon();
// console.count('complex');

} else {
// console.count('simple');
var contour = [];
vertices = lines[0];
for (m = 1; m < vertices.length; m++) {
contour.push([vertices[m].x, vertices[m].y]);
}
if (!contour.length) continue;
for (var i = this.features.length - 1; i >= 0; i--) {
var rings = this.features[i].loadGeometry();
var polygons = classifyRings(convertCoords(rings));

var triangles = earcut([contour]);
for (var j = 0; j < polygons.length; j++) {
if (polygons[j][0].length === 4603) console.log(JSON.stringify(polygons[j]));
var triangles = earcut(polygons[j]);
var elementGroup = this.elementGroups.makeRoomFor(triangles.length);

var elementGroup = this.elementGroups.makeRoomFor(m);
for (m = 0; m < triangles.length; m++) {
for (var m = 0; m < triangles.length; m++) {
var index = fillVertex.index - elementGroup.vertexStartIndex;
fillVertex.add(triangles[m][0], triangles[m][1]);
fillElement.add(index);
elementGroup.elementLength++;
elementGroup.vertexLength++;
}
}
}

self.tesselateTime += self.performance.now() - start;

function addVertex(data) {
if (n % 3 === 0) elementGroups.makeRoomFor(10);
var index = fillVertex.index - elementGroups.current.vertexStartIndex;
fillVertex.add(data[0], data[1]);
fillElement.add(index);
elementGroups.current.elementLength++;
n++;
}
};

FillBucket.prototype.hasData = function() {
return !!this.elementGroups.current;
};

function initTesselator() {
var tesselator = new libtess.GluTesselator();
tesselator.gluTessCallback(libtess.gluEnum.GLU_TESS_COMBINE, function(coords) { return coords; });
tesselator.gluTessCallback(libtess.gluEnum.GLU_TESS_EDGE_FLAG, function() {});
tesselator.gluTessNormal(0, 0, 1);
return tesselator;
function convertCoords(rings) {
var result = [];
for (var i = 0; i < rings.length; i++) {
var ring = [];
for (var j = 0; j < rings[i].length; j++) {
var p = rings[i][j];
ring.push([p.x, p.y]);
}
result.push(ring);
}
return result;
}
61 changes: 61 additions & 0 deletions js/util/classify_rings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'use strict';

module.exports = classifyRings;

// classifies an array of rings into polygons with outer rings and holes

function classifyRings(rings) {
var len = rings.length;

if (len <= 1) return [rings];

var i, j,
config = new Array(len);

for (i = 0; i < len; i++) {
if (config[i]) continue;
config[i] = false;
for (j = 0; j < len; j++) {
if (i === j || config[j]) continue;

if (ringPartiallyContains(rings[i], rings[j])) {

// mark i as outer ring; add j as inner ring
config[i] = config[i] || [rings[i]];
config[i].push(rings[j]);
config[j] = true; // mark j as inner ring
}
}
}

var polygons = [];
for (i = 0; i < len; i++) {
if (config[i] === false) polygons.push([rings[i]]);
else if (config[i].length) polygons.push(config[i]);
}

return polygons;
}

function ringPartiallyContains(outer, inner) {
var threshold = Math.min(Math.ceil(inner.length * 0.01), 10),
num = 0;
for (var i = 0; i < threshold * 2; i++) {
if (ringContains(outer, inner[i])) num++;
}
if (num >= threshold) return true;
}

function ringContains(points, p) {
var len = points.length,
inside = false,
i, j, p1, p2;

for (i = 0, j = len - 1; i < len; j = i++) {
p1 = points[i];
p2 = points[j];
if (((p1[1] > p[1]) !== (p2[1] > p[1])) &&
(p[0] < (p2[0] - p1[0]) * (p[1] - p1[1]) / (p2[1] - p1[1]) + p1[0])) inside = !inside;
}
return inside;
}
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"dependencies": {
"brfs": "1.2.0",
"csscolorparser": "~1.0.2",
"earcut": "^1.0.0",
"earcut": "^1.0.3",
"envify": "2.0.1",
"es6ify": "^1.5.1",
"feature-filter": "1.0.0",
Expand All @@ -22,7 +22,6 @@
"mapbox-gl-style-spec": "6.0.0",
"minifyify": "^4.4.0",
"pbf": "^1.2.0",
"libtess": "^1.0.2",
"pngjs": "^0.4.0",
"point-geometry": "0.0.0",
"rbush": "^1.3.4",
Expand Down

0 comments on commit df78343

Please sign in to comment.