Skip to content

Commit

Permalink
Merge release-2.12
Browse files Browse the repository at this point in the history
Release 2.12
  • Loading branch information
axelpale authored Mar 10, 2023
2 parents 02acef9 + 16350a4 commit ba4d830
Show file tree
Hide file tree
Showing 141 changed files with 3,895 additions and 76 deletions.
1,261 changes: 1,226 additions & 35 deletions docs/API.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/box2/atNorm.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const at = require('./at')
module.exports = (box, nw, nh) => {
// @affineplane.box2.atNorm(box, nw, nh)
//
// Take a point on the box.
// Take a point on the box at the normalized coordinates.
//
// Parameters
// box
Expand Down
106 changes: 106 additions & 0 deletions lib/box2/collide.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
const getBasis = require('./getBasis')
const getBasisInverse = require('./getBasisInverse')
const getCircle = require('./getCircle')
const getSegments = require('./getSegments')
const planeCompose = require('../plane2/compose')
const pointTransitFrom = require('../point2/transitFrom')
const collideCircles = require('../sphere2/collide')
const collideSegments = require('../segment2/collide')

const inside = (box, point) => {
// Test if a point in the box basis is inside the box.
const px = point.x
const py = point.y
return px >= 0 && px <= box.w && py >= 0 && py <= box.h
}

module.exports = function (b, bb) {
// @affineplane.box2.collide(b, bb)
//
// Do two boxes collide?
//
// Parameters:
// b
// a box2, in the reference basis
// bb
// a box2, in the reference basis
//
// Return:
// a boolean, true if boxes collide.
//

// We can first quickly determine if boxes cannot collide by
// comparing their bounding spheres. If the spheres do not collide,
// the boxes cannot collide. If the spheres do collide, we need to
// test further do the boxes truly collide.

// Boxes can collide without edges crossing.
// Think of nested boxes.
// Also, boxes can collide without any of the corners inside another.
// Think of two long boards crossed.

// Therefore we need to check if at least one of the corners is inside
// the other box, and if not, check if any of the edge segments collide.
// We need 4x4 segment collision tests.

// For nested boxes, we need to test if a corner point is inside.
// We do not know the nesting order, thus test both directions.
// We need to test only one corner per direction. Alternatively we could
// test each corner, which would imply 4 transits and 4 tests.

// Test bounding circle collision
const bCircle = getCircle(b)
const bbCircle = getCircle(bb)
const circleHit = collideCircles(bCircle, bbCircle)
if (!circleHit) {
// Bounding circles do not touch, thus we can exit quickly.
return false
}
// Else the circles collide and we need to inspect further.

// Test if boxes are nested.
// Form transitions between boxes
const bToRef = getBasis(b)
const refToB = getBasisInverse(b)
const bbToRef = getBasis(bb)
const refToBb = getBasisInverse(bb)
// bToBb is a box plane b, represented on the box plane bb.
const bToBb = planeCompose(refToBb, bToRef)
// bbToB is a box plane bb, represented on the box plane b.
const bbToB = planeCompose(refToB, bbToRef)

// Test if top left corner of box b is inside box bb.
const corner = { x: 0, y: 0 }
const cornerInBb = pointTransitFrom(corner, bToBb)
const cornerInB = pointTransitFrom(corner, bbToB)

const isInsideBb = inside(bb, cornerInBb)
const isInsideB = inside(b, cornerInB)

if (isInsideB || isInsideBb) {
// Boxes are nested and therefore collide.
return true
}
// Else, boxes are not nested, but still can collide.

// Test if box segments cross. Any crossing implies collision.
const segsB = getSegments(b)
const segsBb = getSegments(bb)
const len = 4

let i, j, segb, segbb
// For each b segment
for (i = 0; i < len; i += 1) {
segb = segsB[i]
// For each bb segment
for (j = 0; j < len; j += 1) {
segbb = segsBb[j]
if (collideSegments(segb, segbb)) {
return true
}
}
}
// Segments did not cross.

return false
}
27 changes: 27 additions & 0 deletions lib/box2/getCircle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const at = require('./at')

module.exports = (box) => {
// @affineplane.box2.getCircle(box)
// @affineplane.box2.getSphere
//
// Get the circumscribed circle of the box. In other words, get the circle
// that contains the box so that the box corners are on it.
// The resulting circle is also the minimum bounding circle of the box.
//
// Parameters
// box
// a box2, in the reference basis.
//
// Return
// a sphere2
//

// Circle center
const hw = box.w / 2
const hh = box.h / 2
const p = at(box, hw, hh)
// Patch radius
p.r = Math.sqrt(hw * hw + hh * hh)

return p
}
26 changes: 26 additions & 0 deletions lib/box2/getSegments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const at = require('./at')

module.exports = (box) => {
// @affineplane.box2.getSegments(box)
//
// Get the four line segments of the box.
//
// Parameters:
// box
// a box2, in the reference basis.
//
// Return
// array of segment2, each segment in the reference basis.
//
const c00 = at(box, 0, 0)
const cW0 = at(box, box.w, 0)
const cWH = at(box, box.w, box.h)
const c0H = at(box, 0, box.h)

return [
[c00, cW0],
[cW0, cWH],
[cWH, c0H],
[c0H, c00]
]
}
33 changes: 33 additions & 0 deletions lib/box2/hasPoint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const getBasisInverse = require('./getBasisInverse')
const pointTransitFrom = require('../point2/transitFrom')

module.exports = function (box, point) {
// @affineplane.box2.hasPoint(box, point)
//
// Test if a point is inside the box. If the point is at the box edge,
// it is counted as being inside.
//
// Parameters:
// box
// a box2, in the reference basis.
// point
// a point2, in the reference basis.
//
// Return:
// a boolean
//

// Note: we thought it would be practical to count inner box (0,0) inside
// and inner box (w,h) outside. However, due to rotation that makes things
// confusing. Therefore all edge points are counted as inside.

// Transit the point to the inner box basis for easier checking.
const innerBasis = getBasisInverse(box)
const innerPoint = pointTransitFrom(point, innerBasis)

// Test the point
const px = innerPoint.x
const py = innerPoint.y

return px >= 0 && px <= box.w && py >= 0 && py <= box.h
}
16 changes: 8 additions & 8 deletions lib/box2/homothety.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
const pointHomothety = require('../point2/homothety')
const scaleSize = require('../size2/scaleBy')

module.exports = (box, center, ratio) => {
// @affineplane.box2.homothety(box, center, ratio)
// @affineplane.box2.scaleBy(box, center, ratio)
module.exports = (box, origin, ratio) => {
// @affineplane.box2.homothety(box, origin, ratio)
// @affineplane.box2.scaleBy(box, origin, ratio)
//
// Perform homothety about the center. In other words, scale the box
// about the fixed center point.
// Perform homothety about the origin. In other words, scale the box
// about the fixed pivot point.
//
// Parameters:
// box
// a box2, the box to be scaled.
// center
// a point2, the fixed pivot point.
// origin
// a point2, the fixed origin point.
// ratio
// a number, the scaling ratio.
//
Expand All @@ -21,7 +21,7 @@ module.exports = (box, center, ratio) => {
//

// Scale component-wise
const boxOrigin = pointHomothety(box, center, ratio)
const boxOrigin = pointHomothety(box, origin, ratio)
const boxSize = scaleSize(box, ratio)

// Patch size.
Expand Down
9 changes: 8 additions & 1 deletion lib/box2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,25 @@ exports.almostEqual = require('./almostEqual')
exports.at = require('./at')
exports.atBox = require('./atBox')
exports.atNorm = require('./atNorm')
exports.collide = require('./collide')
exports.create = require('./create')
exports.fromPoints = require('./fromPoints')
exports.getAngle = require('./getAngle')
exports.getArea = require('./getArea')
exports.getBasis = require('./getBasis')
exports.getBasisInverse = require('./getBasisInverse')
exports.getBounds = require('./getBounds')
exports.getCircle = require('./getCircle')
exports.getSphere = exports.getCircle
exports.getMinimumBounds = require('./getMinimumBounds')
exports.getPath = require('./getPath')
exports.getPoints = exports.getPath
exports.getPolygon = exports.getPath
exports.getSegments = require('./getSegments')
exports.getSize = require('./getSize')
exports.hasPoint = require('./hasPoint')
exports.homothety = require('./homothety')
exports.offset = require('./offset')
exports.projectToPlane = require('./projectToPlane')
exports.projectTo = exports.projectToPlane
exports.resizeBy = require('./resizeBy')
Expand All @@ -34,5 +40,6 @@ exports.rotateBy = require('./rotateBy')
exports.scaleBy = exports.homothety
exports.transitFrom = require('./transitFrom')
exports.transitTo = require('./transitTo')
exports.translateBy = require('./translateBy')
exports.translate = require('./translate')
exports.translateBy = exports.offset // TODO alias translate in v3
exports.validate = require('./validate')
2 changes: 1 addition & 1 deletion lib/box2/translateBy.js → lib/box2/offset.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = (box, dx, dy) => {
// @affineplane.box2.translateBy(box, dx, dy)
// @affineplane.box2.offset(box, dx, dy)
//
// Move the box horizontally and vertically.
//
Expand Down
24 changes: 24 additions & 0 deletions lib/box2/translate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module.exports = (box, vec) => {
// @affineplane.box2.translate(box, vec)
//
// Move the box horizontally and vertically by a vector.
// See affineplane.box2.offset to move by scalars.
//
// Parameters:
// box
// a box2
// vec
// a vec2
//
// Return
// a box2
//
return {
a: box.a,
b: box.b,
x: box.x + vec.x,
y: box.y + vec.y,
w: box.w,
h: box.h
}
}
23 changes: 23 additions & 0 deletions lib/box3/getBasisInverse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = (box) => {
// @affineplane.box3.getBasisInverse(box)
//
// Get the outer basis of the box represented in the box basis.
// The scale of the resulting basis is always 1.
// See also affineplane.box3.getBasis.
//
// Parameters:
// box
// a box3 in the reference basis.
//
// Return
// a plane3 in the box basis. The outer basis.
//

return {
a: box.a,
b: -box.b,
x: -box.a * box.x - box.b * box.y,
y: box.b * box.x - box.a * box.y,
z: -box.z
}
}
27 changes: 27 additions & 0 deletions lib/box3/getSphere.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const at = require('./at')

module.exports = (box) => {
// @affineplane.box3.getSphere(box)
//
// Get the circumscribed sphere of the box. In other words, get the sphere
// that contains the box so that the box corners are on the sphere.
// The resulting sphere is also the minimum bounding sphere of the box.
//
// Parameters
// box
// a box3, in the reference basis.
//
// Return
// a sphere3
//

// Sphere center
const hw = box.w / 2
const hh = box.h / 2
const hd = box.d / 2
const p = at(box, hw, hh, hd)
// Patch radius
p.r = Math.sqrt(hw * hw + hh * hh + hd * hd)

return p
}
32 changes: 32 additions & 0 deletions lib/box3/hasPoint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const getBasisInverse = require('./getBasisInverse')
const pointTransitFrom = require('../point3/transitFrom')

module.exports = function (box, point) {
// @affineplane.box3.hasPoint(box, point)
//
// Test if a point is inside the box. If the point is at the box edge,
// it is counted as being inside.
//
// Parameters:
// box
// a box3, in the reference basis.
// point
// a point3, in the reference basis.
//
// Return:
// a boolean
//

// Transit the point to the inner box basis for easier checking.
const innerBasis = getBasisInverse(box)
const innerPoint = pointTransitFrom(point, innerBasis)

// Test the point
const px = innerPoint.x
const py = innerPoint.y
const pz = innerPoint.z

return (px >= 0 && px <= box.w &&
py >= 0 && py <= box.h &&
pz >= 0 && pz <= box.d)
}
Loading

0 comments on commit ba4d830

Please sign in to comment.