Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(modeling): undo-revert measureBoundingBox #973

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
169 changes: 131 additions & 38 deletions packages/modeling/src/measurements/measureBoundingBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const flatten = require('../utils/flatten')

const vec2 = require('../maths/vec2')
const vec3 = require('../maths/vec3')
const mat4 = require('../maths/mat4')

const geom2 = require('../geometries/geom2')
const geom3 = require('../geometries/geom3')
Expand All @@ -11,86 +12,129 @@ const poly3 = require('../geometries/poly3')
const cache = new WeakMap()

/*
* Measure the min and max bounds of the given (path2) geometry.
* @return {Array[]} the min and max bounds for the geometry
* Measure the min and max bounds of the given (path2) geometry.points.
* @return {Array[]} the min and max bounds for the geometry.points
*/
const measureBoundingBoxOfPath2 = (geometry) => {
let boundingBox = cache.get(geometry)
const measureBoundingBoxOfPath2Points = (points) => {
let boundingBox = cache.get(points)
if (boundingBox) return boundingBox

const points = path2.toPoints(geometry)

let minpoint
if (points.length === 0) {
minpoint = vec2.create()
} else {
minpoint = vec2.clone(points[0])
}
let maxpoint = vec2.clone(minpoint)
const maxpoint = vec2.clone(minpoint)

points.forEach((point) => {
vec2.min(minpoint, minpoint, point)
vec2.max(maxpoint, maxpoint, point)
})
minpoint = [minpoint[0], minpoint[1], 0]
maxpoint = [maxpoint[0], maxpoint[1], 0]

boundingBox = [minpoint, maxpoint]

cache.set(geometry, boundingBox)
boundingBox = [[minpoint[0], minpoint[1], 0], [maxpoint[0], maxpoint[1], 0]]

cache.set(points, boundingBox)
return boundingBox
}

/*
* Measure the min and max bounds of the given (geom2) geometry.
* Measure the min and max bounds of the given (path2) geometry.
* @return {Array[]} the min and max bounds for the geometry
*/
const measureBoundingBoxOfGeom2 = (geometry) => {
const measureBoundingBoxOfPath2 = (geometry) => {
let boundingBox = cache.get(geometry)
if (boundingBox) return boundingBox

const points = geom2.toPoints(geometry)

let minpoint
if (points.length === 0) {
minpoint = vec2.create()
if (mat4.isOnlyTransformScale(geometry.transforms)) {
// get boundingBox of original points and transform it
boundingBox = transformBoundingBox(measureBoundingBoxOfPath2Points(geometry.points), geometry.transforms)
} else {
minpoint = vec2.clone(points[0])
// transform the points and then caclulate the boundingBox
boundingBox = measureBoundingBoxOfPath2Points(path2.toPoints(geometry))
}
let maxpoint = vec2.clone(minpoint)

points.forEach((point) => {
vec2.min(minpoint, minpoint, point)
vec2.max(maxpoint, maxpoint, point)
})
cache.set(geometry, boundingBox)
return boundingBox
}

minpoint = [minpoint[0], minpoint[1], 0]
maxpoint = [maxpoint[0], maxpoint[1], 0]
/*
* Measure the min and max bounds of the given (geom2) geometry.points/sides.
* @return {Array[]} the min and max bounds for the geometr.points/sidesy
*/
const measureBoundingBoxOfGeom2Points = ({ points, sides }) => {
const cacheKey = points || sides

boundingBox = [minpoint, maxpoint]
let boundingBox = cache.get(cacheKey)
if (boundingBox) return boundingBox

cache.set(geometry, boundingBox)
let minpoint, maxpoint

if (points) {
if (points.length === 0) {
minpoint = vec2.create()
} else {
minpoint = vec2.clone(points[0])
}
maxpoint = vec2.clone(minpoint)

points.forEach((point) => {
vec2.min(minpoint, minpoint, point)
vec2.max(maxpoint, maxpoint, point)
})
} else { // sides
// to avoid calling costly toPoints, we take advantage of the knowlege how the toPoints works
if (sides.length === 0) {
minpoint = vec2.create()
} else {
minpoint = vec2.clone(sides[0][0])
}
maxpoint = vec2.clone(minpoint)

sides.forEach((side) => {
vec2.min(minpoint, minpoint, side[0])
vec2.max(maxpoint, maxpoint, side[0])
})
}
boundingBox = [[minpoint[0], minpoint[1], 0], [maxpoint[0], maxpoint[1], 0]]

cache.set(cacheKey, boundingBox)
return boundingBox
}

/*
* Measure the min and max bounds of the given (geom3) geometry.
* Measure the min and max bounds of the given (geom2) geometry.
* @return {Array[]} the min and max bounds for the geometry
*/
const measureBoundingBoxOfGeom3 = (geometry) => {
const measureBoundingBoxOfGeom2 = (geometry) => {
let boundingBox = cache.get(geometry)
if (boundingBox) return boundingBox

const polygons = geom3.toPolygons(geometry)
if (mat4.isOnlyTransformScale(geometry.transforms)) {
// get boundingBox of original points and transform it
boundingBox = transformBoundingBox(measureBoundingBoxOfGeom2Points(geometry), geometry.transforms)
} else {
// transform the points and then caclulate the boundingBox
boundingBox = measureBoundingBoxOfGeom2Points({ points: geom2.toPoints(geometry) })
}

cache.set(geometry, boundingBox)
return boundingBox
}

/*
* Measure the min and max bounds of the given (geom3) geometry.polygons.
* @return {Array[]} the min and max bounds for the geometry.polygons
*/
const measureBoundingBoxOfGeom3Polygons = (polygons) => {
let boundingBox = cache.get(polygons)
if (boundingBox) return boundingBox

let minpoint = vec3.create()
const minpoint = vec3.create()
if (polygons.length > 0) {
const points = poly3.toPoints(polygons[0])
vec3.copy(minpoint, points[0])
}
let maxpoint = vec3.clone(minpoint)
const maxpoint = vec3.clone(minpoint)

polygons.forEach((polygon) => {
poly3.toPoints(polygon).forEach((point) => {
Expand All @@ -99,13 +143,62 @@ const measureBoundingBoxOfGeom3 = (geometry) => {
})
})

minpoint = [minpoint[0], minpoint[1], minpoint[2]]
maxpoint = [maxpoint[0], maxpoint[1], maxpoint[2]]
boundingBox = [[minpoint[0], minpoint[1], minpoint[2]], [maxpoint[0], maxpoint[1], maxpoint[2]]]

boundingBox = [minpoint, maxpoint]
cache.set(polygons, boundingBox)
return boundingBox
}

/*
* Measure the min and max bounds of the given (geom3) geometry.
* @return {Array[]} the min and max bounds for the geometry
*/
const measureBoundingBoxOfGeom3 = (geometry) => {
let boundingBox = cache.get(geometry)
if (boundingBox) return boundingBox

if (mat4.isOnlyTransformScale(geometry.transforms)) {
// get boundingBox of original points and transform it
boundingBox = transformBoundingBox(measureBoundingBoxOfGeom3Polygons(geometry.polygons), geometry.transforms)
} else {
// transform the points and then caclulate the boundingBox
boundingBox = measureBoundingBoxOfGeom3Polygons(geom3.toPolygons(geometry))
}

cache.set(geometry, boundingBox)
return boundingBox
}

/*
* swap values if specific axis value in the second vector has lower value than the axis value in first vector
*/
const fixBound = (i, v1, v2) => {
if (v1[i] > v2[i]) {
const tmp = v1[i]
v1[i] = v2[i]
v2[i] = tmp
}
}

/*
* Transform the given bounding box
*/
const transformBoundingBox = (boundingBox, transforms) => {
if (!mat4.isIdentity(transforms)) {
// create a new boundingBox. Mutating the passed boundingBox would break cached calculations
boundingBox = [
vec3.transform(vec3.create(), boundingBox[0], transforms),
vec3.transform(vec3.create(), boundingBox[1], transforms)
]

// we now have a new 2 vectors: [v1,v2] => [ [x1,y1,z1], [x2,y2,z2] ]
// transform can move bounding box corner in such way that it is no longer true that
// - v1 = [min(x1,x2),min(y1,y2),min(z1,z2)]
// - v2 = [max(x1,x2),max(y1,y2),max(z1,z2)]
fixBound(0, ...boundingBox) // swap x, if higher value is in first vector
fixBound(1, ...boundingBox) // swap y, if higher value is in first vector
fixBound(2, ...boundingBox) // swap z, if higher value is in first vector
}
return boundingBox
}

Expand Down
19 changes: 18 additions & 1 deletion packages/modeling/src/measurements/measureBoundingBox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const { geom2, geom3, path2 } = require('../geometries')

const { line, rectangle, cuboid } = require('../primitives')

const { mirror } = require('../operations/transforms')
const { mirror, translate } = require('../operations/transforms')

const { measureBoundingBox } = require('./index')

Expand Down Expand Up @@ -46,6 +46,23 @@ test('measureBoundingBox (single objects)', (t) => {
t.deepEqual(xbounds, [[0, 0, 0], [0, 0, 0]])
})

test('measureBoundingBox transformed', (t) => {

const acube1 = cuboid()
const acube2 = translate([1,1,1], acube1)
const acube3 = translate([1,1,1], acube2)

let cbounds1 = measureBoundingBox(acube1)
let cbounds2 = measureBoundingBox(acube2)
let cbounds3 = measureBoundingBox(acube3)

t.deepEqual(cbounds1, [[-1, -1, -1], [1, 1, 1]])
t.deepEqual(cbounds2, [[0, 0, 0], [2, 2, 2]])
t.deepEqual(cbounds3, [[1, 1, 1], [3, 3, 3]])

})


test('measureBoundingBox (multiple objects)', (t) => {
const aline = line([[10, 10], [15, 15]])
const arect = rectangle({ size: [10, 20] })
Expand Down