@jscad/modeling 3.0.4-alpha.0 → 3.0.6-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/jscad-modeling.es.js +2 -2
- package/dist/jscad-modeling.min.js +2 -2
- package/package.json +4 -4
- package/rollup.config.js +1 -1
- package/src/colors/colorize.js +17 -1
- package/src/colors/hsvToRgb.js +1 -0
- package/src/colors/rgbToHsl.js +3 -1
- package/src/colors/rgbToHsv.js +1 -0
- package/src/curves/bezier/create.js +1 -1
- package/src/geometries/geom2/fromPoints.d.ts +4 -0
- package/src/geometries/geom2/fromPoints.js +28 -0
- package/src/geometries/geom2/fromPoints.test.js +22 -0
- package/src/geometries/geom2/index.d.ts +1 -0
- package/src/geometries/geom2/index.js +1 -0
- package/src/geometries/geom2/transform.js +1 -0
- package/src/geometries/geom3/transform.js +1 -0
- package/src/geometries/geom3/validate.js +11 -1
- package/src/geometries/path2/appendArc.js +19 -25
- package/src/geometries/path2/appendBezier.js +8 -11
- package/src/geometries/path2/appendPoints.js +21 -3
- package/src/geometries/path2/appendPoints.test.js +16 -4
- package/src/geometries/path2/close.js +7 -10
- package/src/geometries/path2/concat.js +2 -2
- package/src/geometries/path2/fromPoints.js +7 -7
- package/src/geometries/path2/reverse.js +3 -3
- package/src/geometries/path2/transform.js +1 -0
- package/src/geometries/path2/validate.js +2 -2
- package/src/geometries/path3/clone.d.ts +3 -0
- package/src/geometries/path3/clone.js +11 -0
- package/src/geometries/path3/close.js +7 -8
- package/src/geometries/path3/index.d.ts +1 -0
- package/src/geometries/path3/index.js +1 -0
- package/src/geometries/path3/transform.js +1 -0
- package/src/geometries/poly2/type.d.ts +1 -5
- package/src/geometries/slice/earcut/index.js +4 -2
- package/src/geometries/slice/earcut/linkedPolygon.js +4 -2
- package/src/geometries/slice/fromOutlines.d.ts +5 -0
- package/src/geometries/slice/fromOutlines.js +16 -0
- package/src/geometries/slice/fromOutlines.test.js +17 -0
- package/src/geometries/slice/index.d.ts +1 -1
- package/src/geometries/slice/index.js +1 -1
- package/src/geometries/slice/transform.js +2 -1
- package/src/maths/line2/intersectPointOfLines.js +1 -4
- package/src/maths/plane/fromNormalAndPoint.js +4 -6
- package/src/maths/plane/fromPoints.js +8 -7
- package/src/maths/plane/fromPointsRandom.js +13 -13
- package/src/measurements/measureAggregateEpsilon.js +3 -1
- package/src/measurements/measureAggregateEpsilon.test.js +1 -1
- package/src/measurements/measureArea.js +6 -4
- package/src/measurements/measureArea.test.js +4 -1
- package/src/measurements/measureBoundingBox.js +16 -2
- package/src/measurements/measureBoundingBox.test.js +4 -1
- package/src/measurements/measureBoundingSphere.js +38 -29
- package/src/measurements/measureBoundingSphere.test.js +4 -1
- package/src/measurements/measureCenterOfMass.js +3 -2
- package/src/measurements/measureEpsilon.js +4 -2
- package/src/operations/booleans/martinez/computeFields.js +6 -0
- package/src/operations/booleans/martinez/divideSegment.js +2 -1
- package/src/operations/booleans/martinez/fillQueue.js +3 -1
- package/src/operations/booleans/martinez/subdivideSegments.js +5 -4
- package/src/operations/booleans/subtractGeom3Sub.js +1 -1
- package/src/operations/booleans/trees/PolygonTreeNode.js +3 -0
- package/src/operations/booleans/trees/splitPolygonByPlane.d.ts +1 -3
- package/src/operations/booleans/union.js +1 -1
- package/src/operations/extrusions/extrudeFromSlices.js +1 -1
- package/src/operations/extrusions/extrudeFromSlices.test.js +1 -1
- package/src/operations/extrusions/extrudeHelical.js +2 -1
- package/src/operations/extrusions/extrudeLinear.js +1 -1
- package/src/operations/extrusions/extrudeLinearGeom2.js +12 -8
- package/src/operations/extrusions/extrudeRotate.js +1 -1
- package/src/operations/hulls/hull.js +3 -2
- package/src/operations/hulls/quickhull/Face.js +8 -3
- package/src/operations/hulls/quickhull/QuickHull.js +10 -5
- package/src/operations/hulls/quickhull/VertexList.js +2 -1
- package/src/operations/hulls/toUniquePoints.js +3 -0
- package/src/operations/modifiers/generalize.js +9 -2
- package/src/operations/modifiers/reTesselateCoplanarPolygons.js +2 -2
- package/src/operations/modifiers/reTesselateCoplanarPolygons.test.js +29 -3
- package/src/operations/modifiers/retessellate.js +1 -0
- package/src/operations/modifiers/snap.js +22 -3
- package/src/operations/modifiers/snap.test.js +12 -2
- package/src/operations/offsets/offsetGeom3.test.js +2 -2
- package/src/operations/transforms/align.js +2 -1
- package/src/operations/transforms/align.test.js +1 -1
- package/src/operations/transforms/mirror.js +6 -2
- package/src/operations/transforms/rotate.js +6 -2
- package/src/operations/transforms/scale.js +6 -2
- package/src/operations/transforms/transform.js +6 -2
- package/src/operations/transforms/transform.test.js +16 -5
- package/src/operations/transforms/translate.js +12 -4
- package/src/primitives/arc.js +14 -14
- package/src/primitives/circle.js +10 -9
- package/src/primitives/cube.js +5 -6
- package/src/primitives/cuboid.js +6 -6
- package/src/primitives/cylinder.js +8 -8
- package/src/primitives/cylinderElliptic.js +11 -11
- package/src/primitives/ellipse.js +12 -12
- package/src/primitives/ellipsoid.js +8 -8
- package/src/primitives/geodesicSphere.js +6 -6
- package/src/primitives/line.js +2 -0
- package/src/primitives/polygon.js +6 -7
- package/src/primitives/polyhedron.js +7 -8
- package/src/primitives/rectangle.js +6 -6
- package/src/primitives/roundedCuboid.js +8 -8
- package/src/primitives/roundedCylinder.js +9 -9
- package/src/primitives/roundedRectangle.js +8 -8
- package/src/primitives/sphere.js +7 -8
- package/src/primitives/square.js +6 -6
- package/src/primitives/star.js +12 -14
- package/src/primitives/torus.js +11 -11
- package/src/primitives/triangle.js +7 -6
- package/src/utils/areAllShapesTheSameType.js +4 -0
- package/test/helpers/nearlyEqual.js +1 -0
- package/src/geometries/slice/fromGeom2.d.ts +0 -5
- package/src/geometries/slice/fromGeom2.js +0 -17
|
@@ -28,8 +28,10 @@ export const triangulate = (data, holeIndices, dim = 2) => {
|
|
|
28
28
|
|
|
29
29
|
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
|
|
30
30
|
if (data.length > 80 * dim) {
|
|
31
|
-
minX =
|
|
32
|
-
|
|
31
|
+
minX = data[0]
|
|
32
|
+
maxX = data[0]
|
|
33
|
+
minY = data[1]
|
|
34
|
+
maxY = data[1]
|
|
33
35
|
|
|
34
36
|
for (let i = dim; i < outerLen; i += dim) {
|
|
35
37
|
const x = data[i]
|
|
@@ -39,7 +39,8 @@ export const filterPoints = (start, end) => {
|
|
|
39
39
|
|
|
40
40
|
if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
|
|
41
41
|
removeNode(p)
|
|
42
|
-
p =
|
|
42
|
+
p = p.prev
|
|
43
|
+
end = p.prev
|
|
43
44
|
if (p === p.next) break
|
|
44
45
|
again = true
|
|
45
46
|
} else {
|
|
@@ -68,7 +69,8 @@ export const cureLocalIntersections = (start, triangles, dim) => {
|
|
|
68
69
|
removeNode(p)
|
|
69
70
|
removeNode(p.next)
|
|
70
71
|
|
|
71
|
-
p =
|
|
72
|
+
p = b
|
|
73
|
+
start = b
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
p = p.next
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as vec3 from '../../maths/vec3/index.js'
|
|
2
|
+
|
|
3
|
+
import { create } from './create.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Create a slice from the given outlines.
|
|
7
|
+
*
|
|
8
|
+
* @param {Array} outlines - where each outline is an array of ordered points
|
|
9
|
+
* @returns {Slice} a new slice
|
|
10
|
+
* @alias module:modeling/slice.fromOutlines
|
|
11
|
+
*/
|
|
12
|
+
export const fromOutlines = (outlines) => {
|
|
13
|
+
// Convert from 2D points to 3D vertices
|
|
14
|
+
const contours = outlines.map((outline) => outline.map((point) => vec3.fromVec2(vec3.create(), point)))
|
|
15
|
+
return create(contours)
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import test from 'ava'
|
|
2
|
+
|
|
3
|
+
import { fromOutlines } from './index.js'
|
|
4
|
+
|
|
5
|
+
test('slice: fromOutlines() should return a new slice with correct values', (t) => {
|
|
6
|
+
const exp1 = {
|
|
7
|
+
contours: [
|
|
8
|
+
[[0, 0, 0], [1, 0, 0], [1, 1, 0]],
|
|
9
|
+
[[2, 2, 0], [3, 1, 0], [3, 3, 0]]
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
const obs1 = fromOutlines([
|
|
13
|
+
[[0, 0], [1, 0], [1, 1]],
|
|
14
|
+
[[2, 2], [3, 1], [3, 3]]
|
|
15
|
+
])
|
|
16
|
+
t.deepEqual(obs1, exp1)
|
|
17
|
+
})
|
|
@@ -2,7 +2,7 @@ export { calculatePlane } from './calculatePlane.js'
|
|
|
2
2
|
export { clone } from './clone.js'
|
|
3
3
|
export { create } from './create.js'
|
|
4
4
|
export { equals } from './equals.js'
|
|
5
|
-
export {
|
|
5
|
+
export { fromOutlines } from './fromOutlines.js'
|
|
6
6
|
export { fromVertices } from './fromVertices.js'
|
|
7
7
|
export { isA } from './isA.js'
|
|
8
8
|
export { reverse } from './reverse.js'
|
|
@@ -26,7 +26,7 @@ export { calculatePlane } from './calculatePlane.js'
|
|
|
26
26
|
export { clone } from './clone.js'
|
|
27
27
|
export { create } from './create.js'
|
|
28
28
|
export { equals } from './equals.js'
|
|
29
|
-
export {
|
|
29
|
+
export { fromOutlines } from './fromOutlines.js'
|
|
30
30
|
export { fromVertices } from './fromVertices.js'
|
|
31
31
|
export { isA } from './isA.js'
|
|
32
32
|
export { reverse } from './reverse.js'
|
|
@@ -16,5 +16,6 @@ import { create } from './create.js'
|
|
|
16
16
|
*/
|
|
17
17
|
export const transform = (matrix, slice) => {
|
|
18
18
|
const contours = slice.contours.map((contour) => contour.map((vertex) => vec3.transform(vec3.create(), vertex, matrix)))
|
|
19
|
-
|
|
19
|
+
// use Object.assign in order to assign EXTRA attributes like color, name, etc
|
|
20
|
+
return Object.assign({}, slice, { contours })
|
|
20
21
|
}
|
|
@@ -14,7 +14,4 @@ import { solve2Linear } from '../utils/solve2Linear.js'
|
|
|
14
14
|
* @return {Vec2} the point of intersection
|
|
15
15
|
* @alias module:modeling/maths/line2.intersectPointOfLines
|
|
16
16
|
*/
|
|
17
|
-
export const intersectPointOfLines = (line1, line2) =>
|
|
18
|
-
const point = solve2Linear(line1[0], line1[1], line2[0], line2[1], line1[2], line2[2])
|
|
19
|
-
return vec2.clone(point)
|
|
20
|
-
}
|
|
17
|
+
export const intersectPointOfLines = (line1, line2) => solve2Linear(line1[0], line1[1], line2[0], line2[1], line1[2], line2[2])
|
|
@@ -18,12 +18,10 @@ import * as vec3 from '../vec3/index.js'
|
|
|
18
18
|
* @alias module:modeling/maths/plane.fromNormalAndPoint
|
|
19
19
|
*/
|
|
20
20
|
export const fromNormalAndPoint = (out, normal, point) => {
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
// normalize to out
|
|
22
|
+
vec3.normalize(out, normal)
|
|
23
|
+
// calculate distance
|
|
24
|
+
out[3] = vec3.dot(point, out)
|
|
23
25
|
|
|
24
|
-
out[0] = u[0]
|
|
25
|
-
out[1] = u[1]
|
|
26
|
-
out[2] = u[2]
|
|
27
|
-
out[3] = w
|
|
28
26
|
return out
|
|
29
27
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import * as vec3 from '../vec3/index.js'
|
|
2
2
|
|
|
3
|
+
const ba = vec3.create()
|
|
4
|
+
const ca = vec3.create()
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* Create a plane from the given points.
|
|
5
8
|
*
|
|
6
9
|
* @param {Plane} out - receiving plane
|
|
7
|
-
* @param {Array} vertices - points on the plane
|
|
10
|
+
* @param {Array} vertices - list of points on the plane
|
|
8
11
|
* @returns {Plane} out
|
|
9
12
|
* @alias module:modeling/maths/plane.fromPoints
|
|
10
13
|
*/
|
|
@@ -13,8 +16,6 @@ export const fromPoints = (out, ...vertices) => {
|
|
|
13
16
|
|
|
14
17
|
// Calculate normal vector for a single vertex
|
|
15
18
|
// Inline to avoid allocations
|
|
16
|
-
const ba = vec3.create()
|
|
17
|
-
const ca = vec3.create()
|
|
18
19
|
const vertexNormal = (index) => {
|
|
19
20
|
const a = vertices[index]
|
|
20
21
|
const b = vertices[(index + 1) % len]
|
|
@@ -26,18 +27,18 @@ export const fromPoints = (out, ...vertices) => {
|
|
|
26
27
|
return ba
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
out[0] = 0
|
|
30
|
-
out[1] = 0
|
|
31
|
-
out[2] = 0
|
|
32
30
|
if (len === 3) {
|
|
33
31
|
// optimization for triangles, which are always coplanar
|
|
34
32
|
vec3.copy(out, vertexNormal(0))
|
|
35
33
|
} else {
|
|
36
34
|
// sum of vertex normals
|
|
35
|
+
out[0] = 0
|
|
36
|
+
out[1] = 0
|
|
37
|
+
out[2] = 0
|
|
37
38
|
vertices.forEach((v, i) => {
|
|
38
39
|
vec3.add(out, out, vertexNormal(i))
|
|
39
40
|
})
|
|
40
|
-
//
|
|
41
|
+
// normalize sum
|
|
41
42
|
vec3.normalize(out, out)
|
|
42
43
|
}
|
|
43
44
|
out[3] = vec3.dot(out, vertices[0])
|
|
@@ -15,26 +15,26 @@ import * as vec3 from '../vec3/index.js'
|
|
|
15
15
|
* @alias module:modeling/maths/plane.fromPointsRandom
|
|
16
16
|
*/
|
|
17
17
|
export const fromPointsRandom = (out, a, b, c) => {
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
const ba = vec3.subtract(vec3.create(), b, a)
|
|
19
|
+
const ca = vec3.subtract(vec3.create(), c, a)
|
|
20
20
|
if (vec3.length(ba) < EPS) {
|
|
21
|
-
|
|
21
|
+
vec3.orthogonal(ba, ca)
|
|
22
22
|
}
|
|
23
23
|
if (vec3.length(ca) < EPS) {
|
|
24
|
-
|
|
24
|
+
vec3.orthogonal(ca, ba)
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
// calculate plane normal
|
|
28
|
+
const normal = vec3.cross(out, ba, ca)
|
|
27
29
|
if (vec3.length(normal) < EPS) {
|
|
28
30
|
// this would mean that ba == ca.negated()
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
vec3.orthogonal(ca, ba)
|
|
32
|
+
vec3.cross(normal, ba, ca)
|
|
31
33
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
vec3.normalize(normal, normal)
|
|
35
|
+
|
|
36
|
+
// and distance
|
|
37
|
+
out[3] = vec3.dot(normal, a)
|
|
34
38
|
|
|
35
|
-
out[0] = normal[0]
|
|
36
|
-
out[1] = normal[1]
|
|
37
|
-
out[2] = normal[2]
|
|
38
|
-
out[3] = w
|
|
39
39
|
return out
|
|
40
40
|
}
|
|
@@ -3,6 +3,8 @@ import { flatten } from '../utils/flatten.js'
|
|
|
3
3
|
import * as geom2 from '../geometries/geom2/index.js'
|
|
4
4
|
import * as geom3 from '../geometries/geom3/index.js'
|
|
5
5
|
import * as path2 from '../geometries/path2/index.js'
|
|
6
|
+
import * as path3 from '../geometries/path3/index.js'
|
|
7
|
+
import * as slice from '../geometries/slice/index.js'
|
|
6
8
|
|
|
7
9
|
import { measureAggregateBoundingBox } from './measureAggregateBoundingBox.js'
|
|
8
10
|
import { calculateEpsilonFromBounds } from './calculateEpsilonFromBounds.js'
|
|
@@ -23,7 +25,7 @@ export const measureAggregateEpsilon = (...geometries) => {
|
|
|
23
25
|
let dimensions = 0
|
|
24
26
|
dimensions = geometries.reduce((dimensions, geometry) => {
|
|
25
27
|
if (path2.isA(geometry) || geom2.isA(geometry)) return Math.max(dimensions, 2)
|
|
26
|
-
if (geom3.isA(geometry)) return Math.max(dimensions, 3)
|
|
28
|
+
if (geom3.isA(geometry || path3.isA(geometry) || slice.isA(geometry))) return Math.max(dimensions, 3)
|
|
27
29
|
return 0
|
|
28
30
|
}, dimensions)
|
|
29
31
|
return calculateEpsilonFromBounds(bounds, dimensions)
|
|
@@ -13,7 +13,7 @@ test('measureAggregateEpsilon (single objects)', (t) => {
|
|
|
13
13
|
t.is(calculatedEpsilon, expectedEpsilon)
|
|
14
14
|
})
|
|
15
15
|
|
|
16
|
-
test('measureAggregateEpsilon (multiple objects)', (t) => {
|
|
16
|
+
test('measureAggregateEpsilon (multiple 3D objects)', (t) => {
|
|
17
17
|
const highCube = cube({ size: 4, center: [-40, 100, 20] })
|
|
18
18
|
const lowCube = cube({ size: 60, center: [20, -10, 20] })
|
|
19
19
|
const calculatedEpsilon = measureAggregateEpsilon(highCube, lowCube)
|
|
@@ -3,6 +3,7 @@ import { flatten } from '../utils/flatten.js'
|
|
|
3
3
|
import * as geom2 from '../geometries/geom2/index.js'
|
|
4
4
|
import * as geom3 from '../geometries/geom3/index.js'
|
|
5
5
|
import * as path2 from '../geometries/path2/index.js'
|
|
6
|
+
import * as path3 from '../geometries/path3/index.js'
|
|
6
7
|
import * as poly3 from '../geometries/poly3/index.js'
|
|
7
8
|
import * as slice from '../geometries/slice/index.js'
|
|
8
9
|
|
|
@@ -12,10 +13,10 @@ const cache = new WeakMap()
|
|
|
12
13
|
* Measure the area of the given geometry.
|
|
13
14
|
* NOTE: paths are infinitely narrow and do not have an area
|
|
14
15
|
*
|
|
15
|
-
* @param {Path2} geometry - geometry to measure
|
|
16
|
+
* @param {Path2|Path3} geometry - geometry to measure
|
|
16
17
|
* @returns {number} area of the geometry
|
|
17
18
|
*/
|
|
18
|
-
const
|
|
19
|
+
const measureAreaOfPath = () => 0
|
|
19
20
|
|
|
20
21
|
/*
|
|
21
22
|
* Measure the area of the given geometry.
|
|
@@ -87,9 +88,10 @@ export const measureArea = (...geometries) => {
|
|
|
87
88
|
geometries = flatten(geometries)
|
|
88
89
|
|
|
89
90
|
const results = geometries.map((geometry) => {
|
|
90
|
-
if (path2.isA(geometry)) return measureAreaOfPath2(geometry)
|
|
91
|
-
if (geom2.isA(geometry)) return measureAreaOfGeom2(geometry)
|
|
92
91
|
if (geom3.isA(geometry)) return measureAreaOfGeom3(geometry)
|
|
92
|
+
if (geom2.isA(geometry)) return measureAreaOfGeom2(geometry)
|
|
93
|
+
if (path2.isA(geometry)) return measureAreaOfPath(geometry)
|
|
94
|
+
if (path3.isA(geometry)) return measureAreaOfPath(geometry)
|
|
93
95
|
if (slice.isA(geometry)) return measureAreaOfSlice(geometry)
|
|
94
96
|
return 0
|
|
95
97
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from 'ava'
|
|
2
2
|
|
|
3
|
-
import { geom2, geom3, path2, slice } from '../geometries/index.js'
|
|
3
|
+
import { geom2, geom3, path2, path3, slice } from '../geometries/index.js'
|
|
4
4
|
|
|
5
5
|
import { line, rectangle, cuboid } from '../primitives/index.js'
|
|
6
6
|
|
|
@@ -12,6 +12,7 @@ test('measureArea: single objects', (t) => {
|
|
|
12
12
|
const acube = cuboid()
|
|
13
13
|
|
|
14
14
|
const apath2 = path2.create()
|
|
15
|
+
const apath3 = path3.create()
|
|
15
16
|
const ageom2 = geom2.create()
|
|
16
17
|
const ageom3 = geom3.create()
|
|
17
18
|
const aslice = slice.create()
|
|
@@ -25,6 +26,7 @@ test('measureArea: single objects', (t) => {
|
|
|
25
26
|
const carea = measureArea(acube)
|
|
26
27
|
|
|
27
28
|
const p2area = measureArea(apath2)
|
|
29
|
+
const p3area = measureArea(apath3)
|
|
28
30
|
const g2area = measureArea(ageom2)
|
|
29
31
|
const g3area = measureArea(ageom3)
|
|
30
32
|
const slarea = measureArea(aslice)
|
|
@@ -38,6 +40,7 @@ test('measureArea: single objects', (t) => {
|
|
|
38
40
|
t.is(carea, 24) // 2x2x6
|
|
39
41
|
|
|
40
42
|
t.is(p2area, 0)
|
|
43
|
+
t.is(p3area, 0)
|
|
41
44
|
t.is(g2area, 0)
|
|
42
45
|
t.is(g3area, 0)
|
|
43
46
|
t.is(slarea, 0)
|
|
@@ -6,6 +6,7 @@ import * as vec3 from '../maths/vec3/index.js'
|
|
|
6
6
|
import * as geom2 from '../geometries/geom2/index.js'
|
|
7
7
|
import * as geom3 from '../geometries/geom3/index.js'
|
|
8
8
|
import * as path2 from '../geometries/path2/index.js'
|
|
9
|
+
import * as path3 from '../geometries/path3/index.js'
|
|
9
10
|
import * as poly3 from '../geometries/poly3/index.js'
|
|
10
11
|
import * as slice from '../geometries/slice/index.js'
|
|
11
12
|
|
|
@@ -70,6 +71,18 @@ const measureBoundingBoxOfPath2 = (geometry) => {
|
|
|
70
71
|
return boundingBox
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
/*
|
|
75
|
+
* Measure the min and max bounds of the given (path3) geometry.
|
|
76
|
+
* @return {Array[]} the min and max bounds for the geometry
|
|
77
|
+
*/
|
|
78
|
+
const measureBoundingBoxOfPath3 = (geometry) => {
|
|
79
|
+
const boundingBox = []
|
|
80
|
+
path3.toVertices(geometry).forEach((vertice) => {
|
|
81
|
+
expand3(boundingBox, vertice)
|
|
82
|
+
})
|
|
83
|
+
return boundingBox
|
|
84
|
+
}
|
|
85
|
+
|
|
73
86
|
/*
|
|
74
87
|
* Measure the min and max bounds of the given (geom2) geometry.
|
|
75
88
|
* @return {Array[]} the min and max bounds for the geometry
|
|
@@ -123,9 +136,10 @@ export const measureBoundingBox = (...geometries) => {
|
|
|
123
136
|
geometries = flatten(geometries)
|
|
124
137
|
|
|
125
138
|
const results = geometries.map((geometry) => {
|
|
126
|
-
if (path2.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfPath2)
|
|
127
|
-
if (geom2.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfGeom2)
|
|
128
139
|
if (geom3.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfGeom3)
|
|
140
|
+
if (geom2.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfGeom2)
|
|
141
|
+
if (path2.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfPath2)
|
|
142
|
+
if (path3.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfPath3)
|
|
129
143
|
if (slice.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfSlice)
|
|
130
144
|
return [[0, 0, 0], [0, 0, 0]]
|
|
131
145
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from 'ava'
|
|
2
2
|
|
|
3
|
-
import { geom2, geom3, path2, slice } from '../geometries/index.js'
|
|
3
|
+
import { geom2, geom3, path2, path3, slice } from '../geometries/index.js'
|
|
4
4
|
|
|
5
5
|
import { line, rectangle, cuboid } from '../primitives/index.js'
|
|
6
6
|
|
|
@@ -14,6 +14,7 @@ test('measureBoundingBox (single objects)', (t) => {
|
|
|
14
14
|
const acube = cuboid()
|
|
15
15
|
|
|
16
16
|
const apath2 = path2.create()
|
|
17
|
+
const apath3 = path3.create()
|
|
17
18
|
const ageom2 = geom2.create()
|
|
18
19
|
const ageom3 = geom3.create()
|
|
19
20
|
const aslice = slice.create()
|
|
@@ -27,6 +28,7 @@ test('measureBoundingBox (single objects)', (t) => {
|
|
|
27
28
|
const cbounds = measureBoundingBox(acube)
|
|
28
29
|
|
|
29
30
|
const p2bounds = measureBoundingBox(apath2)
|
|
31
|
+
const p3bounds = measureBoundingBox(apath3)
|
|
30
32
|
const g2bounds = measureBoundingBox(ageom2)
|
|
31
33
|
const g3bounds = measureBoundingBox(ageom3)
|
|
32
34
|
const slbounds = measureBoundingBox(aslice)
|
|
@@ -40,6 +42,7 @@ test('measureBoundingBox (single objects)', (t) => {
|
|
|
40
42
|
t.deepEqual(cbounds, [[-1, -1, -1], [1, 1, 1]])
|
|
41
43
|
|
|
42
44
|
t.deepEqual(p2bounds, [[0, 0, 0], [0, 0, 0]])
|
|
45
|
+
t.deepEqual(p3bounds, [[0, 0, 0], [0, 0, 0]])
|
|
43
46
|
t.deepEqual(g2bounds, [[0, 0, 0], [0, 0, 0]])
|
|
44
47
|
t.deepEqual(g3bounds, [[0, 0, 0], [0, 0, 0]])
|
|
45
48
|
t.deepEqual(slbounds, [[0, 0, 0], [0, 0, 0]])
|
|
@@ -6,6 +6,7 @@ import * as vec3 from '../maths/vec3/index.js'
|
|
|
6
6
|
import * as geom2 from '../geometries/geom2/index.js'
|
|
7
7
|
import * as geom3 from '../geometries/geom3/index.js'
|
|
8
8
|
import * as path2 from '../geometries/path2/index.js'
|
|
9
|
+
import * as path3 from '../geometries/path3/index.js'
|
|
9
10
|
import * as poly3 from '../geometries/poly3/index.js'
|
|
10
11
|
import * as slice from '../geometries/slice/index.js'
|
|
11
12
|
|
|
@@ -60,6 +61,33 @@ const measureBoundingSphereOfPoints = (points) => {
|
|
|
60
61
|
return [centroid, radius]
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
/*
|
|
65
|
+
* Measure the bounding sphere of the given 2D points.
|
|
66
|
+
* @return {[[x, y, z], radius]} the bounding sphere for the points
|
|
67
|
+
*/
|
|
68
|
+
const measureBoundingSphereOfVertices = (vertices) => {
|
|
69
|
+
const centroid = vec3.create()
|
|
70
|
+
let radius = 0
|
|
71
|
+
|
|
72
|
+
if (vertices.length > 0) {
|
|
73
|
+
// calculate the centroid of the vertices
|
|
74
|
+
let numVertices = 0
|
|
75
|
+
vertices.forEach((vertex) => {
|
|
76
|
+
vec3.add(centroid, centroid, vertex)
|
|
77
|
+
numVertices++
|
|
78
|
+
})
|
|
79
|
+
vec3.scale(centroid, centroid, 1 / numVertices)
|
|
80
|
+
|
|
81
|
+
// find the farthest vertex from the centroid
|
|
82
|
+
vertices.forEach((vertex) => {
|
|
83
|
+
radius = Math.max(radius, vec3.squaredDistance(centroid, vertex))
|
|
84
|
+
})
|
|
85
|
+
radius = Math.sqrt(radius)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return [centroid, radius]
|
|
89
|
+
}
|
|
90
|
+
|
|
63
91
|
/*
|
|
64
92
|
* Measure the bounding sphere of the given (path2) geometry.
|
|
65
93
|
* @return {[[x, y, z], radius]} the bounding sphere for the geometry
|
|
@@ -106,36 +134,16 @@ const measureBoundingSphereOfGeom3 = (geometry) => {
|
|
|
106
134
|
}
|
|
107
135
|
|
|
108
136
|
/*
|
|
109
|
-
* Measure the bounding sphere of the given (
|
|
137
|
+
* Measure the bounding sphere of the given (slice) geometry.
|
|
110
138
|
* @return {[[x, y, z], radius]} the bounding sphere for the geometry
|
|
111
139
|
*/
|
|
112
|
-
const
|
|
113
|
-
const centroid = vec3.create()
|
|
114
|
-
let radius = 0
|
|
115
|
-
let numVertices = 0
|
|
140
|
+
const measureBoundingSphereOfPath3 = (geometry) => measureBoundingSphereOfVertices(path3.toVertices(geometry))
|
|
116
141
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
})
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
if (numVertices > 0) {
|
|
126
|
-
vec3.scale(centroid, centroid, 1 / numVertices)
|
|
127
|
-
|
|
128
|
-
// find the farthest vertex from the centroid
|
|
129
|
-
geometry.contours.forEach((contour) => {
|
|
130
|
-
contour.forEach((vertex) => {
|
|
131
|
-
radius = Math.max(radius, vec3.squaredDistance(centroid, vertex))
|
|
132
|
-
})
|
|
133
|
-
})
|
|
134
|
-
radius = Math.sqrt(radius)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return [centroid, radius]
|
|
138
|
-
}
|
|
142
|
+
/*
|
|
143
|
+
* Measure the bounding sphere of the given (slice) geometry.
|
|
144
|
+
* @return {[[x, y, z], radius]} the bounding sphere for the geometry
|
|
145
|
+
*/
|
|
146
|
+
const measureBoundingSphereOfSlice = (geometry) => measureBoundingSphereOfVertices(slice.toVertices(geometry))
|
|
139
147
|
|
|
140
148
|
/**
|
|
141
149
|
* Measure the (approximate) bounding sphere of the given geometries.
|
|
@@ -151,9 +159,10 @@ export const measureBoundingSphere = (...geometries) => {
|
|
|
151
159
|
geometries = flatten(geometries)
|
|
152
160
|
|
|
153
161
|
const results = geometries.map((geometry) => {
|
|
154
|
-
if (path2.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfPath2)
|
|
155
|
-
if (geom2.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfGeom2)
|
|
156
162
|
if (geom3.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfGeom3)
|
|
163
|
+
if (geom2.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfGeom2)
|
|
164
|
+
if (path2.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfPath2)
|
|
165
|
+
if (path3.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfPath3)
|
|
157
166
|
if (slice.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfSlice)
|
|
158
167
|
return [[0, 0, 0], 0]
|
|
159
168
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from 'ava'
|
|
2
2
|
|
|
3
|
-
import { geom2, geom3, path2, slice } from '../geometries/index.js'
|
|
3
|
+
import { geom2, geom3, path2, path3, slice } from '../geometries/index.js'
|
|
4
4
|
|
|
5
5
|
import { line, rectangle, ellipsoid } from '../primitives/index.js'
|
|
6
6
|
|
|
@@ -12,6 +12,7 @@ test('measureBoundingSphere (single objects)', (t) => {
|
|
|
12
12
|
const aellipsoid = ellipsoid({ radius: [5, 10, 15], center: [5, 5, 5] })
|
|
13
13
|
|
|
14
14
|
const apath2 = path2.create()
|
|
15
|
+
const apath3 = path3.create()
|
|
15
16
|
const ageom2 = geom2.create()
|
|
16
17
|
const ageom3 = geom3.create()
|
|
17
18
|
const aslice = slice.create()
|
|
@@ -25,6 +26,7 @@ test('measureBoundingSphere (single objects)', (t) => {
|
|
|
25
26
|
const cbounds = measureBoundingSphere(aellipsoid)
|
|
26
27
|
|
|
27
28
|
const p2bounds = measureBoundingSphere(apath2)
|
|
29
|
+
const p3bounds = measureBoundingSphere(apath3)
|
|
28
30
|
const g2bounds = measureBoundingSphere(ageom2)
|
|
29
31
|
const g3bounds = measureBoundingSphere(ageom3)
|
|
30
32
|
const slbounds = measureBoundingSphere(aslice)
|
|
@@ -38,6 +40,7 @@ test('measureBoundingSphere (single objects)', (t) => {
|
|
|
38
40
|
t.deepEqual(cbounds, [[5.000000000000018, 4.999999999999983, 5.000000000000001], 15])
|
|
39
41
|
|
|
40
42
|
t.deepEqual(p2bounds, [[0, 0, 0], 0])
|
|
43
|
+
t.deepEqual(p3bounds, [[0, 0, 0], 0])
|
|
41
44
|
t.deepEqual(g2bounds, [[0, 0, 0], 0])
|
|
42
45
|
t.deepEqual(g3bounds, [[0, 0, 0], 0])
|
|
43
46
|
t.deepEqual(slbounds, [[0, 0, 0], 0])
|
|
@@ -95,9 +95,10 @@ export const measureCenterOfMass = (...geometries) => {
|
|
|
95
95
|
geometries = flatten(geometries)
|
|
96
96
|
|
|
97
97
|
const results = geometries.map((geometry) => {
|
|
98
|
-
// NOTE: center of mass for geometry path2 is not possible
|
|
99
|
-
if (geom2.isA(geometry)) return measureCenterOfMassGeom2(geometry)
|
|
100
98
|
if (geom3.isA(geometry)) return measureCenterOfMassGeom3(geometry)
|
|
99
|
+
if (geom2.isA(geometry)) return measureCenterOfMassGeom2(geometry)
|
|
100
|
+
// TODO if (slice.isA(geometry)) return measureCenterOfMassSlice(geometry)
|
|
101
|
+
// NOTE: center of mass for geometry path2 and path3 is not possible
|
|
101
102
|
return [0, 0, 0]
|
|
102
103
|
})
|
|
103
104
|
return results.length === 1 ? results[0] : results
|
|
@@ -3,6 +3,7 @@ import { flatten } from '../utils/flatten.js'
|
|
|
3
3
|
import * as geom2 from '../geometries/geom2/index.js'
|
|
4
4
|
import * as geom3 from '../geometries/geom3/index.js'
|
|
5
5
|
import * as path2 from '../geometries/path2/index.js'
|
|
6
|
+
import * as path3 from '../geometries/path3/index.js'
|
|
6
7
|
import * as slice from '../geometries/slice/index.js'
|
|
7
8
|
|
|
8
9
|
import { calculateEpsilonFromBounds } from './calculateEpsilonFromBounds.js'
|
|
@@ -22,9 +23,10 @@ export const measureEpsilon = (...geometries) => {
|
|
|
22
23
|
geometries = flatten(geometries)
|
|
23
24
|
|
|
24
25
|
const results = geometries.map((geometry) => {
|
|
25
|
-
if (path2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
|
|
26
|
-
if (geom2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
|
|
27
26
|
if (geom3.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 3)
|
|
27
|
+
if (geom2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
|
|
28
|
+
if (path2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
|
|
29
|
+
if (path3.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 3)
|
|
28
30
|
if (slice.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 3)
|
|
29
31
|
return 0
|
|
30
32
|
})
|
|
@@ -70,6 +70,8 @@ const inResult = (event, operation) => {
|
|
|
70
70
|
(!event.isSubject && !event.otherInOut)
|
|
71
71
|
case XOR:
|
|
72
72
|
return true
|
|
73
|
+
default:
|
|
74
|
+
throw new Error('computeFields01')
|
|
73
75
|
}
|
|
74
76
|
break
|
|
75
77
|
case SAME_TRANSITION:
|
|
@@ -78,6 +80,8 @@ const inResult = (event, operation) => {
|
|
|
78
80
|
return operation === DIFFERENCE
|
|
79
81
|
case NON_CONTRIBUTING:
|
|
80
82
|
return false
|
|
83
|
+
default:
|
|
84
|
+
throw new Error('computeFields02')
|
|
81
85
|
}
|
|
82
86
|
return false
|
|
83
87
|
}
|
|
@@ -104,6 +108,8 @@ const determineResultTransition = (event, operation) => {
|
|
|
104
108
|
isIn = thatIn && !thisIn
|
|
105
109
|
}
|
|
106
110
|
break
|
|
111
|
+
default:
|
|
112
|
+
throw new Error('computeFields03')
|
|
107
113
|
}
|
|
108
114
|
return isIn ? +1 : -1
|
|
109
115
|
}
|
|
@@ -12,7 +12,8 @@ export const divideSegment = (segment, point, queue) => {
|
|
|
12
12
|
const r = new SweepEvent(point, false, segment, segment.isSubject)
|
|
13
13
|
const l = new SweepEvent(point, true, segment.otherEvent, segment.isSubject)
|
|
14
14
|
|
|
15
|
-
r.contourId =
|
|
15
|
+
r.contourId = segment.contourId
|
|
16
|
+
l.contourId = segment.contourId
|
|
16
17
|
|
|
17
18
|
// avoid a rounding error. The left event would be processed after the right event
|
|
18
19
|
if (compareEvents(l, segment.otherEvent) > 0) {
|
|
@@ -25,7 +25,9 @@ const processPolygon = (contourOrHole, isSubject, ringId, queue, bbox, isExterio
|
|
|
25
25
|
continue // skip collapsed edges, or it breaks
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
e1.contourId =
|
|
28
|
+
e1.contourId = ringId
|
|
29
|
+
e2.contourId = ringId
|
|
30
|
+
|
|
29
31
|
if (!isExteriorRing) {
|
|
30
32
|
e1.isExteriorRing = false
|
|
31
33
|
e2.isExteriorRing = false
|
|
@@ -32,14 +32,14 @@ export const subdivideSegments = (eventQueue, subject, clipping, sbbox, cbbox, o
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
if (event.left) {
|
|
35
|
-
|
|
35
|
+
prev = sweepLine.insert(event)
|
|
36
36
|
begin = sweepLine.minNode()
|
|
37
37
|
|
|
38
|
+
next = sweepLine.next(prev)
|
|
39
|
+
|
|
38
40
|
if (prev !== begin) prev = sweepLine.prev(prev)
|
|
39
41
|
else prev = null
|
|
40
42
|
|
|
41
|
-
next = sweepLine.next(next)
|
|
42
|
-
|
|
43
43
|
const prevEvent = prev ? prev.key : null
|
|
44
44
|
let prevprevEvent
|
|
45
45
|
computeFields(event, prevEvent, operation)
|
|
@@ -62,7 +62,8 @@ export const subdivideSegments = (eventQueue, subject, clipping, sbbox, cbbox, o
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
} else {
|
|
65
|
-
|
|
65
|
+
prev = sweepLine.find(event.otherEvent)
|
|
66
|
+
next = prev
|
|
66
67
|
|
|
67
68
|
if (prev && next) {
|
|
68
69
|
// FIXME is this correct? begin is assigned if event.left, not every iterration
|
|
@@ -13,7 +13,7 @@ import { mayOverlap } from './mayOverlap.js'
|
|
|
13
13
|
*/
|
|
14
14
|
export const subtractGeom3Sub = (geometry1, geometry2) => {
|
|
15
15
|
if (!mayOverlap(geometry1, geometry2)) {
|
|
16
|
-
return
|
|
16
|
+
return geometry1
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
const a = new Tree(geom3.toPolygons(geometry1))
|