Skip to content

Commit 48606fc

Browse files
authored
feat(modeling): preserve color for offset and extrude
1 parent f88669b commit 48606fc

21 files changed

+181
-49
lines changed

packages/modeling/src/geometries/geom2/reverse.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { clone } from './clone.js'
1+
import { create } from './create.js'
2+
import { toOutlines } from './toOutlines.js'
23

34
/**
45
* Reverses the given geometry so that the outline points are flipped in the opposite order.
@@ -11,7 +12,9 @@ import { clone } from './clone.js'
1112
* let newGeometry = reverse(geometry)
1213
*/
1314
export const reverse = (geometry) => {
14-
const reversed = clone(geometry)
15-
reversed.outlines = reversed.outlines.map((outline) => outline.slice().reverse())
15+
const outlines = toOutlines(geometry)
16+
.map((outline) => outline.slice().reverse())
17+
const reversed = create(outlines)
18+
if (geometry.color) reversed.color = geometry.color
1619
return reversed
1720
}

packages/modeling/src/geometries/geom2/reverse.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import test from 'ava'
22

3+
import { colorize } from '../../colors/index.js'
4+
35
import { create, reverse, toPoints } from './index.js'
46

57
import { comparePoints, compareVectors } from '../../../test/helpers/index.js'
@@ -29,3 +31,10 @@ test('reverse: does not modify input geometry', (t) => {
2931
t.true(comparePoints(toPoints(geometry), forward))
3032
t.true(comparePoints(toPoints(another), backward))
3133
})
34+
35+
test('reverse: preserves color', (t) => {
36+
const points = [[0, 0], [1, 0], [0, 1]]
37+
const geometry = colorize([1, 0, 0], create([points]))
38+
const reversed = reverse(geometry)
39+
t.deepEqual(reversed.color, [1, 0, 0, 1])
40+
})

packages/modeling/src/operations/extrusions/extrudeLinear.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { comparePolygonsAsPoints } from '../../../test/helpers/index.js'
44

55
import { TAU } from '../../maths/constants.js'
66

7+
import { colorize } from '../../colors/index.js'
8+
79
import { geom2, geom3, path2 } from '../../geometries/index.js'
810

911
import { measureVolume } from '../../measurements/index.js'
@@ -37,6 +39,17 @@ test('extrudeLinear (defaults)', (t) => {
3739
t.true(comparePolygonsAsPoints(pts, exp))
3840
})
3941

42+
test('extrudeLinear: preserves color', (t) => {
43+
const redSquare = colorize([1, 0, 0], square())
44+
const extruded = extrudeLinear({ }, redSquare)
45+
t.deepEqual(extruded.color, [1, 0, 0, 1])
46+
47+
// one red, one blue
48+
const out = extrudeLinear({ }, [redSquare, square()])
49+
t.deepEqual(out[0].color, [1, 0, 0, 1])
50+
t.is(out[1].color, undefined)
51+
})
52+
4053
test('extrudeLinear (no twist)', (t) => {
4154
const geometry2 = square({ size: 10 })
4255

packages/modeling/src/operations/extrusions/extrudeLinearGeom2.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,7 @@ export const extrudeLinearGeom2 = (options, geometry) => {
5353
repair,
5454
callback: createTwist
5555
}
56-
return extrudeFromSlices(options, baseSlice)
56+
const output = extrudeFromSlices(options, baseSlice)
57+
if (geometry.color) output.color = geometry.color
58+
return output
5759
}

packages/modeling/src/operations/extrusions/extrudeLinearPath2.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ export const extrudeLinearPath2 = (options, geometry) => {
1818
// Convert path2 to geom2
1919
const points = path2.toPoints(geometry)
2020
const geometry2 = geom2.create([points])
21+
if (geometry.color) geometry2.color = geometry.color
2122
return extrudeLinearGeom2(options, geometry2)
2223
}

packages/modeling/src/operations/extrusions/extrudeRotate.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ export const extrudeRotate = (options, geometry) => {
5858

5959
// convert geometry to an array of sides, easier to deal with
6060
let shapeSides = geom2.toSides(geometry)
61-
if (shapeSides.length === 0) throw new Error('the given geometry cannot be empty')
61+
if (shapeSides.length === 0) return geometry
62+
let sliceGeometry = geometry
6263

6364
// determine if the extrusion can be computed in the first place
6465
// ie all the points have to be either x > 0 or x < 0
@@ -87,8 +88,8 @@ export const extrudeRotate = (options, geometry) => {
8788
return [point0, point1]
8889
})
8990
// recreate the geometry from the (-) capped points
90-
geometry = geom2.reverse(geom2.fromSides(shapeSides))
91-
geometry = mirrorX(geometry)
91+
sliceGeometry = geom2.reverse(geom2.fromSides(shapeSides))
92+
sliceGeometry = mirrorX(sliceGeometry)
9293
} else if (pointsWithPositiveX.length >= pointsWithNegativeX.length) {
9394
shapeSides = shapeSides.map((side) => {
9495
let point0 = side[0]
@@ -98,13 +99,13 @@ export const extrudeRotate = (options, geometry) => {
9899
return [point0, point1]
99100
})
100101
// recreate the geometry from the (+) capped points
101-
geometry = geom2.fromSides(shapeSides)
102+
sliceGeometry = geom2.fromSides(shapeSides)
102103
}
103104
}
104105

