@jscad/modeling 3.0.5-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 +6 -0
- package/dist/jscad-modeling.es.js +2 -2
- package/dist/jscad-modeling.min.js +2 -2
- package/package.json +2 -2
- 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/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/close.js +7 -8
- package/src/geometries/path3/transform.js +1 -0
- package/src/geometries/slice/earcut/index.js +4 -2
- package/src/geometries/slice/earcut/linkedPolygon.js +4 -2
- package/src/geometries/slice/transform.js +2 -1
- package/src/maths/line2/intersectPointOfLines.js +1 -4
- 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/extrusions/extrudeLinearGeom2.js +10 -7
- 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/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 +3 -3
- package/src/operations/modifiers/snap.test.js +12 -2
- package/src/operations/offsets/offsetGeom3.test.js +2 -2
- package/src/operations/transforms/translate.js +6 -2
- package/src/primitives/arc.js +3 -4
- package/src/primitives/ellipse.js +2 -3
- package/src/primitives/star.js +2 -4
- package/test/helpers/nearlyEqual.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jscad/modeling",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.6-alpha.0",
|
|
4
4
|
"description": "Constructive Solid Geometry (CSG) Library for JSCAD",
|
|
5
5
|
"homepage": "https://openjscad.xyz/",
|
|
6
6
|
"repository": "https://github.com/jscad/OpenJSCAD.org",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"c8": "^10.1.0",
|
|
64
64
|
"rollup": "^4.52.0"
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "201d3987b309fd1f383b10b5555dd0a2765cad43"
|
|
67
67
|
}
|
package/src/colors/hsvToRgb.js
CHANGED
package/src/colors/rgbToHsl.js
CHANGED
|
@@ -24,7 +24,8 @@ export const rgbToHsl = (...values) => {
|
|
|
24
24
|
const l = (max + min) / 2
|
|
25
25
|
|
|
26
26
|
if (max === min) {
|
|
27
|
-
h =
|
|
27
|
+
h = 0
|
|
28
|
+
s = 0 // achromatic
|
|
28
29
|
} else {
|
|
29
30
|
const d = max - min
|
|
30
31
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
|
|
@@ -36,6 +37,7 @@ export const rgbToHsl = (...values) => {
|
|
|
36
37
|
h = (b - r) / d + 2
|
|
37
38
|
break
|
|
38
39
|
case b:
|
|
40
|
+
default:
|
|
39
41
|
h = (r - g) / d + 4
|
|
40
42
|
break
|
|
41
43
|
}
|
package/src/colors/rgbToHsv.js
CHANGED
|
@@ -51,7 +51,7 @@ const getPointType = function (points) {
|
|
|
51
51
|
})
|
|
52
52
|
pType = 'float_' + point.length
|
|
53
53
|
} else throw new Error('Bezier points must all be numbers or arrays of number.')
|
|
54
|
-
if (firstPointType
|
|
54
|
+
if (firstPointType === null) {
|
|
55
55
|
firstPointType = pType
|
|
56
56
|
} else {
|
|
57
57
|
if (firstPointType !== pType) {
|
|
@@ -18,6 +18,7 @@ import { reverse } from './reverse.js'
|
|
|
18
18
|
*/
|
|
19
19
|
export const transform = (matrix, geometry) => {
|
|
20
20
|
const transforms = mat4.multiply(mat4.create(), matrix, geometry.transforms)
|
|
21
|
+
// use Object.assign in order to assign EXTRA attributes like color, name, etc
|
|
21
22
|
const transformed = Object.assign({}, geometry, { transforms })
|
|
22
23
|
// determine if the transform is mirroring in 2D
|
|
23
24
|
if (matrix[0] * matrix[5] - matrix[4] * matrix[1] < 0) {
|
|
@@ -17,5 +17,6 @@ import * as mat4 from '../../maths/mat4/index.js'
|
|
|
17
17
|
*/
|
|
18
18
|
export const transform = (matrix, geometry) => {
|
|
19
19
|
const transforms = mat4.multiply(mat4.create(), matrix, geometry.transforms)
|
|
20
|
+
// use Object.assign in order to assign EXTRA attributes like color, name, etc
|
|
20
21
|
return Object.assign({}, geometry, { transforms })
|
|
21
22
|
}
|
|
@@ -22,7 +22,17 @@ export const validate = (object) => {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// check polygons
|
|
25
|
-
object.polygons.forEach(
|
|
25
|
+
object.polygons.forEach((p, i) => {
|
|
26
|
+
if (p.vertices.length < 3) {
|
|
27
|
+
throw new Error(`invalid polygon ${i}: only ${p.vertices.length} vertices`)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
poly3.validate(p)
|
|
32
|
+
} catch (e) {
|
|
33
|
+
throw new Error(`invalid polygon ${i}: ${e}`)
|
|
34
|
+
}
|
|
35
|
+
})
|
|
26
36
|
validateManifold(object)
|
|
27
37
|
|
|
28
38
|
// check transforms
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TAU } from '../../maths/constants.js'
|
|
2
2
|
import * as vec2 from '../../maths/vec2/index.js'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { appendPoints } from './appendPoints.js'
|
|
5
5
|
import { toPoints } from './toPoints.js'
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -27,19 +27,19 @@ import { toPoints } from './toPoints.js'
|
|
|
27
27
|
* myShape = path2.appendArc({endpoint: [12.5, -22.96875], radius: [15, -19.6875]}, myShape);
|
|
28
28
|
*/
|
|
29
29
|
export const appendArc = (options, geometry) => {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
const {
|
|
31
|
+
endpoint = null,
|
|
32
|
+
radius = [0, 0], // X and Y radius
|
|
33
|
+
xaxisRotation = 0,
|
|
34
|
+
clockwise = false,
|
|
35
|
+
large = false,
|
|
36
|
+
segments = 16
|
|
37
|
+
} = options
|
|
38
38
|
|
|
39
39
|
// validate the given options
|
|
40
40
|
if (!Array.isArray(endpoint)) throw new Error('endpoint must be an array of X and Y values')
|
|
41
|
-
if (endpoint.length
|
|
42
|
-
|
|
41
|
+
if (endpoint.length !== 2) throw new Error('endpoint must contain X and Y values')
|
|
42
|
+
let xendPoint = vec2.clone(endpoint)
|
|
43
43
|
|
|
44
44
|
if (!Array.isArray(radius)) throw new Error('radius must be an array of X and Y values')
|
|
45
45
|
if (radius.length < 2) throw new Error('radius must contain X and Y values')
|
|
@@ -48,11 +48,6 @@ export const appendArc = (options, geometry) => {
|
|
|
48
48
|
|
|
49
49
|
const decimals = 100000
|
|
50
50
|
|
|
51
|
-
// validate the given geometry
|
|
52
|
-
if (geometry.isClosed) {
|
|
53
|
-
throw new Error('the given path cannot be closed')
|
|
54
|
-
}
|
|
55
|
-
|
|
56
51
|
const points = toPoints(geometry)
|
|
57
52
|
if (points.length < 1) {
|
|
58
53
|
throw new Error('the given path must contain one or more points (as the starting point for the arc)')
|
|
@@ -65,14 +60,14 @@ export const appendArc = (options, geometry) => {
|
|
|
65
60
|
// round to precision in order to have determinate calculations
|
|
66
61
|
xRadius = Math.round(xRadius * decimals) / decimals
|
|
67
62
|
yRadius = Math.round(yRadius * decimals) / decimals
|
|
68
|
-
|
|
63
|
+
xendPoint = vec2.fromValues(Math.round(xendPoint[0] * decimals) / decimals, Math.round(xendPoint[1] * decimals) / decimals)
|
|
69
64
|
|
|
70
65
|
const sweepFlag = !clockwise
|
|
71
|
-
|
|
66
|
+
const newPoints = []
|
|
72
67
|
if ((xRadius === 0) || (yRadius === 0)) {
|
|
73
68
|
// http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes:
|
|
74
69
|
// If rx = 0 or ry = 0, then treat this as a straight line from (x1, y1) to (x2, y2) and stop
|
|
75
|
-
newPoints.push(
|
|
70
|
+
newPoints.push(xendPoint)
|
|
76
71
|
} else {
|
|
77
72
|
xRadius = Math.abs(xRadius)
|
|
78
73
|
yRadius = Math.abs(yRadius)
|
|
@@ -81,7 +76,7 @@ export const appendArc = (options, geometry) => {
|
|
|
81
76
|
const phi = xaxisRotation
|
|
82
77
|
const cosPhi = Math.cos(phi)
|
|
83
78
|
const sinPhi = Math.sin(phi)
|
|
84
|
-
const minusHalfDistance = vec2.subtract(vec2.create(), startpoint,
|
|
79
|
+
const minusHalfDistance = vec2.subtract(vec2.create(), startpoint, xendPoint)
|
|
85
80
|
vec2.scale(minusHalfDistance, minusHalfDistance, 0.5)
|
|
86
81
|
// F.6.5.1:
|
|
87
82
|
// round to precision in order to have determinate calculations
|
|
@@ -106,7 +101,7 @@ export const appendArc = (options, geometry) => {
|
|
|
106
101
|
vec2.scale(centerTranslated, centerTranslated, multiplier1)
|
|
107
102
|
// F.6.5.3:
|
|
108
103
|
let center = vec2.fromValues(cosPhi * centerTranslated[0] - sinPhi * centerTranslated[1], sinPhi * centerTranslated[0] + cosPhi * centerTranslated[1])
|
|
109
|
-
center = vec2.add(center, center, vec2.scale(vec2.create(), vec2.add(vec2.create(), startpoint,
|
|
104
|
+
center = vec2.add(center, center, vec2.scale(vec2.create(), vec2.add(vec2.create(), startpoint, xendPoint), 0.5))
|
|
110
105
|
|
|
111
106
|
// F.6.5.5:
|
|
112
107
|
const vector1 = vec2.fromValues((startTranslated[0] - centerTranslated[0]) / xRadius, (startTranslated[1] - centerTranslated[1]) / yRadius)
|
|
@@ -134,9 +129,8 @@ export const appendArc = (options, geometry) => {
|
|
|
134
129
|
newPoints.push(point)
|
|
135
130
|
}
|
|
136
131
|
// ensure end point is precisely what user gave as parameter
|
|
137
|
-
if (numSteps) newPoints.push(
|
|
132
|
+
if (numSteps) newPoints.push(endpoint)
|
|
138
133
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return result
|
|
134
|
+
|
|
135
|
+
return appendPoints(newPoints, geometry)
|
|
142
136
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TAU } from '../../maths/constants.js'
|
|
2
2
|
import * as vec2 from '../../maths/vec2/index.js'
|
|
3
|
-
import * as vec3 from '../../maths/vec3/index.js' //
|
|
3
|
+
import * as vec3 from '../../maths/vec3/index.js' // required due to vec2.cross()
|
|
4
4
|
|
|
5
5
|
import { appendPoints } from './appendPoints.js'
|
|
6
6
|
import { toPoints } from './toPoints.js'
|
|
@@ -27,10 +27,10 @@ import { toPoints } from './toPoints.js'
|
|
|
27
27
|
* myShape = path2.appendBezier({controlPoints: [null, [25,-30],[40,-30],[40,-20]]}, myShape)
|
|
28
28
|
*/
|
|
29
29
|
export const appendBezier = (options, geometry) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
let {
|
|
31
|
+
controlPoints = null,
|
|
32
|
+
segments = 16
|
|
33
|
+
} = options
|
|
34
34
|
|
|
35
35
|
// validate the given options
|
|
36
36
|
if (!Array.isArray(controlPoints)) throw new Error('controlPoints must be an array of one or more points')
|
|
@@ -39,10 +39,6 @@ export const appendBezier = (options, geometry) => {
|
|
|
39
39
|
if (segments < 4) throw new Error('segments must be four or more')
|
|
40
40
|
|
|
41
41
|
// validate the given geometry
|
|
42
|
-
if (geometry.isClosed) {
|
|
43
|
-
throw new Error('the given geometry cannot be closed')
|
|
44
|
-
}
|
|
45
|
-
|
|
46
42
|
const points = toPoints(geometry)
|
|
47
43
|
if (points.length < 1) {
|
|
48
44
|
throw new Error('the given path must contain one or more points (as the starting point for the bezier curve)')
|
|
@@ -148,9 +144,10 @@ export const appendBezier = (options, geometry) => {
|
|
|
148
144
|
}
|
|
149
145
|
|
|
150
146
|
// append to the new points to the given path
|
|
151
|
-
// but skip the first new point because it is identical to the last point in the given path
|
|
152
|
-
newPoints.shift()
|
|
153
147
|
const result = appendPoints(newPoints, geometry)
|
|
148
|
+
|
|
149
|
+
// add a hint for the next bezier
|
|
154
150
|
result.lastBezierControlPoint = controlPoints[controlPoints.length - 2]
|
|
151
|
+
|
|
155
152
|
return result
|
|
156
153
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { equals } from '../../maths/vec2/equals.js'
|
|
2
|
+
|
|
3
|
+
import { fromPoints } from './fromPoints.js'
|
|
4
|
+
import { toPoints } from './toPoints.js'
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Append the given list of points to the end of the given geometry.
|
|
@@ -12,4 +14,20 @@ import { create } from './create.js'
|
|
|
12
14
|
* @example
|
|
13
15
|
* let newPath = path2.appendPoints([[3, 4], [4, 5]], oldPath)
|
|
14
16
|
*/
|
|
15
|
-
export const appendPoints = (points, geometry) =>
|
|
17
|
+
export const appendPoints = (points, geometry) => {
|
|
18
|
+
let originalPoints = toPoints(geometry)
|
|
19
|
+
|
|
20
|
+
if (geometry.isClosed && originalPoints.length > 1) {
|
|
21
|
+
// add the closing point to the originalPoints
|
|
22
|
+
originalPoints = originalPoints.slice()
|
|
23
|
+
originalPoints.push(originalPoints[0])
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (originalPoints.length > 0) {
|
|
27
|
+
// remove redundant points
|
|
28
|
+
const lastPoint = originalPoints[originalPoints.length - 1]
|
|
29
|
+
while (points.length > 0 && equals(points[0], lastPoint)) points.shift()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return fromPoints({}, originalPoints.concat(points))
|
|
33
|
+
}
|
|
@@ -12,10 +12,16 @@ test('appendPoints: appending to an empty path produces a new path with expected
|
|
|
12
12
|
|
|
13
13
|
test('appendPoints: appending to a path produces a new path with expected points', (t) => {
|
|
14
14
|
const p1 = fromPoints({}, [[1, 1], [2, 2]])
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
let obs = appendPoints([[3, 3], [4, 4]], p1)
|
|
16
|
+
let pts = toPoints(obs)
|
|
17
17
|
t.not(p1, obs)
|
|
18
18
|
t.is(pts.length, 4)
|
|
19
|
+
|
|
20
|
+
const p2 = fromPoints({ closed: true }, [[0, 0], [1, 0], [0, 1]])
|
|
21
|
+
obs = appendPoints([[0, -1], [0, -2]], p2)
|
|
22
|
+
pts = toPoints(obs)
|
|
23
|
+
t.not(p2, obs)
|
|
24
|
+
t.is(pts.length, 6)
|
|
19
25
|
})
|
|
20
26
|
|
|
21
27
|
test('appendPoints: appending empty points to a path produces a new path with expected points', (t) => {
|
|
@@ -28,8 +34,14 @@ test('appendPoints: appending empty points to a path produces a new path with ex
|
|
|
28
34
|
|
|
29
35
|
test('appendPoints: appending same points to a path produces a new path with expected points', (t) => {
|
|
30
36
|
const p1 = fromPoints({}, [[1, 1], [2, 2]])
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
let obs = appendPoints([[2, 2], [3, 3]], p1)
|
|
38
|
+
let pts = toPoints(obs)
|
|
33
39
|
t.not(p1, obs)
|
|
34
40
|
t.is(pts.length, 3)
|
|
41
|
+
|
|
42
|
+
const p2 = fromPoints({ closed: true }, [[0, 0], [1, 0], [0, 1]])
|
|
43
|
+
obs = appendPoints([[0, 0], [0, 0], [0, -1]], p2)
|
|
44
|
+
pts = toPoints(obs)
|
|
45
|
+
t.not(p2, obs)
|
|
46
|
+
t.is(pts.length, 5)
|
|
35
47
|
})
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { EPS } from '../../maths/constants.js'
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
import { clone } from './clone.js'
|
|
3
|
+
import { distance } from '../../maths/vec2/distance.js'
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* Close the given geometry.
|
|
@@ -17,19 +15,18 @@ import { clone } from './clone.js'
|
|
|
17
15
|
export const close = (geometry) => {
|
|
18
16
|
if (geometry.isClosed) return geometry
|
|
19
17
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (cloned.points.length > 1) {
|
|
18
|
+
const isClosed = true
|
|
19
|
+
const points = geometry.points.slice()
|
|
20
|
+
if (points.length > 1) {
|
|
24
21
|
// make sure the paths are formed properly
|
|
25
|
-
const points = cloned.points
|
|
26
22
|
const p0 = points[0]
|
|
27
23
|
let pn = points[points.length - 1]
|
|
28
|
-
while (
|
|
24
|
+
while (distance(p0, pn) < EPS) {
|
|
29
25
|
points.pop()
|
|
30
26
|
if (points.length === 1) break
|
|
31
27
|
pn = points[points.length - 1]
|
|
32
28
|
}
|
|
33
29
|
}
|
|
34
|
-
|
|
30
|
+
// use Object.assign in order to assign EXTRA attributes like color, name, etc
|
|
31
|
+
return Object.assign({}, geometry, { isClosed, points })
|
|
35
32
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { equals } from '../../maths/vec2/
|
|
1
|
+
import { equals } from '../../maths/vec2/equals.js'
|
|
2
2
|
|
|
3
3
|
import { fromPoints } from './fromPoints.js'
|
|
4
4
|
import { toPoints } from './toPoints.js'
|
|
@@ -6,7 +6,7 @@ import { toPoints } from './toPoints.js'
|
|
|
6
6
|
/**
|
|
7
7
|
* Concatenate the given paths.
|
|
8
8
|
*
|
|
9
|
-
* If
|
|
9
|
+
* If paths contain the same junction point, merge it into one.
|
|
10
10
|
* A concatenation of zero paths is an empty, open path.
|
|
11
11
|
* A concatenation of one closed path to a series of open paths produces a closed path.
|
|
12
12
|
* A concatenation of a path to a closed path is an error.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EPS } from '../../maths/constants.js'
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { distance } from '../../maths/vec2/distance.js'
|
|
4
4
|
|
|
5
5
|
import { close } from './close.js'
|
|
6
6
|
import { create } from './create.js'
|
|
@@ -18,20 +18,20 @@ import { create } from './create.js'
|
|
|
18
18
|
* @alias module:modeling/path2.fromPoints
|
|
19
19
|
*
|
|
20
20
|
* @example
|
|
21
|
-
*
|
|
21
|
+
* let newPath = path2.fromPoints({closed: true}, [[10, 10], [-10, 10]])
|
|
22
22
|
*/
|
|
23
23
|
export const fromPoints = (options, points) => {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
let {
|
|
25
|
+
closed = false
|
|
26
|
+
} = options
|
|
26
27
|
|
|
27
|
-
let created = create()
|
|
28
|
-
created.points = points.map((point) => vec2.clone(point))
|
|
28
|
+
let created = create(points.slice())
|
|
29
29
|
|
|
30
30
|
// check if first and last points are equal
|
|
31
31
|
if (created.points.length > 1) {
|
|
32
32
|
const p0 = created.points[0]
|
|
33
33
|
const pn = created.points[created.points.length - 1]
|
|
34
|
-
if (
|
|
34
|
+
if (distance(p0, pn) < EPS) {
|
|
35
35
|
// and close automatically
|
|
36
36
|
closed = true
|
|
37
37
|
}
|
|
@@ -14,7 +14,7 @@ import { clone } from './clone.js'
|
|
|
14
14
|
*/
|
|
15
15
|
export const reverse = (geometry) => {
|
|
16
16
|
// NOTE: this only updates the order of the points
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
return
|
|
17
|
+
const points = geometry.points.slice().reverse()
|
|
18
|
+
// use Object.assign in order to assign EXTRA attributes like color, name, etc
|
|
19
|
+
return Object.assign({}, geometry, { points })
|
|
20
20
|
}
|
|
@@ -16,5 +16,6 @@ import * as mat4 from '../../maths/mat4/index.js'
|
|
|
16
16
|
*/
|
|
17
17
|
export const transform = (matrix, geometry) => {
|
|
18
18
|
const transforms = mat4.multiply(mat4.create(), matrix, geometry.transforms)
|
|
19
|
+
// use Object.assign in order to assign EXTRA attributes like color, name, etc
|
|
19
20
|
return Object.assign({}, geometry, { transforms })
|
|
20
21
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { equals } from '../../maths/vec2/equals.js'
|
|
2
2
|
|
|
3
3
|
import { isA } from './isA.js'
|
|
4
4
|
|
|
@@ -24,7 +24,7 @@ export const validate = (object) => {
|
|
|
24
24
|
// check for duplicate points
|
|
25
25
|
if (object.points.length > 1) {
|
|
26
26
|
for (let i = 0; i < object.points.length; i++) {
|
|
27
|
-
if (
|
|
27
|
+
if (equals(object.points[i], object.points[(i + 1) % object.points.length])) {
|
|
28
28
|
throw new Error(`path2 has duplicate point ${object.points[i]}`)
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EPS } from '../../maths/constants.js'
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { distance } from '../../maths/vec3/distance.js'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Close the given geometry.
|
|
@@ -15,19 +15,18 @@ import * as vec3 from '../../maths/vec3/index.js'
|
|
|
15
15
|
export const close = (geometry) => {
|
|
16
16
|
if (geometry.isClosed) return geometry
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (cloned.vertices.length > 1) {
|
|
18
|
+
const isClosed = true
|
|
19
|
+
const vertices = geometry.vertices.slice()
|
|
20
|
+
if (vertices.length > 1) {
|
|
22
21
|
// make sure the paths are formed properly
|
|
23
|
-
const vertices = cloned.vertices
|
|
24
22
|
const p0 = vertices[0]
|
|
25
23
|
let pn = vertices[vertices.length - 1]
|
|
26
|
-
while (
|
|
24
|
+
while (distance(p0, pn) < EPS) {
|
|
27
25
|
vertices.pop()
|
|
28
26
|
if (vertices.length === 1) break
|
|
29
27
|
pn = vertices[vertices.length - 1]
|
|
30
28
|
}
|
|
31
29
|
}
|
|
32
|
-
|
|
30
|
+
// use Object.assign in order to assign EXTRA attributes like color, name, etc
|
|
31
|
+
return Object.assign({}, geometry, { isClosed, vertices })
|
|
33
32
|
}
|
|
@@ -16,5 +16,6 @@ import * as mat4 from '../../maths/mat4/index.js'
|
|
|
16
16
|
*/
|
|
17
17
|
export const transform = (matrix, geometry) => {
|
|
18
18
|
const transforms = mat4.multiply(mat4.create(), matrix, geometry.transforms)
|
|
19
|
+
// use Object.assign in order to assign EXTRA attributes like color, name, etc
|
|
19
20
|
return Object.assign({}, geometry, { transforms })
|
|
20
21
|
}
|
|
@@ -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
|
|
@@ -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])
|
|
@@ -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))
|