Skip to content

Commit 729d459

Browse files
Sébastien Mischler (aka skarab)kaosat-dev
authored andcommitted
fix(2d shape factories): fix fromNestedPointsArray(#103)
* fix(fromNestedPointsArray) : polygon was not generated if only one points set is provided * add simple test : polygon(nested points array, with single path) * add contains() CAG validation: Check if all points from one CAG stay inside another CAG * use contains() rather than hasPointInside() : More robust verification. Without that, two shapes that shares a side does not behave as intended. 1 - two shape that do not touch -> union 2 - one shape that stay inside another -> difference 3 - one shape that intersect with another -> union 4 - two shape that share one or multiple edges -> union The last case did not happen as expected before this change.
1 parent 7d93db8 commit 729d459

File tree

3 files changed

+44
-10
lines changed

3 files changed

+44
-10
lines changed

src/api/primitives2d.test.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ test('polygon (params.points[][], params.paths[][] - two paths)', t => {
313313
t.truthy(comparePositonVertices(obs.sides, houseSides))
314314
})
315315

316-
test('polygon (points[][] - with holes)', t => {
316+
test('polygon (nested points array, with holes)', t => {
317317
const obs = polygon([
318318
[ [0,0], [0,10], [10,10], [10,0] ],
319319
[ [2,2], [2,8], [8,8], [8,2] ],
@@ -337,10 +337,25 @@ test('polygon (points[][] - with holes)', t => {
337337
[ [8,8], [8,2] ],
338338
[ [2,8], [8,8] ],
339339
[ [2,2], [2,8] ],
340-
[ [8,2], [2,2] ],
340+
[ [8,2], [2,2] ]
341341
]
342342

343343
// we just use a sample of points for simplicity
344344
t.deepEqual(obs.sides.length, 16)
345345
t.truthy(comparePositonVertices(obs.sides, expSides))
346346
})
347+
348+
test('polygon (nested points array, with single path)', t => {
349+
const obs = polygon([ [ [0,0], [0,10], [10,10], [10,0] ] ])
350+
351+
const expSides = [
352+
[ [10,0], [10,10] ],
353+
[ [10,10], [0,10] ],
354+
[ [0,10], [0,0] ],
355+
[ [0,0], [10,0] ]
356+
]
357+
358+
// we just use a sample of points for simplicity
359+
t.deepEqual(obs.sides.length, 4)
360+
t.truthy(comparePositonVertices(obs.sides, expSides))
361+
})

src/core/CAGFactories.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const Side = require('./math/Side')
22
const Vector2D = require('./math/Vector2')
33
const Vertex2 = require('./math/Vertex2')
44
const {areaEPS} = require('./constants')
5-
const {isSelfIntersecting, hasPointInside} = require('./utils/cagValidation')
5+
const {isSelfIntersecting, contains} = require('./utils/cagValidation')
66
const {union, difference} = require('../api/ops-booleans')
77

88
/** Construct a CAG from a list of `Side` instances.
@@ -80,26 +80,26 @@ const fromPointsArray = function (points) {
8080
}
8181

8282
const fromNestedPointsArray = function (points) {
83+
if (points.length === 1) {
84+
return fromPoints(points[0])
85+
}
8386
// First pass: create a collection of CAG paths
8487
let paths = []
8588
points.forEach(path => {
8689
paths.push(fromPointsArray(path))
8790
})
8891
// Second pass: make a tree of paths
8992
let tree = {}
90-
let point = null
91-
// for each polygon extract parents and childs polygons
93+
// for each polygon extract parents and childs polygons
9294
paths.forEach((p1, i) => {
93-
// only check for first point in polygon
94-
point = p1.sides[0].vertex0.pos
9595
// check for intersection
9696
paths.forEach((p2, y) => {
9797
if (p1 !== p2) {
9898
// create default node
9999
tree[i] || (tree[i] = { parents: [], isHole: false })
100100
tree[y] || (tree[y] = { parents: [], isHole: false })
101-
// check if point stay in poylgon
102-
if (hasPointInside(p2, point)) {
101+
// check if polygon2 stay in poylgon1
102+
if (contains(p2, p1)) {
103103
// push parent and child; odd parents number ==> hole
104104
tree[i].parents.push(y)
105105
tree[i].isHole = !! (tree[i].parents.length % 2)

src/core/utils/cagValidation.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,23 @@ const hasPointInside = function (cag, p0) {
7878
hasPointInside.c1 = (p0, p1, p2) => (p1.y > p0.y) !== (p2.y > p0.y)
7979
hasPointInside.c2 = (p0, p1, p2) => (p0.x < (p2.x - p1.x) * (p0.y - p1.y) / (p2.y - p1.y) + p1.x)
8080

81-
module.exports = {isCAGValid, isSelfIntersecting, hasPointInside}
81+
/** Check if all points from one CAG stay inside another CAG
82+
* @param {CAG} cag1 - CAG object
83+
* @param {Object} cag2 - CAG object
84+
* @returns {Boolean}
85+
*/
86+
const contains = function (cag1, cag2) {
87+
for (let i = 0, il = cag2.sides.length; i < il; i++) {
88+
if (!hasPointInside(cag1, cag2.sides[i].vertex0.pos)) {
89+
return false
90+
}
91+
}
92+
return true
93+
}
94+
95+
module.exports = {
96+
isCAGValid,
97+
isSelfIntersecting,
98+
hasPointInside,
99+
contains
100+
}

0 commit comments

Comments
 (0)