Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/modeling/src/geometries/geom2/reverse.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ import { clone } from './clone.js'
export const reverse = (geometry) => {
const reversed = clone(geometry)
reversed.outlines = reversed.outlines.map((outline) => outline.slice().reverse())
if (geometry.color) reversed.color = geometry.color
return reversed
}
9 changes: 9 additions & 0 deletions packages/modeling/src/geometries/geom2/reverse.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import test from 'ava'

import { colorize } from '../../colors/index.js'

import { create, reverse, toPoints } from './index.js'

import { comparePoints, compareVectors } from '../../../test/helpers/index.js'
Expand Down Expand Up @@ -29,3 +31,10 @@ test('reverse: does not modify input geometry', (t) => {
t.true(comparePoints(toPoints(geometry), forward))
t.true(comparePoints(toPoints(another), backward))
})

test('reverse: preserves color', (t) => {
const points = [[0, 0], [1, 0], [0, 1]]
const geometry = colorize([1, 0, 0], create([points]))
const reversed = reverse(geometry)
t.deepEqual(reversed.color, [1, 0, 0, 1])
})
13 changes: 13 additions & 0 deletions packages/modeling/src/operations/extrusions/extrudeLinear.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { comparePolygonsAsPoints } from '../../../test/helpers/index.js'

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

import { colorize } from '../../colors/index.js'

import { geom2, geom3, path2 } from '../../geometries/index.js'

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

test('extrudeLinear: preserves color', (t) => {
const red = colorize([1, 0, 0], square())
const extruded = extrudeLinear({ }, red)
t.deepEqual(extruded.color, [1, 0, 0, 1])

// one red, one blue
const out = extrudeLinear({ }, [red, square()])
t.deepEqual(out[0].color, [1, 0, 0, 1])
t.is(out[1].color, undefined)
})

test('extrudeLinear (no twist)', (t) => {
const geometry2 = square({ size: 10 })

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,7 @@ export const extrudeLinearGeom2 = (options, geometry) => {
repair,
callback: createTwist
}
return extrudeFromSlices(options, baseSlice)
const output = extrudeFromSlices(options, baseSlice)
if (geometry.color) output.color = geometry.color
return output
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ export const extrudeLinearPath2 = (options, geometry) => {
// Convert path2 to geom2
const points = path2.toPoints(geometry)
const geometry2 = geom2.create([points])
if (geometry.color) geometry2.color = geometry.color
return extrudeLinearGeom2(options, geometry2)
}
13 changes: 8 additions & 5 deletions packages/modeling/src/operations/extrusions/extrudeRotate.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const extrudeRotate = (options, geometry) => {
// convert geometry to an array of sides, easier to deal with
let shapeSides = geom2.toSides(geometry)
if (shapeSides.length === 0) throw new Error('the given geometry cannot be empty')
let sliceGeometry = geometry

// determine if the extrusion can be computed in the first place
// ie all the points have to be either x > 0 or x < 0
Expand Down Expand Up @@ -87,8 +88,8 @@ export const extrudeRotate = (options, geometry) => {
return [point0, point1]
})
// recreate the geometry from the (-) capped points
geometry = geom2.reverse(geom2.fromSides(shapeSides))
geometry = mirrorX(geometry)
sliceGeometry = geom2.reverse(geom2.fromSides(shapeSides))
sliceGeometry = mirrorX(sliceGeometry)
} else if (pointsWithPositiveX.length >= pointsWithNegativeX.length) {
shapeSides = shapeSides.map((side) => {
let point0 = side[0]
Expand All @@ -98,13 +99,13 @@ export const extrudeRotate = (options, geometry) => {
return [point0, point1]
})
// recreate the geometry from the (+) capped points
geometry = geom2.fromSides(shapeSides)
sliceGeometry = geom2.fromSides(shapeSides)
}
}

const rotationPerSlice = totalRotation / segments
const isCapped = Math.abs(totalRotation) < TAU
let baseSlice = slice.fromGeom2(geometry)
let baseSlice = slice.fromGeom2(sliceGeometry)
baseSlice = slice.reverse(baseSlice)

const matrix = mat4.create()
Expand All @@ -126,5 +127,7 @@ export const extrudeRotate = (options, geometry) => {
close: !isCapped,
callback: createSlice
}
return extrudeFromSlices(options, baseSlice)
const output = extrudeFromSlices(options, baseSlice)
if (geometry.color) output.color = geometry.color
return output
}
10 changes: 10 additions & 0 deletions packages/modeling/src/operations/extrusions/extrudeRotate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import { comparePoints, comparePolygonsAsPoints } from '../../../test/helpers/in

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

