Skip to content

Commit

Permalink
Add fill-extrude-height and fill-extrude-base properties. (#3223)
Browse files Browse the repository at this point in the history
馃帀 馃彈 馃啓
  • Loading branch information
Lauren Budorick committed Oct 8, 2016
1 parent 5994bc3 commit c8cbb9d
Show file tree
Hide file tree
Showing 15 changed files with 848 additions and 22 deletions.
2 changes: 1 addition & 1 deletion debug/index.html
Expand Up @@ -3,7 +3,7 @@
<head>
<title>Mapbox GL JS debug page</title>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

<link rel='stylesheet' href='/dist/mapbox-gl.css' />
<style>
Expand Down
11 changes: 10 additions & 1 deletion js/data/bucket.js
Expand Up @@ -17,11 +17,20 @@ module.exports = Bucket;
Bucket.create = function(options) {
var Classes = {
fill: require('./bucket/fill_bucket'),
fillextrusion: require('./bucket/fill_extrusion_bucket'),
line: require('./bucket/line_bucket'),
circle: require('./bucket/circle_bucket'),
symbol: require('./bucket/symbol_bucket')
};
return new Classes[options.layer.type](options);

var type = options.layer.type;
if (type === 'fill' && (!options.layer.isPaintValueFeatureConstant('fill-extrude-height') ||
!options.layer.isPaintValueZoomConstant('fill-extrude-height') ||
options.layer.getPaintValue('fill-extrude-height') !== 0)) {
type = 'fillextrusion';
}

return new Classes[type](options);
};


Expand Down
25 changes: 20 additions & 5 deletions js/data/bucket/fill_bucket.js
Expand Up @@ -5,6 +5,7 @@ var util = require('../../util/util');
var loadGeometry = require('../load_geometry');
var earcut = require('earcut');
var classifyRings = require('../../util/classify_rings');
var Point = require('point-geometry');
var EARCUT_MAX_RINGS = 500;

module.exports = FillBucket;
Expand Down Expand Up @@ -56,9 +57,15 @@ FillBucket.prototype.programInterfaces = {
}
};

FillBucket.prototype.addVertex = function(vertexArray, x, y) {
return vertexArray.emplaceBack(x, y);
};

