Skip to content

Commit

Permalink
Merge release-2.17
Browse files Browse the repository at this point in the history
Release 2.17
  • Loading branch information
axelpale committed Oct 15, 2023
2 parents 7c5107e + cba31d4 commit 2b82957
Show file tree
Hide file tree
Showing 19 changed files with 367 additions and 153 deletions.
240 changes: 94 additions & 146 deletions docs/API.md

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions lib/orient2/fromPolar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const sqrt = Math.sqrt
const sin = Math.sin
const cos = Math.cos

module.exports = (direction) => {
// @affineplane.orient2.fromPolar(direction)
// @affineplane.orient2.fromVector(vec)
//
// Create an orientation from angle or vector.
/// TODO See also affineplane.orient2.toPolar
//
// Parameters:
// direction
// a number, the azimuth angle in radians.
// a dir2
// a vec2
//
// Return
// an orient2
//

if (!direction) {
return { a: 1, b: 0 } // default
}

if (typeof direction === 'object') {
const x = direction.x
const y = direction.y
const norm = sqrt(x * x + y * y) // sums up to vector length
return {
// Normalize to 1
a: direction.x / norm,
b: direction.y / norm
}
}

if (typeof direction === 'number') {
// Radians
return {
a: cos(direction),
b: sin(direction)
}
}

return { a: 1, b: 0 } // default
}
2 changes: 2 additions & 0 deletions lib/orient2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//
exports.almostEqual = require('./almostEqual')
exports.create = require('./create')
exports.fromPolar = require('./fromPolar')
exports.fromVector = exports.fromPolar
exports.transitFrom = require('./transitFrom')
exports.transitTo = require('./transitTo')
exports.validate = require('./validate')
22 changes: 16 additions & 6 deletions lib/plane3/projectToPlane.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,30 @@ module.exports = (plane, target, camera) => {
if (camera) {
// Find scaling ratio.
let ratio
// Distances to camera.
// Distance from camera to the plane
// Distances from camera to the target.
const distToPlane = camera.z - plane.z
const distToTarget = camera.z - target.z
// If camera is at the plane depth,
// the ratio would become infinite.
if (distToPlane === 0) {
// Becomes singular
// Becomes singular.
// TODO throw error instead singular?
plane = { a: 0, b: 0, x: camera.x, y: camera.y }
} else {
ratio = distToTarget / distToPlane
// Treat camera as 2d point projected onto the reference plane.
// Treat plane as plane2 orthogonally projected onto the reference plane.
plane = scaleBy(plane, camera, ratio)
if (distToTarget === 0) {
// Camera on the target. Projection would have infinite size.
// Becomes singular.
// TODO throw error instead singular?
plane = { a: 0, b: 0, x: camera.x, y: camera.y }
} else {
// Scaling ratio
ratio = distToTarget / distToPlane

// Treat camera as 2d point projected onto the reference plane.
// Treat plane as plane2 orthogonally projected onto the reference plane.
plane = scaleBy(plane, camera, ratio)
}
}
}

Expand Down
50 changes: 50 additions & 0 deletions lib/sphere2/collisionArea.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
module.exports = (c, cc) => {
// @affineplane.sphere2.collisionArea(c, cc)
//
// Compute collision area between two spheres.
//
// Parameters:
// c
// a sphere2
// cc
// a sphere2
//
// Return
// a scalar2, number, the area.
//

// For details about circle intersection, see:
// https://mathworld.wolfram.com/Circle-CircleIntersection.html
const dx = cc.x - c.x
const dy = cc.y - c.y
const d2 = dx * dx + dy * dy
const r = c.r
const rr = cc.r
const d = Math.sqrt(d2)
const r2 = r * r
const rr2 = rr * rr

if (r === 0 || rr === 0) {
// One of the circles is a point.
return 0
}

if (d === 0) {
// Concentric circles. Area of the smaller is the overlap.
if (r <= rr) {
return Math.PI * r2
}
return Math.PI * rr2
}

if (d > r + rr) {
// Circles too distant. Just for optimization.
return 0
}

const area = r2 * Math.acos((d2 + r2 - rr2) / (2 * d * r)) +
rr2 * Math.acos((d2 + rr2 - r2) / (2 * d * rr)) -
0.5 * Math.sqrt((-d + r + rr) * (d + r - rr) * (d - r + rr) * (d + r + rr))

return area
}
23 changes: 23 additions & 0 deletions lib/sphere2/fromPoints.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = (p, q) => {
// @affineplane.sphere2.fromPoints(p, q)
//
// Create a sphere2 from two points,
// the origin p and the circumference point q.
//
// Parameters:
// p
// a point2, at the circle center.
// q
// a point2, on the circle circumference.
//
// Return
// a sphere2
//
const dx = q.x - p.x
const dy = q.y - p.y
return {
x: p.x,
y: p.y,
r: Math.sqrt(dx * dx + dy * dy)
}
}
2 changes: 2 additions & 0 deletions lib/sphere2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ exports.atCenter = require('./atCenter')
exports.boundingBox = require('./boundingBox')
exports.boundingCircle = require('./boundingCircle')
exports.collide = require('./collide')
exports.collisionArea = require('./collisionArea')
exports.copy = require('./copy')
exports.create = require('./create')
exports.fromPoints = require('./fromPoints')
exports.gap = require('./gap')
exports.hasPoint = require('./hasPoint')
exports.homothety = require('./homothety')
Expand Down
25 changes: 25 additions & 0 deletions lib/sphere3/fromPoints.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module.exports = (p, q) => {
// @affineplane.sphere3.fromPoints(p, q)
//
// Create a sphere3 from two points,
// the origin p and the circumference point q.
//
// Parameters:
// p
// a point3, at the circle center.
// q
// a point3, on the circle circumference.
//
// Return
// a sphere3
//
const dx = q.x - p.x
const dy = q.y - p.y
const dz = q.z - p.z
return {
x: p.x,
y: p.y,
z: p.z,
r: Math.sqrt(dx * dx + dy * dy + dz * dz)
}
}
1 change: 1 addition & 0 deletions lib/sphere3/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ exports.boundingSphere = require('./boundingSphere')
exports.collide = require('./collide')
exports.copy = require('./copy')
exports.create = require('./create')
exports.fromPoints = require('./fromPoints')
exports.gap = require('./gap')
exports.hasPoint = require('./hasPoint')
exports.homothety = require('./homothety')
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"standard": "^17.0.0",
"tap-arc": "^0.3.5",
"tape": "^5.6.3",
"yamdog": "^2.0.0"
"yamdog": "^2.1.0"
},
"scripts": {
"lint": "standard index.js 'lib/**/*.js' 'test/**/*.js'",
Expand Down
1 change: 1 addition & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ test.Test.prototype.almostEqualBasis = require('./utils/almostEqualBasis')
test.Test.prototype.almostEqualBox = require('./utils/almostEqualBox')
test.Test.prototype.almostEqualCircle = require('./utils/almostEqualSphere')
test.Test.prototype.almostEqualHelmert = require('./utils/almostEqualHelmert')
test.Test.prototype.almostEqualOrient = require('./utils/almostEqualOrient')
test.Test.prototype.almostEqualPoint = require('./utils/almostEqualPoint')
test.Test.prototype.almostEqualSphere = test.Test.prototype.almostEqualCircle
test.Test.prototype.almostEqualVector = require('./utils/almostEqualVector')
Expand Down
20 changes: 20 additions & 0 deletions test/orient2/fromPolar.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const affineplane = require('../../index')
const orient2 = affineplane.orient2

module.exports = (ts) => {
ts.test('case: basic fromPolar', (t) => {
t.almostEqualOrient(
orient2.fromPolar(0),
{ a: 1, b: 0 },
'zero angle'
)

t.almostEqualOrient(
orient2.fromPolar(Math.PI / 2),
{ a: 0, b: 1 },
'angle of +90deg'
)

t.end()
})
}
1 change: 1 addition & 0 deletions test/orient2/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
const units = {
almostEqual: require('./almostEqual.test'),
create: require('./create.test'),
fromPolar: require('./fromPolar.test'),
validate: require('./validate.test')
}

Expand Down
33 changes: 33 additions & 0 deletions test/sphere2/collisionArea.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const sphere2 = require('../../lib/sphere2')

module.exports = (ts) => {
ts.test('case: basic collisionArea', (t) => {
t.equal(
sphere2.collisionArea({ x: 0, y: 0, r: 0 }, { x: 0, y: 0, r: 0 }),
0,
'zero spheres have zero overlap'
)

t.equal(
sphere2.collisionArea({ x: 0, y: 0, r: 1 }, { x: 0, y: 0, r: 2 }),
Math.PI,
'concentric spheres overlap'
)

t.equal(
sphere2.collisionArea({ x: 0, y: 0, r: 1 }, { x: 1.5, y: 1.5, r: 1 }),
0,
'spheres too far'
)

t.almostEqual(
sphere2.collisionArea({ x: 0, y: 0, r: 1 }, { x: 1, y: 0, r: 1 }),
// overlap = 2 * segment
// segment = π/3 - sqrt(3)/4
(2 * Math.PI / 3) - Math.sqrt(3) / 2,
'spheres overlap'
)

t.end()
})
}
15 changes: 15 additions & 0 deletions test/sphere2/fromPoints.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const sphere2 = require('../../lib/sphere2')

module.exports = (ts) => {
ts.test('case: create sphere2 from two points', (t) => {
const p1 = { x: 1, y: 1 }
const p2 = { x: 5, y: 4 }
t.deepEqual(
sphere2.fromPoints(p1, p2),
{ x: 1, y: 1, r: 5 },
'correct origin and radius'
)

t.end()
})
}
2 changes: 2 additions & 0 deletions test/sphere2/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const units = {
boundingBox: require('./boundingBox.test'),
boundingCircle: require('./boundingCircle.test'),
collide: require('./collide.test'),
collisionArea: require('./collisionArea.test'),
fromPoints: require('./fromPoints.test'),
gap: require('./gap.test'),
hasPoint: require('./hasPoint.test'),
homothety: require('./homothety.test'),
Expand Down
15 changes: 15 additions & 0 deletions test/sphere3/fromPoints.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const sphere3 = require('../../lib/sphere3')

module.exports = (ts) => {
ts.test('case: create sphere3 from two points', (t) => {
const p1 = { x: 1, y: 1, z: 1 }
const p2 = { x: 3, y: 3, z: 2 }
t.deepEqual(
sphere3.fromPoints(p1, p2),
{ x: 1, y: 1, z: 1, r: 3 },
'correct origin and radius'
)

t.end()
})
}
1 change: 1 addition & 0 deletions test/sphere3/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const units = {
boundingBox: require('./boundingBox.test'),
boundingSphere: require('./boundingSphere.test'),
collide: require('./collide.test'),
fromPoints: require('./fromPoints.test'),
gap: require('./gap.test'),
hasPoint: require('./hasPoint.test'),
homothety: require('./homothety.test'),
Expand Down
19 changes: 19 additions & 0 deletions test/utils/almostEqualOrient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const orient2 = require('../../lib/orient2')

module.exports = function (actual, expected, message) {
// Custom tape.js assertion.

let isEqual = false
if (orient2.validate(expected)) {
// Expect orient2
isEqual = orient2.validate(actual)
isEqual = isEqual && orient2.almostEqual(actual, expected)
}

this._assert(isEqual, {
message: message || 'orientation should have correct elements',
operator: 'almostEqualOrientation',
actual,
expected
})
}

0 comments on commit 2b82957

Please sign in to comment.