import { colorize } from '../../colors/index.js'

import { geom2, geom3 } from '../../geometries/index.js'

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

import { square } from '../../primitives/index.js'

import { extrudeRotate } from './index.js'

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

test('extrudeRotate: preserves color', (t) => {
const red = colorize([1, 0, 0], square())
const extruded = extrudeRotate({ }, red)
t.deepEqual(extruded.color, [1, 0, 0, 1])
})

test('extrudeRotate: (angle) extruding of a geom2 produces an expected geom3', (t) => {
const geometry2 = geom2.create([[[10, 8], [10, -8], [26, -8], [26, 8]]])

Expand Down
4 changes: 3 additions & 1 deletion packages/modeling/src/operations/extrusions/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ const projectGeom3 = (options, geometry) => {
return geom2.create([cloned])
})

return unionGeom2(projGeoms)
const output = unionGeom2(projGeoms)
if (geometry.color) output.color = geometry.color
return output
}

/**
Expand Down
8 changes: 8 additions & 0 deletions packages/modeling/src/operations/extrusions/project.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import test from 'ava'

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

import { colorize } from '../../colors/index.js'

import { geom2, geom3 } from '../../geometries/index.js'

import { measureArea } from '../../measurements/index.js'
Expand Down Expand Up @@ -139,3 +141,9 @@ test('project torus (martinez issue #155)', (t) => {
t.notThrows(() => geom2.validate(result))
t.is(measureArea(result), 21.15545050788201)
})

test('project: preserves color', (t) => {
const red = colorize([1, 0, 0], cube())
const result = project({ }, red)
t.deepEqual(result.color, [1, 0, 0, 1])
})
5 changes: 4 additions & 1 deletion packages/modeling/src/operations/offsets/offsetGeom2.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ export const offsetGeom2 = (options, geometry) => {
}
return offsetFromPoints(options, outline)
})
// TODO: union outlines that expanded into each other

// create a composite geometry from the new outlines
return geom2.create(newOutlines)
const output = geom2.create(newOutlines)
if (geometry.color) output.color = geometry.color
return output
}
19 changes: 11 additions & 8 deletions packages/modeling/src/operations/offsets/offsetGeom2.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import test from 'ava'

import { colorize } from '../../colors/index.js'
import { geom2 } from '../../geometries/index.js'

import { measureArea } from '../../measurements/index.js'

import { roundedRectangle, square } from '../../primitives/index.js'

import { offset } from './index.js'
Expand All @@ -12,12 +11,10 @@ import { comparePoints } from '../../../test/helpers/index.js'

test('offset: offset an empty geom2', (t) => {
const empty = geom2.create()
const obs = offset({ delta: 1 }, empty)
const pts = geom2.toPoints(obs)
const exp = []
t.notThrows(() => geom2.validate(obs))
t.is(measureArea(obs), 0)
t.true(comparePoints(pts, exp))
const result = offset({ delta: 1 }, empty)
t.notThrows(() => geom2.validate(result))
t.is(measureArea(result), 0)
t.is(geom2.toPoints(result).length, 0)
})

test('offset: offset option validation', (t) => {
Expand All @@ -38,6 +35,12 @@ test('offset: offset option validation', (t) => {
t.throws(() => offset({ corners: 'fluffy' }, empty), { message: 'corners must be "edge", "chamfer", or "round"' })
})

test('offset: offset geom2 preserves color', (t) => {
const geometry = colorize([1, 0, 0], square({ }))
const result = offset({ }, geometry)
t.deepEqual(result.color, [1, 0, 0, 1])
})

test('offset: offset of a geom2 produces expected changes to points', (t) => {
const geometry = square({ size: 16 })

Expand Down
7 changes: 3 additions & 4 deletions packages/modeling/src/operations/offsets/offsetGeom3.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ export const offsetGeom3 = (options, geometry) => {
throw new Error('corners must be "round" for 3D geometries')
}

const polygons = geom3.toPolygons(geometry)
if (polygons.length === 0) throw new Error('the given geometry cannot be empty')

options = { delta, corners, segments }
const expanded = offsetShell(options, geometry)
return union(geometry, expanded)
const output = union(geometry, expanded)
if (geometry.color) output.color = geometry.color
return output
}
20 changes: 18 additions & 2 deletions packages/modeling/src/operations/offsets/offsetGeom3.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,28 @@ import test from 'ava'

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

import { colorize } from '../../colors/index.js'
import { geom3, poly3 } from '../../geometries/index.js'

import { sphere } from '../../primitives/index.js'
import { measureVolume } from '../../measurements/index.js'
import { cube, sphere } from '../../primitives/index.js'

import { offset } from './index.js'

test('offset: offset empty geom3', (t) => {
const geometry = geom3.create()
const result = offset({ }, geometry)
t.notThrows(() => geom3.validate(result))
t.is(measureVolume(result), 0)
t.is(geom3.toPolygons(result).length, 0)
t.is(geom3.toPoints(result).length, 0)
})

test('offset: offset geom3 preserves color', (t) => {
const geometry = colorize([1, 0, 0], cube({ }))
const result = offset({ }, geometry)
t.deepEqual(result.color, [1, 0, 0, 1])
})

test('offset: offset of a geom3 produces expected changes to polygons', (t) => {
const polygonsAsPoints = [
[[-5, -5, -5], [-5, -5, 15], [-5, 15, 15], [-5, 15, -5]],
Expand Down
14 changes: 7 additions & 7 deletions packages/modeling/src/operations/offsets/offsetPath2.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,17 @@ export const offsetPath2 = (options, geometry) => {

const closed = geometry.isClosed
const points = path2.toPoints(geometry)
if (points.length === 0) throw new Error('the given geometry cannot be empty')
if (points.length === 0) return geometry

const paths = {
points: points,
points,
external: offsetFromPoints({ delta, corners, segments, closed }, points),
internal: offsetFromPoints({ delta: -delta, corners, segments, closed }, points)
}

if (geometry.isClosed) {
return createGeometryFromClosedOffsets(paths)
} else {
return createGeometryFromExpandedOpenPath(paths, segments, corners, delta)
}
const output = geometry.isClosed ?
createGeometryFromClosedOffsets(paths) :
createGeometryFromExpandedOpenPath(paths, segments, corners, delta)
if (geometry.color) output.color = geometry.color
return output
}
17 changes: 16 additions & 1 deletion packages/modeling/src/operations/offsets/offsetPath2.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,29 @@ import test from 'ava'

import { comparePoints, nearlyEqual } from '../../../test/helpers/index.js'

import { colorize } from '../../colors/index.js'
import { geom2, geom3, path2 } from '../../geometries/index.js'
import { measureBoundingBox } from '../../measurements/index.js'
import { measureArea, measureBoundingBox } from '../../measurements/index.js'
import { area } from '../../maths/utils/index.js'
import { TAU } from '../../maths/constants.js'
import { sphere, square } from '../../primitives/index.js'

import { offset } from './index.js'

test('offset: offset empty path2', (t) => {
const geometry = path2.create()
const result = offset({ }, geometry)
t.notThrows(() => path2.validate(result))
t.is(measureArea(result), 0)
t.is(path2.toPoints(result).length, 0)
})

test('offset: offset path2 preserves color', (t) => {
const geometry = colorize([1, 0, 0], path2.create())
const result = offset({ }, geometry)
t.deepEqual(result.color, [1, 0, 0, 1])
})

test('offset: edge-expanding a straight line produces rectangle', (t) => {
const points = [[0, 0], [0, 10]]
const linePath2 = path2.fromPoints({ closed: false }, points)
Expand Down
4 changes: 3 additions & 1 deletion packages/modeling/src/operations/offsets/offsetShell.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ export const offsetShell = (options, geometry) => {
}
const { delta, segments } = Object.assign({ }, defaults, options)

const polygons = geom3.toPolygons(geometry)
if (polygons.length === 0) return geometry

let result = geom3.create()
const vertices2planes = new Map() // {vertex: [vertex, [plane, ...]]}
const edges2planes = new Map() // {edge: [[vertex, vertex], [plane, ...]]}
Expand All @@ -82,7 +85,6 @@ export const offsetShell = (options, geometry) => {
// - extruded the polygon, and add to the composite result
// - add the plane to the unique vertex map
// - add the plane to the unique edge map
const polygons = geom3.toPolygons(geometry)
polygons.forEach((polygon, index) => {
const extrudeVector = vec3.scale(vec3.create(), poly3.plane(polygon), 2 * delta)
const translatedPolygon = poly3.transform(mat4.fromTranslation(mat4.create(), vec3.scale(vec3.create(), extrudeVector, -0.5)), polygon)
Expand Down