FillBucket.prototype.addFeature = function(feature) {
var lines = loadGeometry(feature);
var polygons = classifyRings(lines, EARCUT_MAX_RINGS);
var polygons = convertCoords(classifyRings(lines, EARCUT_MAX_RINGS));

this.factor = Math.pow(2, 13);

var startGroup = this.prepareArrayGroup('fill', 0);
var startIndex = startGroup.layoutVertexArray.length;
Expand All @@ -81,23 +88,26 @@ FillBucket.prototype.addPolygon = function(polygon) {
var holeIndices = [];
var startIndex = group.layoutVertexArray.length;

var indices = [];

for (var r = 0; r < polygon.length; r++) {
var ring = polygon[r];

if (r > 0) holeIndices.push(flattened.length / 2);

for (var v = 0; v < ring.length; v++) {
var vertex = ring[v];
var v1 = ring[v];

var index = group.layoutVertexArray.emplaceBack(vertex.x, vertex.y);
var index = this.addVertex(group.layoutVertexArray, v1[0], v1[1], 0, 0, 1, 1, 0);
indices.push(index);

if (v >= 1) {
group.elementArray2.emplaceBack(index - 1, index);
}

// convert to format used by earcut
flattened.push(vertex.x);
flattened.push(vertex.y);
flattened.push(v1[0]);
flattened.push(v1[1]);
}
}

Expand All @@ -107,3 +117,8 @@ FillBucket.prototype.addPolygon = function(polygon) {
group.elementArray.emplaceBack(triangleIndices[i] + startIndex);
}
};

function convertCoords(rings) {
if (rings instanceof Point) return [rings.x, rings.y];
return rings.map(convertCoords);
}
173 changes: 173 additions & 0 deletions js/data/bucket/fill_extrusion_bucket.js
@@ -0,0 +1,173 @@
'use strict';

var Bucket = require('../bucket');
var util = require('../../util/util');
var loadGeometry = require('../load_geometry');
var earcut = require('earcut');
var classifyRings = require('../../util/classify_rings');
var Point = require('point-geometry');
var EARCUT_MAX_RINGS = 500;

module.exports = FillExtrusionBucket;

function FillExtrusionBucket() {
Bucket.apply(this, arguments);
}

FillExtrusionBucket.prototype = util.inherit(Bucket, {});

FillExtrusionBucket.prototype.programInterfaces = {
fillextrusion: {
layoutVertexArrayType: new Bucket.VertexArrayType([{
name: 'a_pos',
components: 2,
type: 'Int16'
}, {
name: 'a_normal',
components: 3,
type: 'Int16'
}, {
name: 'a_edgedistance',
components: 1,
type: 'Int16'
}]),
elementArrayType: new Bucket.ElementArrayType(3),

paintAttributes: [{
name: 'a_minH',
components: 1,
type: 'Uint16',
getValue: function(layer, globalProperties, featureProperties) {
return [layer.getPaintValue("fill-extrude-base", globalProperties, featureProperties)];
},
multiplier: 1,
paintProperty: 'fill-extrude-base'
}, {
name: 'a_maxH',
components: 1,
type: 'Uint16',
getValue: function(layer, globalProperties, featureProperties) {
return [layer.getPaintValue("fill-extrude-height", globalProperties, featureProperties)];
},
multiplier: 1,
paintProperty: 'fill-extrude-height'
}, {
name: 'a_color',
components: 4,
type: 'Uint8',
getValue: function(layer, globalProperties, featureProperties) {
var color = layer.getPaintValue("fill-color", globalProperties, featureProperties);
color[3] = 1.0;
return color;
},
multiplier: 255,
paintProperty: 'fill-color'
}]
}
};

FillExtrusionBucket.prototype.addVertex = function(vertexArray, x, y, nx, ny, nz, t, e) {
return vertexArray.emplaceBack(
// a_pos
x,
y,
// a_normal
Math.floor(nx * this.factor) * 2 + t,
ny * this.factor * 2,
nz * this.factor * 2,

// a_edgedistance
Math.round(e)
);
};

FillExtrusionBucket.prototype.addFeature = function(feature) {
var lines = loadGeometry(feature);
var polygons = convertCoords(classifyRings(lines, EARCUT_MAX_RINGS));

this.factor = Math.pow(2, 13);

var startGroup = this.prepareArrayGroup('fillextrusion', 0);
var startIndex = startGroup.layoutVertexArray.length;

for (var i = 0; i < polygons.length; i++) {
this.addPolygon(polygons[i]);
}

this.populatePaintArrays('fillextrusion', {zoom: this.zoom}, feature.properties, startGroup, startIndex);
};

FillExtrusionBucket.prototype.addPolygon = function(polygon) {
var numVertices = 0;
for (var k = 0; k < polygon.length; k++) {
numVertices += polygon[k].length;
}
numVertices *= 5;

var group = this.prepareArrayGroup('fillextrusion', numVertices);
var flattened = [];
var holeIndices = [];

var indices = [];

for (var r = 0; r < polygon.length; r++) {
var ring = polygon[r];

if (r > 0) holeIndices.push(flattened.length / 2);

var edgeDistance = 0;

for (var v = 0; v < ring.length; v++) {
var v1 = ring[v];

var index = this.addVertex(group.layoutVertexArray, v1[0], v1[1], 0, 0, 1, 1, 0);
indices.push(index);

if (v >= 1) {
var v2 = ring[v - 1];

if (!isBoundaryEdge(v1, v2)) {
var perp = Point.convert(v1)._sub(Point.convert(v2))._perp()._unit();

var bottomRight = this.addVertex(group.layoutVertexArray, v1[0], v1[1], perp.x, perp.y, 0, 0, edgeDistance);
this.addVertex(group.layoutVertexArray, v1[0], v1[1], perp.x, perp.y, 0, 1, edgeDistance);

edgeDistance += Point.convert(v2).dist(Point.convert(v1));

this.addVertex(group.layoutVertexArray, v2[0], v2[1], perp.x, perp.y, 0, 0, edgeDistance);
this.addVertex(group.layoutVertexArray, v2[0], v2[1], perp.x, perp.y, 0, 1, edgeDistance);

group.elementArray.emplaceBack(bottomRight, bottomRight + 1, bottomRight + 2);
group.elementArray.emplaceBack(bottomRight + 1, bottomRight + 2, bottomRight + 3);
}
}

// convert to format used by earcut
flattened.push(v1[0]);
flattened.push(v1[1]);
}
}

var triangleIndices = earcut(flattened, holeIndices);

for (var j = 0; j < triangleIndices.length - 2; j += 3) {
group.elementArray.emplaceBack(indices[triangleIndices[j]],
indices[triangleIndices[j + 1]],
indices[triangleIndices[j + 2]]);
}
};

function convertCoords(rings) {
if (rings instanceof Point) return [rings.x, rings.y];
return rings.map(convertCoords);
}

function isBoundaryEdge(v1, v2) {
return v1.some(function(a, i) {
return isOutside(v2[i]) && v2[i] === a;
});
}

function isOutside(coord) {
return coord < 0 || coord > Bucket.EXTENT;
}

0 comments on commit c8cbb9d

Please sign in to comment.