From 116ff8f3f85457d2f1f5a07f007a3889c6c6bcc7 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 23 Mar 2020 16:14:50 -0400 Subject: [PATCH] fix fill extrusion querying for colinear points When checking for intersections with a fill-extrusion it has to calculate the distance to the plane the polygon exist on. If the first points of the geometry were colinear this calculation would fail. It now: - finds two distinct starting points a, b - loops over all the remaining vertices looking for a value that is not colinear with a, b Usually this will be the first vertex it encounters. --- .../style_layer/fill_extrusion_style_layer.js | 46 ++++++------ .../mapbox-gl-js#8999-2/expected.json | 37 ++++++++++ .../mapbox-gl-js#8999-2/style.json | 70 +++++++++++++++++++ 3 files changed, 133 insertions(+), 20 deletions(-) create mode 100644 test/integration/query-tests/regressions/mapbox-gl-js#8999-2/expected.json create mode 100644 test/integration/query-tests/regressions/mapbox-gl-js#8999-2/style.json diff --git a/src/style/style_layer/fill_extrusion_style_layer.js b/src/style/style_layer/fill_extrusion_style_layer.js index 533fb8978f8..900879b8118 100644 --- a/src/style/style_layer/fill_extrusion_style_layer.js +++ b/src/style/style_layer/fill_extrusion_style_layer.js @@ -81,34 +81,40 @@ export function getIntersectionDistance(projectedQueryGeometry: Array, pr // Check whether points are coincident and use other points if they are. let i = 0; const a = projectedFace[i++]; - let b, c; + let b; while (!b || a.equals(b)) { b = projectedFace[i++]; if (!b) return Infinity; } - while (!c || a.equals(c) || b.equals(c)) { - c = projectedFace[i++]; - if (!c) return Infinity; - } - const p = projectedQueryGeometry[0]; + // Loop until point `c` is not colinear with points `a` and `b`. + for (; i < projectedFace.length; i++) { + const c = projectedFace[i]; + + const p = projectedQueryGeometry[0]; + + const ab = b.sub(a); + const ac = c.sub(a); + const ap = p.sub(a); - const ab = b.sub(a); - const ac = c.sub(a); - const ap = p.sub(a); + const dotABAB = dot(ab, ab); + const dotABAC = dot(ab, ac); + const dotACAC = dot(ac, ac); + const dotAPAB = dot(ap, ab); + const dotAPAC = dot(ap, ac); + const denom = dotABAB * dotACAC - dotABAC * dotABAC; - const dotABAB = dot(ab, ab); - const dotABAC = dot(ab, ac); - const dotACAC = dot(ac, ac); - const dotAPAB = dot(ap, ab); - const dotAPAC = dot(ap, ac); - const denom = dotABAB * dotACAC - dotABAC * dotABAC; - const v = (dotACAC * dotAPAB - dotABAC * dotAPAC) / denom; - const w = (dotABAB * dotAPAC - dotABAC * dotAPAB) / denom; - const u = 1 - v - w; + const v = (dotACAC * dotAPAB - dotABAC * dotAPAC) / denom; + const w = (dotABAB * dotAPAC - dotABAC * dotAPAB) / denom; + const u = 1 - v - w; + + // Use the barycentric weighting along with the original triangle z coordinates to get the point of intersection. + const distance = a.z * u + b.z * v + c.z * w; + + if (isFinite(distance)) return distance; + } - // Use the barycentric weighting along with the original triangle z coordinates to get the point of intersection. - return a.z * u + b.z * v + c.z * w; + return Infinity; } else { // The counts as closest is less clear when the query is a box. This diff --git a/test/integration/query-tests/regressions/mapbox-gl-js#8999-2/expected.json b/test/integration/query-tests/regressions/mapbox-gl-js#8999-2/expected.json new file mode 100644 index 00000000000..aa6b91a9b11 --- /dev/null +++ b/test/integration/query-tests/regressions/mapbox-gl-js#8999-2/expected.json @@ -0,0 +1,37 @@ +[ + { + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -19.9951171875, + -40.01078714046552 + ], + [ + -19.9951171875, + 40.01078714046551 + ], + [ + -19.9951171875, + 59.99898612060443 + ], + [ + 19.9951171875, + 20.014645445341372 + ], + [ + -19.9951171875, + -40.01078714046552 + ] + ] + ] + }, + "type": "Feature", + "properties": { + "layer": "extrusion_colinear_points" + }, + "source": "extrusion_colinear_points", + "state": {} + } +] \ No newline at end of file diff --git a/test/integration/query-tests/regressions/mapbox-gl-js#8999-2/style.json b/test/integration/query-tests/regressions/mapbox-gl-js#8999-2/style.json new file mode 100644 index 00000000000..3dcf313c594 --- /dev/null +++ b/test/integration/query-tests/regressions/mapbox-gl-js#8999-2/style.json @@ -0,0 +1,70 @@ +{ + "version": 8, + "metadata": { + "test": { + "debug": true, + "width": 200, + "height": 200, + "queryGeometry": [ + 100, + 40 + ] + } + }, + "pitch": 0, + "center": [ + 0, + 0 + ], + "zoom": 0, + "sources": { + "extrusion_colinear_points": { + "type": "geojson", + "tolerance": 0, + "data": { + "type": "Feature", + "properties": { + "layer": "extrusion_colinear_points" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -20, + -40 + ], + [ + -20, + 40 + ], + [ + -20, + 60 + ], + [ + 20, + 20 + ], + [ + -20, + -40 + ] + ] + ] + } + } + } + }, + "layers": [ + { + "id": "extrusion_colinear_points", + "type": "fill-extrusion", + "source": "extrusion_colinear_points", + "paint": { + "fill-extrusion-color": "#ecc", + "fill-extrusion-height": 0 + } + } + ] +}