105106
const rotationPerSlice = totalRotation / segments
106107
const isCapped = Math.abs(totalRotation) < TAU
107-
let baseSlice = slice.fromGeom2(geometry)
108+
let baseSlice = slice.fromGeom2(sliceGeometry)
108109
baseSlice = slice.reverse(baseSlice)
109110

110111
const matrix = mat4.create()
@@ -126,5 +127,7 @@ export const extrudeRotate = (options, geometry) => {
126127
close: !isCapped,
127128
callback: createSlice
128129
}
129-
return extrudeFromSlices(options, baseSlice)
130+
const output = extrudeFromSlices(options, baseSlice)
131+
if (geometry.color) output.color = geometry.color
132+
return output
130133
}

packages/modeling/src/operations/extrusions/extrudeRotate.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ import { comparePoints, comparePolygonsAsPoints } from '../../../test/helpers/in
44

55
import { TAU } from '../../maths/constants.js'
66

7+
import { colorize } from '../../colors/index.js'
8+
79
import { geom2, geom3 } from '../../geometries/index.js'
810

911
import { measureVolume } from '../../measurements/index.js'
1012

13+
import { square } from '../../primitives/index.js'
14+
1115
import { extrudeRotate } from './index.js'
1216

1317
test('extrudeRotate: (defaults) extruding of a geom2 produces an expected geom3', (t) => {
@@ -20,6 +24,12 @@ test('extrudeRotate: (defaults) extruding of a geom2 produces an expected geom3'
2024
t.is(pts.length, 96)
2125
})
2226

27+
test('extrudeRotate: preserves color', (t) => {
28+
const red = colorize([1, 0, 0], square())
29+
const extruded = extrudeRotate({ }, red)
30+
t.deepEqual(extruded.color, [1, 0, 0, 1])
31+
})
32+
2333
test('extrudeRotate: (angle) extruding of a geom2 produces an expected geom3', (t) => {
2434
const geometry2 = geom2.create([[[10, 8], [10, -8], [26, -8], [26, 8]]])
2535

packages/modeling/src/operations/extrusions/project.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ const projectGeom3 = (options, geometry) => {
5656
return geom2.create([cloned])
5757
})
5858

59-
return unionGeom2(projGeoms)
59+
const output = unionGeom2(projGeoms)
60+
if (geometry.color) output.color = geometry.color
61+
return output
6062
}
6163

6264
/**

packages/modeling/src/operations/extrusions/project.test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import test from 'ava'
22

33
import { comparePoints } from '../../../test/helpers/index.js'
44

5+
import { colorize } from '../../colors/index.js'
6+
57
import { geom2, geom3 } from '../../geometries/index.js'
68

79
import { measureArea } from '../../measurements/index.js'
@@ -139,3 +141,16 @@ test('project torus (martinez issue #155)', (t) => {
139141
t.notThrows(() => geom2.validate(result))
140142
t.is(measureArea(result), 21.15545050788201)
141143
})
144+
145+
test('project: preserves color', (t) => {
146+
const redCube = colorize([1, 0, 0], cube())
147+
const result = project({ }, redCube)
148+
t.deepEqual(result.color, [1, 0, 0, 1])
149+
})
150+
151+
test('project: empty geometry', (t) => {
152+
const obj = geom3.create()
153+
const result = project({ }, obj)
154+
t.notThrows(() => geom2.validate(result))
155+
t.is(measureArea(result), 0)
156+
})

packages/modeling/src/operations/hulls/hull.test.js

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import test from 'ava'
22

33
import { geom2, geom3, path2 } from '../../geometries/index.js'
4-
4+
import { measureArea } from '../../measurements/measureArea.js'
55
import { sphere, cuboid, ellipsoid } from '../../primitives/index.js'
6-
76
import { center } from '../transforms/index.js'
87

98
import { hull } from './index.js'
@@ -15,15 +14,15 @@ test('hull (single, geom2)', (t) => {
1514

1615
let obs = hull(geometry)
1716
let pts = geom2.toPoints(obs)
18-
19-
t.notThrows(() => geom2.validate(geometry))
17+
t.notThrows(() => geom2.validate(obs))
18+
t.is(measureArea(obs), 0)
2019
t.is(pts.length, 0)
2120

2221
geometry = geom2.create([[[5, 5], [-5, 5], [-5, -5], [5, -5]]])
2322
obs = hull(geometry)
2423
pts = geom2.toPoints(obs)
25-
26-
t.notThrows(() => geom2.validate(geometry))
24+
t.notThrows(() => geom2.validate(obs))
25+
t.is(measureArea(obs), 100)
2726
t.is(pts.length, 4)
2827

2928
// convex C shape
@@ -41,8 +40,7 @@ test('hull (single, geom2)', (t) => {
4140
]])
4241
obs = hull(geometry)
4342
pts = geom2.toPoints(obs)
44-
45-
t.notThrows(() => geom2.validate(geometry))
43+
t.notThrows(() => geom2.validate(obs))
4644
t.is(pts.length, 7)
4745
})
4846

0 commit comments

Comments
 (0)