From 9cf7431a0623a85f274021262a1c52307bc00c79 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Fri, 28 Apr 2023 15:56:28 +0300 Subject: [PATCH 01/21] init ray3 --- lib/index.js | 3 +-- lib/ray3/copy.js | 21 +++++++++++++++++++++ lib/ray3/index.js | 8 ++++++++ test/index.test.js | 1 + test/ray3/copy.test.js | 13 +++++++++++++ test/ray3/index.test.js | 10 ++++++++++ 6 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 lib/ray3/copy.js create mode 100644 lib/ray3/index.js create mode 100644 test/ray3/copy.test.js create mode 100644 test/ray3/index.test.js diff --git a/lib/index.js b/lib/index.js index 7d6ef79..f8f4ac9 100644 --- a/lib/index.js +++ b/lib/index.js @@ -56,8 +56,7 @@ exports.point3 = require('./point3') // TODO exports.range1 = require('./range1') -// TODO Ray. A line but into one direction only. -// exports.ray3 = require('./ray3') +exports.ray3 = require('./ray3') exports.rect2 = require('./rect2') exports.rect3 = require('./rect3') diff --git a/lib/ray3/copy.js b/lib/ray3/copy.js new file mode 100644 index 0000000..05337d5 --- /dev/null +++ b/lib/ray3/copy.js @@ -0,0 +1,21 @@ +module.exports = (r) => { + // @affineplane.ray3.copy(r) + // + // Copy ray object. + // + // Parameters + // r + // a ray3 + // + // Return + // a ray3 + // + return { + x: r.x, + y: r.y, + z: r.z, + dx: r.dx, + dy: r.dy, + dz: r.dz + } +} diff --git a/lib/ray3/index.js b/lib/ray3/index.js new file mode 100644 index 0000000..90eb937 --- /dev/null +++ b/lib/ray3/index.js @@ -0,0 +1,8 @@ +// @affineplane.ray3 +// +// Ray is like a line but extends into one direction only from the origin. +// +// We represent ray with object { x, y, z, dx, dy, dz } +// + +exports.copy = require('./copy') diff --git a/test/index.test.js b/test/index.test.js index 0508a72..9dc8f1b 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -24,6 +24,7 @@ const units = { 'affineplane.point2': require('./point2/index.test'), 'affineplane.point3': require('./point3/index.test'), 'affineplane.quat4': require('./quat4/index.test'), + 'affineplane.ray3': require('./ray3/index.test'), 'affineplane.rect2': require('./rect2/index.test'), 'affineplane.rect3': require('./rect3/index.test'), 'affineplane.rot2': require('./rot2/index.test'), diff --git a/test/ray3/copy.test.js b/test/ray3/copy.test.js new file mode 100644 index 0000000..05930e0 --- /dev/null +++ b/test/ray3/copy.test.js @@ -0,0 +1,13 @@ +const ray3 = require('../../lib/ray3') + +module.exports = (ts) => { + ts.test('case: copy ray3', (t) => { + const r = { x: 1, y: 2, z: 3, dx: 1, dy: 2, dz: 3 } + const c = ray3.copy(r) + + t.deepEqual(r, c, 'same content') + t.notEqual(r, c, 'not same object') + + t.end() + }) +} diff --git a/test/ray3/index.test.js b/test/ray3/index.test.js new file mode 100644 index 0000000..4e01003 --- /dev/null +++ b/test/ray3/index.test.js @@ -0,0 +1,10 @@ +// A unit for each method. +const units = { + copy: require('./copy.test') +} + +module.exports = (t) => { + Object.keys(units).forEach((unitName) => { + t.test('affineplane.ray3.' + unitName, units[unitName]) + }) +} From 48ae6d5fcc25b3340f458bc1f06c096f4d28144e Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 13:27:11 +0300 Subject: [PATCH 02/21] implement ray3.create --- lib/ray3/create.js | 23 +++++++++++++++++++++++ lib/ray3/index.js | 1 + test/ray3/create.test.js | 13 +++++++++++++ test/ray3/index.test.js | 3 ++- 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 lib/ray3/create.js create mode 100644 test/ray3/create.test.js diff --git a/lib/ray3/create.js b/lib/ray3/create.js new file mode 100644 index 0000000..0bd6e3e --- /dev/null +++ b/lib/ray3/create.js @@ -0,0 +1,23 @@ +module.exports = (origin, span) => { + // @affineplane.ray3.create(origin, span) + // + // Create a ray object from origin point and a spanning vector. + // + // Parameters + // origin + // a point3, the ray starting point. + // span + // a dir3 or vec3, the ray direction and unit length along the ray. + // + // Return + // a ray3 + // + return { + x: origin.x, + y: origin.y, + z: origin.z, + dx: span.x, + dy: span.y, + dz: span.z + } +} diff --git a/lib/ray3/index.js b/lib/ray3/index.js index 90eb937..2167757 100644 --- a/lib/ray3/index.js +++ b/lib/ray3/index.js @@ -6,3 +6,4 @@ // exports.copy = require('./copy') +exports.create = require('./create') diff --git a/test/ray3/create.test.js b/test/ray3/create.test.js new file mode 100644 index 0000000..4530d37 --- /dev/null +++ b/test/ray3/create.test.js @@ -0,0 +1,13 @@ +const ray3 = require('../../lib/ray3') + +module.exports = (ts) => { + ts.test('case: create ray3', (t) => { + const origin = { x: 1, y: 2, z: 3 } + const span = { x: 1, y: 2, z: 3 } + const r = ray3.create(origin, span) + + t.deepEqual(r, { x: 1, y: 2, z: 3, dx: 1, dy: 2, dz: 3 }, 'same values') + + t.end() + }) +} diff --git a/test/ray3/index.test.js b/test/ray3/index.test.js index 4e01003..baf9ffd 100644 --- a/test/ray3/index.test.js +++ b/test/ray3/index.test.js @@ -1,6 +1,7 @@ // A unit for each method. const units = { - copy: require('./copy.test') + copy: require('./copy.test'), + create: require('./create.test') } module.exports = (t) => { From 815b229a2efdaf79bb79455b9f961b80b3d505a4 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 13:35:56 +0300 Subject: [PATCH 03/21] implement ray3.validate --- lib/ray3/index.js | 1 + lib/ray3/validate.js | 37 +++++++++++++++++++++++++++++++++++++ test/ray3/index.test.js | 3 ++- test/ray3/validate.test.js | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 lib/ray3/validate.js create mode 100644 test/ray3/validate.test.js diff --git a/lib/ray3/index.js b/lib/ray3/index.js index 2167757..a852479 100644 --- a/lib/ray3/index.js +++ b/lib/ray3/index.js @@ -7,3 +7,4 @@ exports.copy = require('./copy') exports.create = require('./create') +exports.validate = require('./validate') diff --git a/lib/ray3/validate.js b/lib/ray3/validate.js new file mode 100644 index 0000000..e7becf1 --- /dev/null +++ b/lib/ray3/validate.js @@ -0,0 +1,37 @@ +module.exports = (r) => { + // @affineplane.ray3.validate(r) + // + // Check if object is a valid ray3. + // + // Parameter + // r + // an object + // + // Return + // a boolean + // + if (!r) { + return false + } + + if (typeof r.x !== 'number' || Number.isNaN(r.x)) { + return false + } + if (typeof r.y !== 'number' || Number.isNaN(r.y)) { + return false + } + if (typeof r.z !== 'number' || Number.isNaN(r.z)) { + return false + } + if (typeof r.dx !== 'number' || Number.isNaN(r.dx)) { + return false + } + if (typeof r.dy !== 'number' || Number.isNaN(r.dy)) { + return false + } + if (typeof r.dz !== 'number' || Number.isNaN(r.dz)) { + return false + } + + return true +} diff --git a/test/ray3/index.test.js b/test/ray3/index.test.js index baf9ffd..ba0f2a9 100644 --- a/test/ray3/index.test.js +++ b/test/ray3/index.test.js @@ -1,7 +1,8 @@ // A unit for each method. const units = { copy: require('./copy.test'), - create: require('./create.test') + create: require('./create.test'), + validate: require('./validate.test') } module.exports = (t) => { diff --git a/test/ray3/validate.test.js b/test/ray3/validate.test.js new file mode 100644 index 0000000..46f988d --- /dev/null +++ b/test/ray3/validate.test.js @@ -0,0 +1,35 @@ +const ray3 = require('../../lib/ray3') + +module.exports = (ts) => { + ts.test('case: valid ray', (t) => { + t.ok( + ray3.validate({ x: 0, y: 1, z: 2, dx: 0, dy: 1, dz: 2 }), + 'should be valid' + ) + t.ok( + ray3.validate({ x: 0, y: 1, z: 2, dx: 0, dy: 1, dz: 2, r: 3 }), + 'allow additional props' + ) + + t.end() + }) + + ts.test('case: invalid ray', (t) => { + t.notOk(ray3.validate(null), 'detect null') + t.notOk(ray3.validate({}), 'detect empty object') + t.notOk( + ray3.validate({ x: 0, y: 0, z: 0, dx: 0, dy: 0 }), + 'detect missing prop' + ) + t.notOk( + ray3.validate({ x: 0, y: 0, z: 0, dx: 0, dy: 0, dz: '1' }), + 'detect string value' + ) + t.notOk( + ray3.validate({ x: 0, y: 0, z: NaN, dx: 0, dy: 0, dz: 0 }), + 'detect NaN' + ) + + t.end() + }) +} From b8755a847b8af21cd403c88769acc34888e44013 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 13:43:38 +0300 Subject: [PATCH 04/21] improve comment wording --- lib/helm2/almostEqual.js | 2 +- lib/helm3/almostEqual.js | 2 +- lib/vec2/almostEqual.js | 2 +- lib/vec3/almostEqual.js | 2 +- lib/vec4/almostEqual.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/helm2/almostEqual.js b/lib/helm2/almostEqual.js index cbce9f2..09019d5 100644 --- a/lib/helm2/almostEqual.js +++ b/lib/helm2/almostEqual.js @@ -41,6 +41,6 @@ module.exports = function (tr, ts, epsilon) { const dx = Math.abs(tr.x - ts.x) const dy = Math.abs(tr.y - ts.y) - // smaller-or-equal instead of smaller-than to make epsilon=0 work. + // smaller-or-equal instead of smaller-than to include epsilon=0. return da + db + dx + dy <= epsilon } diff --git a/lib/helm3/almostEqual.js b/lib/helm3/almostEqual.js index 6809389..1cd872f 100644 --- a/lib/helm3/almostEqual.js +++ b/lib/helm3/almostEqual.js @@ -32,6 +32,6 @@ module.exports = function (tr, ts, epsilon) { const dy = Math.abs(tr.y - ts.y) const dz = Math.abs(tr.z - ts.z) - // smaller-or-equal instead of smaller-than to make epsilon=0 work. + // smaller-or-equal instead of smaller-than to include epsilon=0. return da + db + dx + dy + dz <= epsilon } diff --git a/lib/vec2/almostEqual.js b/lib/vec2/almostEqual.js index 9062a11..8caae6e 100644 --- a/lib/vec2/almostEqual.js +++ b/lib/vec2/almostEqual.js @@ -24,6 +24,6 @@ module.exports = (v, w, epsilon) => { const dx = Math.abs(v.x - w.x) const dy = Math.abs(v.y - w.y) - // Use "<=" instead of "<" to make epsilon=0 work. + // Use "<=" instead of "<" to include epsilon=0. return dx + dy <= epsilon } diff --git a/lib/vec3/almostEqual.js b/lib/vec3/almostEqual.js index c794bf0..7eef7e6 100644 --- a/lib/vec3/almostEqual.js +++ b/lib/vec3/almostEqual.js @@ -25,6 +25,6 @@ module.exports = (v, w, epsilon) => { const dy = Math.abs(v.y - w.y) const dz = Math.abs(v.z - w.z) - // Use "<=" instead of "<" to make epsilon=0 work. + // Use "<=" instead of "<" to include epsilon=0. return dx + dy + dz <= epsilon } diff --git a/lib/vec4/almostEqual.js b/lib/vec4/almostEqual.js index 37c2ea8..a644c56 100644 --- a/lib/vec4/almostEqual.js +++ b/lib/vec4/almostEqual.js @@ -26,6 +26,6 @@ module.exports = (v, w, epsilon) => { const dz = Math.abs(v.z - w.z) const dw = Math.abs(v.w - w.w) - // Use "<=" instead of "<" to make epsilon=0 work. + // Use "<=" instead of "<" to include epsilon=0. return dx + dy + dz + dw <= epsilon } From 83410608c1c611e8974a979ab3ea0c66a6fd0f16 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 13:43:55 +0300 Subject: [PATCH 05/21] implement ray3.almostEqual --- lib/ray3/almostEqual.js | 34 +++++++++++++++++++++++++++++++ lib/ray3/index.js | 1 + test/ray3/almostEqual.test.js | 38 +++++++++++++++++++++++++++++++++++ test/ray3/index.test.js | 1 + 4 files changed, 74 insertions(+) create mode 100644 lib/ray3/almostEqual.js create mode 100644 test/ray3/almostEqual.test.js diff --git a/lib/ray3/almostEqual.js b/lib/ray3/almostEqual.js new file mode 100644 index 0000000..7d09581 --- /dev/null +++ b/lib/ray3/almostEqual.js @@ -0,0 +1,34 @@ +const EPSILON = require('../epsilon') +const abs = Math.abs + +module.exports = (r, rr, epsilon) => { + // @affineplane.ray3.almostEqual(r, rr[, epsilon]) + // + // Test if rays are almost equal by the margin of epsilon. + // + // Parameters + // r + // a ray3 + // rr + // a ray3 + // epsilon + // Optional number, default to affineplane.epsilon. + // Set to 0 for strict comparison. + // + // Return + // a boolean + // + if (typeof epsilon !== 'number') { + epsilon = EPSILON + } + + const dx = abs(r.x - rr.x) + const dy = abs(r.y - rr.y) + const dz = abs(r.z - rr.z) + const ddx = abs(r.dx - rr.dx) + const ddy = abs(r.dy - rr.dy) + const ddz = abs(r.dz - rr.dz) + + // Use "<=" instead of "<" to include epsilon=0. + return dx + dy + dz + ddx + ddy + ddz <= epsilon +} diff --git a/lib/ray3/index.js b/lib/ray3/index.js index a852479..f25331b 100644 --- a/lib/ray3/index.js +++ b/lib/ray3/index.js @@ -5,6 +5,7 @@ // We represent ray with object { x, y, z, dx, dy, dz } // +exports.almostEqual = require('./almostEqual') exports.copy = require('./copy') exports.create = require('./create') exports.validate = require('./validate') diff --git a/test/ray3/almostEqual.test.js b/test/ray3/almostEqual.test.js new file mode 100644 index 0000000..e4c4f8b --- /dev/null +++ b/test/ray3/almostEqual.test.js @@ -0,0 +1,38 @@ +const affineplane = require('../../index') +const ray3 = affineplane.ray3 + +module.exports = (ts) => { + ts.test('case: almost equal ray', (t) => { + t.ok( + ray3.almostEqual( + { x: 0, y: 0, z: 0, dx: 0, dy: 0, dz: 0 }, + { x: 0, y: 0, z: 0, dx: 0, dy: 0, dz: 0 } + ), + 'zero ray' + ) + t.ok( + ray3.almostEqual( + { x: 1, y: 2, z: 3, dx: 1, dy: 2, dz: 3 }, + { x: 1, y: 2, z: 3, dx: 1, dy: 2, dz: 3, r: 4 } + ), + 'allow additional properties' + ) + t.notOk( + ray3.almostEqual( + { x: 0, y: 0, z: 0, dx: 0, dy: 0, dz: 0 }, + { x: 0, y: 0, z: 0, dx: 0, dy: 0, dz: 1 } + ), + 'different rays' + ) + t.ok( + ray3.almostEqual( + { x: 0, y: 0, z: 0, dx: 0, dy: 0, dz: 0 }, + { x: 0, y: 0, z: 0, dx: 0, dy: 0, dz: 1 }, + 1 + ), + 'different rays but within tolerance' + ) + + t.end() + }) +} diff --git a/test/ray3/index.test.js b/test/ray3/index.test.js index ba0f2a9..a27f097 100644 --- a/test/ray3/index.test.js +++ b/test/ray3/index.test.js @@ -1,5 +1,6 @@ // A unit for each method. const units = { + almostEqual: require('./almostEqual.test'), copy: require('./copy.test'), create: require('./create.test'), validate: require('./validate.test') From 7ec063c0c022a668537be2d67f068b196c2b47c3 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 13:52:02 +0300 Subject: [PATCH 06/21] implement ray3 .getOrigin .getVector --- lib/ray3/getOrigin.js | 19 +++++++++++++++++++ lib/ray3/getVector.js | 18 ++++++++++++++++++ lib/ray3/index.js | 2 ++ test/ray3/getOrigin.test.js | 12 ++++++++++++ test/ray3/getVector.test.js | 12 ++++++++++++ test/ray3/index.test.js | 2 ++ 6 files changed, 65 insertions(+) create mode 100644 lib/ray3/getOrigin.js create mode 100644 lib/ray3/getVector.js create mode 100644 test/ray3/getOrigin.test.js create mode 100644 test/ray3/getVector.test.js diff --git a/lib/ray3/getOrigin.js b/lib/ray3/getOrigin.js new file mode 100644 index 0000000..34f4476 --- /dev/null +++ b/lib/ray3/getOrigin.js @@ -0,0 +1,19 @@ +module.exports = (r) => { + // @affineplane.ray3.getOrigin(r) + // + // Get the origin point of the ray. Note that you can also use the ray + // object itself as a point. + // + // Parameters + // r + // a ray3 + // + // Return + // a point3 + // + return { + x: r.x, + y: r.y, + z: r.z + } +} diff --git a/lib/ray3/getVector.js b/lib/ray3/getVector.js new file mode 100644 index 0000000..ef0af58 --- /dev/null +++ b/lib/ray3/getVector.js @@ -0,0 +1,18 @@ +module.exports = (r) => { + // @affineplane.ray3.getVector(r) + // + // Get the spanning vector of the ray. + // + // Parameters + // r + // a ray3 + // + // Return + // a vec3 + // + return { + x: r.dx, + y: r.dy, + z: r.dz + } +} diff --git a/lib/ray3/index.js b/lib/ray3/index.js index f25331b..dc86ed1 100644 --- a/lib/ray3/index.js +++ b/lib/ray3/index.js @@ -8,4 +8,6 @@ exports.almostEqual = require('./almostEqual') exports.copy = require('./copy') exports.create = require('./create') +exports.getOrigin = require('./getOrigin') +exports.getVector = require('./getVector') exports.validate = require('./validate') diff --git a/test/ray3/getOrigin.test.js b/test/ray3/getOrigin.test.js new file mode 100644 index 0000000..c9c6767 --- /dev/null +++ b/test/ray3/getOrigin.test.js @@ -0,0 +1,12 @@ +const ray3 = require('../../lib/ray3') + +module.exports = (ts) => { + ts.test('case: get ray3 origin', (t) => { + const r = { x: 1, y: 2, z: 3, dx: 4, dy: 5, dz: 6 } + const p = ray3.getOrigin(r) + + t.deepEqual(p, { x: 1, y: 2, z: 3 }, 'should be a point') + + t.end() + }) +} diff --git a/test/ray3/getVector.test.js b/test/ray3/getVector.test.js new file mode 100644 index 0000000..e477b29 --- /dev/null +++ b/test/ray3/getVector.test.js @@ -0,0 +1,12 @@ +const ray3 = require('../../lib/ray3') + +module.exports = (ts) => { + ts.test('case: get ray3 vector', (t) => { + const r = { x: 1, y: 2, z: 3, dx: 4, dy: 5, dz: 6 } + const v = ray3.getVector(r) + + t.deepEqual(v, { x: 4, y: 5, z: 6 }, 'should be a vector') + + t.end() + }) +} diff --git a/test/ray3/index.test.js b/test/ray3/index.test.js index a27f097..8863e0d 100644 --- a/test/ray3/index.test.js +++ b/test/ray3/index.test.js @@ -3,6 +3,8 @@ const units = { almostEqual: require('./almostEqual.test'), copy: require('./copy.test'), create: require('./create.test'), + getOrigin: require('./getOrigin.test'), + getVector: require('./getVector.test'), validate: require('./validate.test') } From 4b7cf9791f272629a11b2c34cc82f694faee03ab Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 13:53:30 +0300 Subject: [PATCH 07/21] lint vec3.invert --- lib/vec3/invert.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vec3/invert.js b/lib/vec3/invert.js index 5d87ed7..c02486f 100644 --- a/lib/vec3/invert.js +++ b/lib/vec3/invert.js @@ -1,4 +1,4 @@ -module.exports = (v, w) => { +module.exports = (v) => { // @affineplane.vec3.invert(v) // @affineplane.vec3.negate // From 53d34025a043716873fdd99170e26b7a8bcd3114 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 13:59:05 +0300 Subject: [PATCH 08/21] implement ray3.invert --- lib/ray3/index.js | 2 ++ lib/ray3/invert.js | 22 ++++++++++++++++++++++ test/ray3/index.test.js | 1 + test/ray3/invert.test.js | 16 ++++++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 lib/ray3/invert.js create mode 100644 test/ray3/invert.test.js diff --git a/lib/ray3/index.js b/lib/ray3/index.js index dc86ed1..445e2c2 100644 --- a/lib/ray3/index.js +++ b/lib/ray3/index.js @@ -10,4 +10,6 @@ exports.copy = require('./copy') exports.create = require('./create') exports.getOrigin = require('./getOrigin') exports.getVector = require('./getVector') +exports.invert = require('./invert') +exports.negate = exports.invert exports.validate = require('./validate') diff --git a/lib/ray3/invert.js b/lib/ray3/invert.js new file mode 100644 index 0000000..1439838 --- /dev/null +++ b/lib/ray3/invert.js @@ -0,0 +1,22 @@ +module.exports = (r) => { + // @affineplane.ray3.invert(r) + // @affineplane.ray3.negate + // + // Get a ray with the same magnitude but to opposite direction. + // + // Parameters: + // r + // a ray3 + // + // Return + // a ray3 + // + return { + x: r.x, + y: r.y, + z: r.z, + dx: -r.dx, + dy: -r.dy, + dz: -r.dz + } +} diff --git a/test/ray3/index.test.js b/test/ray3/index.test.js index 8863e0d..422d47f 100644 --- a/test/ray3/index.test.js +++ b/test/ray3/index.test.js @@ -5,6 +5,7 @@ const units = { create: require('./create.test'), getOrigin: require('./getOrigin.test'), getVector: require('./getVector.test'), + invert: require('./invert.test'), validate: require('./validate.test') } diff --git a/test/ray3/invert.test.js b/test/ray3/invert.test.js new file mode 100644 index 0000000..ac48055 --- /dev/null +++ b/test/ray3/invert.test.js @@ -0,0 +1,16 @@ +const ray3 = require('../../lib/ray3') + +module.exports = (ts) => { + ts.test('case: negate ray', (t) => { + const r = { x: 1, y: 2, z: 3, dx: 4, dy: 5, dz: 6 } + t.deepEqual( + ray3.invert(r), + { x: 1, y: 2, z: 3, dx: -4, dy: -5, dz: -6 }, + 'should negate only span' + ) + + t.deepEqual(ray3.negate(r), ray3.invert(r), 'should have alias') + + t.end() + }) +} From 402eb164b4ac055a864b84af0ea42f6ca4aaa54d Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 14:34:18 +0300 Subject: [PATCH 09/21] detect zero ray3 span in validation --- lib/ray3/validate.js | 22 +++++++++++++++------- test/ray3/validate.test.js | 8 ++++++-- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/ray3/validate.js b/lib/ray3/validate.js index e7becf1..1320457 100644 --- a/lib/ray3/validate.js +++ b/lib/ray3/validate.js @@ -1,7 +1,12 @@ +const EPSILON = require('../epsilon') +const abs = Math.abs +const isNaN = Number.isNaN + module.exports = (r) => { // @affineplane.ray3.validate(r) // - // Check if object is a valid ray3. + // Check if object is a valid ray3. Valid ray has numeric properties + // x, y, z, dx, dy, dz and has non-zero spanning vector. // // Parameter // r @@ -14,22 +19,25 @@ module.exports = (r) => { return false } - if (typeof r.x !== 'number' || Number.isNaN(r.x)) { + if (typeof r.x !== 'number' || isNaN(r.x)) { + return false + } + if (typeof r.y !== 'number' || isNaN(r.y)) { return false } - if (typeof r.y !== 'number' || Number.isNaN(r.y)) { + if (typeof r.z !== 'number' || isNaN(r.z)) { return false } - if (typeof r.z !== 'number' || Number.isNaN(r.z)) { + if (typeof r.dx !== 'number' || isNaN(r.dx)) { return false } - if (typeof r.dx !== 'number' || Number.isNaN(r.dx)) { + if (typeof r.dy !== 'number' || isNaN(r.dy)) { return false } - if (typeof r.dy !== 'number' || Number.isNaN(r.dy)) { + if (typeof r.dz !== 'number' || isNaN(r.dz)) { return false } - if (typeof r.dz !== 'number' || Number.isNaN(r.dz)) { + if (abs(r.dx) + abs(r.dy) + abs(r.dz) < EPSILON) { return false } diff --git a/test/ray3/validate.test.js b/test/ray3/validate.test.js index 46f988d..4b30462 100644 --- a/test/ray3/validate.test.js +++ b/test/ray3/validate.test.js @@ -18,7 +18,7 @@ module.exports = (ts) => { t.notOk(ray3.validate(null), 'detect null') t.notOk(ray3.validate({}), 'detect empty object') t.notOk( - ray3.validate({ x: 0, y: 0, z: 0, dx: 0, dy: 0 }), + ray3.validate({ x: 0, y: 0, z: 0, dx: 1, dy: 0 }), 'detect missing prop' ) t.notOk( @@ -26,9 +26,13 @@ module.exports = (ts) => { 'detect string value' ) t.notOk( - ray3.validate({ x: 0, y: 0, z: NaN, dx: 0, dy: 0, dz: 0 }), + ray3.validate({ x: 0, y: 0, z: NaN, dx: 1, dy: 0, dz: 0 }), 'detect NaN' ) + t.notOk( + ray3.validate({ x: 1, y: 1, z: 1, dx: 0, dy: 0, dz: 0 }), + 'detect singular' + ) t.end() }) From 391d490a46b2feacd21cf65a00ad01729945cb55 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 14:52:57 +0300 Subject: [PATCH 10/21] implement ray3 .transitFrom .transitTo --- lib/ray3/index.js | 14 ++++++++++ lib/ray3/transitFrom.js | 29 ++++++++++++++++++++ lib/ray3/transitTo.js | 22 +++++++++++++++ test/ray3/index.test.js | 2 ++ test/ray3/transitFrom.test.js | 50 +++++++++++++++++++++++++++++++++++ test/ray3/transitTo.test.js | 44 ++++++++++++++++++++++++++++++ 6 files changed, 161 insertions(+) create mode 100644 lib/ray3/transitFrom.js create mode 100644 lib/ray3/transitTo.js create mode 100644 test/ray3/transitFrom.test.js create mode 100644 test/ray3/transitTo.test.js diff --git a/lib/ray3/index.js b/lib/ray3/index.js index 445e2c2..1f9a545 100644 --- a/lib/ray3/index.js +++ b/lib/ray3/index.js @@ -5,6 +5,18 @@ // We represent ray with object { x, y, z, dx, dy, dz } // +// @affineplane.ray3.ZERO +// +// Invalid ray with origin at zero and zero spanning vector. +// +exports.ZERO = { x: 0, y: 0, z: 0, dx: 0, dy: 0, dz: 0 } + +// @affineplane.ray3.DEFAULT +// +// Default ray that has origin at zero and unit span along positive x-axis. +// +exports.DEFAULT = { x: 0, y: 0, z: 0, dx: 1, dy: 0, dz: 0 } + exports.almostEqual = require('./almostEqual') exports.copy = require('./copy') exports.create = require('./create') @@ -12,4 +24,6 @@ exports.getOrigin = require('./getOrigin') exports.getVector = require('./getVector') exports.invert = require('./invert') exports.negate = exports.invert +exports.transitFrom = require('./transitFrom') +exports.transitTo = require('./transitTo') exports.validate = require('./validate') diff --git a/lib/ray3/transitFrom.js b/lib/ray3/transitFrom.js new file mode 100644 index 0000000..0707c74 --- /dev/null +++ b/lib/ray3/transitFrom.js @@ -0,0 +1,29 @@ +module.exports = (ray, basis) => { + // @affineplane.ray3.transitFrom(ray, basis) + // + // Represent the ray in the reference basis without losing information. + // + // Parameters: + // ray + // a ray3, represented in the given basis. + // basis + // a plane3, represented in the reference basis. + // + // Return: + // a ray3, represented in the reference basis. + // + + // The basis is a mapping from itself to the reference basis. + const ba = basis.a + const bb = basis.b + const bm = Math.sqrt(ba * ba + bb * bb) + + return { + x: ba * ray.x - bb * ray.y + basis.x, + y: bb * ray.x + ba * ray.y + basis.y, + z: bm * ray.z + basis.z, + dx: ba * ray.dx - bb * ray.dy, + dy: bb * ray.dx + ba * ray.dy, + dz: bm * ray.dz + } +} diff --git a/lib/ray3/transitTo.js b/lib/ray3/transitTo.js new file mode 100644 index 0000000..28d12ee --- /dev/null +++ b/lib/ray3/transitTo.js @@ -0,0 +1,22 @@ +const invert = require('../helm3/invert') +const transitFrom = require('./transitFrom') + +module.exports = (ray, basis) => { + // @affineplane.ray3.transitTo(ray, basis) + // + // Represent the ray in another basis + // without losing information. + // + // Parameters: + // ray + // a ray3, represented in the reference basis. + // basis + // a plane3, represented in the reference basis. + // + // Return: + // a ray3, represented in the given basis. + // + + const ibasis = invert(basis) + return transitFrom(ray, ibasis) +} diff --git a/test/ray3/index.test.js b/test/ray3/index.test.js index 422d47f..ca956ab 100644 --- a/test/ray3/index.test.js +++ b/test/ray3/index.test.js @@ -6,6 +6,8 @@ const units = { getOrigin: require('./getOrigin.test'), getVector: require('./getVector.test'), invert: require('./invert.test'), + transitFrom: require('./transitFrom.test'), + transitTo: require('./transitTo.test'), validate: require('./validate.test') } diff --git a/test/ray3/transitFrom.test.js b/test/ray3/transitFrom.test.js new file mode 100644 index 0000000..327422c --- /dev/null +++ b/test/ray3/transitFrom.test.js @@ -0,0 +1,50 @@ +const ray3 = require('../../lib/ray3') + +module.exports = (ts) => { + ts.test('case: trivial', (t) => { + const basis = { a: 1, b: 0, x: 0, y: 0, z: 0 } + + t.deepEqual( + ray3.transitFrom(ray3.ZERO, basis), + ray3.ZERO, + 'zero ray stays zero' + ) + t.deepEqual( + ray3.transitFrom({ x: 1, y: 1, z: 1, dx: 1, dy: 1, dz: 1 }, basis), + { x: 1, y: 1, z: 1, dx: 1, dy: 1, dz: 1 }, + 'ones ray' + ) + + t.end() + }) + + ts.test('case: basic', (t) => { + let ray, basis + + ray = { x: 1, y: 1, z: 1, dx: 1, dy: 1, dz: 1 } + basis = { a: 1, b: 0, x: 1, y: 1, z: 1 } + t.deepEqual( + ray3.transitFrom(ray, basis), + { x: 2, y: 2, z: 2, dx: 1, dy: 1, dz: 1 }, + 'translation affects only origin' + ) + + ray = { x: 1, y: 1, z: 1, dx: 1, dy: 1, dz: 1 } + basis = { a: 2, b: 0, x: 1, y: 1, z: 1 } + t.deepEqual( + ray3.transitFrom(ray, basis), + { x: 3, y: 3, z: 3, dx: 2, dy: 2, dz: 2 }, + 'scaling affects both' + ) + + ray = { x: 1, y: 1, z: 1, dx: 1, dy: 1, dz: 1 } + basis = { a: 0, b: 2, x: 1, y: 1, z: 1 } + t.deepEqual( + ray3.transitFrom(ray, basis), + { x: -1, y: 3, z: 3, dx: -2, dy: 2, dz: 2 }, + 'rotation affects both' + ) + + t.end() + }) +} diff --git a/test/ray3/transitTo.test.js b/test/ray3/transitTo.test.js new file mode 100644 index 0000000..d9e1cad --- /dev/null +++ b/test/ray3/transitTo.test.js @@ -0,0 +1,44 @@ +const ray3 = require('../../lib/ray3') + +module.exports = (ts) => { + ts.test('case: trivial trasitTo', (t) => { + const basis = { a: 1, b: 0, x: 0, y: 0, z: 0 } + + t.deepEqual( + ray3.transitTo(ray3.ZERO, basis), + ray3.ZERO, + 'identity basis should not affect zero ray' + ) + + const ray = { x: 1, y: 1, z: 1, dx: 1, dy: 1, dz: 1 } + t.deepEqual( + ray3.transitTo(ray, basis), + ray, + 'identity basis should not affect ray' + ) + + t.end() + }) + + ts.test('case: basic transitTo', (t) => { + let ray, basis + + ray = { x: 1, y: 1, z: 1, dx: 1, dy: 1, dz: 1 } + basis = { a: 1, b: 0, x: 1, y: 1, z: 1 } + t.deepEqual( + ray3.transitTo(ray, basis), + { x: 0, y: 0, z: 0, dx: 1, dy: 1, dz: 1 }, + 'translation does not affect ray direction' + ) + + ray = { x: 3, y: 3, z: 3, dx: 2, dy: 2, dz: 2 } + basis = { a: 2, b: 0, x: 1, y: 1, z: 1 } + t.deepEqual( + ray3.transitTo(ray, basis), + { x: 1, y: 1, z: 1, dx: 1, dy: 1, dz: 1 }, + 'scaling preserves ray direction' + ) + + t.end() + }) +} From b05726885f7879c3ac3ec152d47831370cbc9a2b Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 15:28:12 +0300 Subject: [PATCH 11/21] implement ray3 .homothety --- lib/ray3/homothety.js | 37 +++++++++++++++++++++++++++++++++++++ lib/ray3/index.js | 1 + test/ray3/homothety.test.js | 25 +++++++++++++++++++++++++ test/ray3/index.test.js | 1 + 4 files changed, 64 insertions(+) create mode 100644 lib/ray3/homothety.js create mode 100644 test/ray3/homothety.test.js diff --git a/lib/ray3/homothety.js b/lib/ray3/homothety.js new file mode 100644 index 0000000..5d47601 --- /dev/null +++ b/lib/ray3/homothety.js @@ -0,0 +1,37 @@ +const EPSILON = require('../epsilon') + +module.exports = (ray, origin, ratio) => { + // @affineplane.ray3.homothety(ray, origin, ratio) + // + // Perform a homothety about the origin for the ray. + // + // Parameters: + // ray + // a ray3 + // origin + // a point3 + // ratio + // a number, the scaling ratio + // + // Return: + // a ray3 + // + + const dx = (ray.x - origin.x) + const dy = (ray.y - origin.y) + const dz = (ray.z - origin.z) + + // If the ray origin does not move, then the span should not scale. + if (dx * dx + dy * dy + dz * dz < EPSILON) { + return ray + } + + return { + x: origin.x + dx * ratio, + y: origin.y + dy * ratio, + z: origin.z + dz * ratio, + dx: ray.dx * ratio, + dy: ray.dy * ratio, + dz: ray.dz * ratio + } +} diff --git a/lib/ray3/index.js b/lib/ray3/index.js index 1f9a545..acddf8e 100644 --- a/lib/ray3/index.js +++ b/lib/ray3/index.js @@ -22,6 +22,7 @@ exports.copy = require('./copy') exports.create = require('./create') exports.getOrigin = require('./getOrigin') exports.getVector = require('./getVector') +exports.homothety = require('./homothety') exports.invert = require('./invert') exports.negate = exports.invert exports.transitFrom = require('./transitFrom') diff --git a/test/ray3/homothety.test.js b/test/ray3/homothety.test.js new file mode 100644 index 0000000..815c181 --- /dev/null +++ b/test/ray3/homothety.test.js @@ -0,0 +1,25 @@ +const ray3 = require('../../lib/ray3') + +module.exports = (ts) => { + ts.test('case: ray homothety', (t) => { + let ray, center + + ray = { x: 1, y: 1, z: 1, dx: 1, dy: 0, dz: 0 } + center = { x: 1, y: 1, z: 1 } + t.deepEqual( + ray3.homothety(ray, center, 3), + { x: 1, y: 1, z: 1, dx: 1, dy: 0, dz: 0 }, + 'identical origin should not scale vector' + ) + + ray = { x: 1, y: 0, z: 0, dx: 1, dy: 0, dz: 0 } + center = { x: 0, y: 0, z: 0 } + t.deepEqual( + ray3.homothety(ray, center, 2), + { x: 2, y: 0, z: 0, dx: 2, dy: 0, dz: 0 }, + 'double about zero' + ) + + t.end() + }) +} diff --git a/test/ray3/index.test.js b/test/ray3/index.test.js index ca956ab..d9a47ad 100644 --- a/test/ray3/index.test.js +++ b/test/ray3/index.test.js @@ -5,6 +5,7 @@ const units = { create: require('./create.test'), getOrigin: require('./getOrigin.test'), getVector: require('./getVector.test'), + homothety: require('./homothety.test'), invert: require('./invert.test'), transitFrom: require('./transitFrom.test'), transitTo: require('./transitTo.test'), From 72e2af74807e1b6aca00e1dd0741416c901d2a5a Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 17:47:41 +0300 Subject: [PATCH 12/21] implement point3.distanceToLine --- lib/point3/distanceToLine.js | 57 ++++++++++++++++++++++++++++++ lib/point3/index.js | 1 + test/point3/distanceToLine.test.js | 41 +++++++++++++++++++++ test/point3/index.test.js | 1 + 4 files changed, 100 insertions(+) create mode 100644 lib/point3/distanceToLine.js create mode 100644 test/point3/distanceToLine.test.js diff --git a/lib/point3/distanceToLine.js b/lib/point3/distanceToLine.js new file mode 100644 index 0000000..5afcc40 --- /dev/null +++ b/lib/point3/distanceToLine.js @@ -0,0 +1,57 @@ +const epsilon = require('../epsilon') + +module.exports = (p, line) => { + // @affineplane.point3.distanceToLine(p, line) + // + // Compute the smallest euclidean distance between a point and a line. + // See also affineplane.point3.distanceToRay. + // + // Parameters + // p + // a point3 + // line + // a line3 + // + // Return + // a number, a scalar1, a dist3, a distance + // + + // See the commented lines for algebraic solution with function calls. + // After the commented lines, we have opened the function calls + // and removed unnecessary square roots and vectorization. + + // // Get vector from line origin to the point + // const av = pointDiff(line.origin, p) + // // Project the vector to the line + // const bv = vectorProject(av, line.span) + // // Magnitudes + // const am = vectorMagn(av) + // const bm = vectorMagn(bv) + // // Apply pythagoras to find the distance + // return Math.sqrt(am * am - bm * bm) + + // Extract the line + const lp = line.origin + const xs = line.span.x + const ys = line.span.y + const zs = line.span.z + // Get vector va from line origin to the given point. + const xa = p.x - lp.x + const ya = p.y - lp.y + const za = p.z - lp.z + // Get va's magnitude squared + const ma2 = xa * xa + ya * ya + za * za + // Project va to the line to get vector vb and get its magnitude squared. + const adots = xa * xs + ya * ys + za * zs + const sdots = xs * xs + ys * ys + zs * zs + let mb2 + if (sdots < epsilon) { + // The line has zero span. + // Assume vectors perpendicular, thus vb has zero magnitude. + mb2 = 0 + } else { + mb2 = adots * adots / sdots + } + // Apply pythagoras to find the distance + return Math.sqrt(ma2 - mb2) +} diff --git a/lib/point3/index.js b/lib/point3/index.js index 2bba710..6133d80 100644 --- a/lib/point3/index.js +++ b/lib/point3/index.js @@ -17,6 +17,7 @@ exports.diff = exports.delta exports.difference = exports.delta exports.direction = require('./direction') exports.distance = require('./distance') +exports.distanceToLine = require('./distanceToLine') exports.distanceToPlane = require('./distanceToPlane') exports.equal = require('./equal') exports.equals = exports.equal diff --git a/test/point3/distanceToLine.test.js b/test/point3/distanceToLine.test.js new file mode 100644 index 0000000..b8a0fd4 --- /dev/null +++ b/test/point3/distanceToLine.test.js @@ -0,0 +1,41 @@ +const point3 = require('../../lib/point3') + +module.exports = (ts) => { + ts.test('case: distance to line', (t) => { + let point, line + + point = { x: 0, y: 0, z: 0 } + line = { origin: { x: 0, y: 0, z: 0 }, span: { x: 1, y: 0, z: 0 } } + t.equal( + point3.distanceToLine(point, line), + 0, + 'trivial zero distance' + ) + + point = { x: 0, y: 1, z: 0 } + line = { origin: { x: 0, y: 0, z: 0 }, span: { x: 1, y: 0, z: 0 } } + t.equal( + point3.distanceToLine(point, line), + 1, + 'simple distance along y' + ) + + point = { x: 5, y: 5, z: 0 } + line = { origin: { x: 10, y: 0, z: 0 }, span: { x: 0, y: 2, z: 0 } } + t.almostEqual( + point3.distanceToLine(point, line), + 5, + 'distance along x' + ) + + point = { x: 5, y: 5, z: 0 } + line = { origin: { x: 0, y: 0, z: 2 }, span: { x: 2, y: 2, z: 0 } } + t.almostEqual( + point3.distanceToLine(point, line), + 2, + 'distance along z' + ) + + t.end() + }) +} diff --git a/test/point3/index.test.js b/test/point3/index.test.js index b3ddf9d..87190a4 100644 --- a/test/point3/index.test.js +++ b/test/point3/index.test.js @@ -6,6 +6,7 @@ const units = { difference: require('./difference.test'), direction: require('./direction.test'), distance: require('./distance.test'), + distanceToLine: require('./distanceToLine.test'), distanceToPlane: require('./distanceToPlane.test'), equal: require('./equal.test'), fromArray: require('./fromArray.test'), From 57990bca5a04f98b784d526262fb19eec27a5df9 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 18:08:44 +0300 Subject: [PATCH 13/21] implement point3.distanceToRay --- lib/point3/distanceToLine.js | 1 + lib/point3/distanceToRay.js | 52 ++++++++++++++++++++++++++++ lib/point3/index.js | 1 + test/point3/distanceToRay.test.js | 57 +++++++++++++++++++++++++++++++ test/point3/index.test.js | 1 + 5 files changed, 112 insertions(+) create mode 100644 lib/point3/distanceToRay.js create mode 100644 test/point3/distanceToRay.test.js diff --git a/lib/point3/distanceToLine.js b/lib/point3/distanceToLine.js index 5afcc40..5f7590e 100644 --- a/lib/point3/distanceToLine.js +++ b/lib/point3/distanceToLine.js @@ -19,6 +19,7 @@ module.exports = (p, line) => { // See the commented lines for algebraic solution with function calls. // After the commented lines, we have opened the function calls // and removed unnecessary square roots and vectorization. + // Note that distanceToRay uses similar approach. // // Get vector from line origin to the point // const av = pointDiff(line.origin, p) diff --git a/lib/point3/distanceToRay.js b/lib/point3/distanceToRay.js new file mode 100644 index 0000000..e322663 --- /dev/null +++ b/lib/point3/distanceToRay.js @@ -0,0 +1,52 @@ +const epsilon = require('../epsilon') + +module.exports = (p, ray) => { + // @affineplane.point3.distanceToRay(p, ray) + // + // Compute the smallest euclidean distance between a point and a ray. + // For half of the space, the closest point on the ray is the ray origin + // and therefore, for points inside that half, the computation reduces + // to the euclidean distance to the ray origin. + // See also affineplane.point3.distanceToLine. + // + // Parameters + // p + // a point3 + // ray + // a ray3 + // + // Return + // a number, a scalar1, a dist3, a distance + // + + // See distanceToLine for math derivation. + + // Extract the ray spanning vector + const xs = ray.dx + const ys = ray.dy + const zs = ray.dz + // Get vector va from ray origin to the given point. + const xa = p.x - ray.x + const ya = p.y - ray.y + const za = p.z - ray.z + // Get va's magnitude squared + const ma2 = xa * xa + ya * ya + za * za + // Project va to the line to get vector vb and get its magnitude squared. + const adots = xa * xs + ya * ys + za * zs + // Determine if the point is on or behind the ray origin plane + if (adots <= 0) { + // Reduce to euclidean distance to the ray origin. + return Math.sqrt(ma2) + } + const sdots = xs * xs + ys * ys + zs * zs + let mb2 + if (sdots < epsilon) { + // The line has zero span. + // Assume vectors perpendicular, thus vb has zero magnitude. + mb2 = 0 + } else { + mb2 = adots * adots / sdots + } + // Apply pythagoras to find the distance + return Math.sqrt(ma2 - mb2) +} diff --git a/lib/point3/index.js b/lib/point3/index.js index 6133d80..9b7c614 100644 --- a/lib/point3/index.js +++ b/lib/point3/index.js @@ -19,6 +19,7 @@ exports.direction = require('./direction') exports.distance = require('./distance') exports.distanceToLine = require('./distanceToLine') exports.distanceToPlane = require('./distanceToPlane') +exports.distanceToRay = require('./distanceToRay') exports.equal = require('./equal') exports.equals = exports.equal exports.fromArray = require('./fromArray') diff --git a/test/point3/distanceToRay.test.js b/test/point3/distanceToRay.test.js new file mode 100644 index 0000000..5b70aa2 --- /dev/null +++ b/test/point3/distanceToRay.test.js @@ -0,0 +1,57 @@ +const point3 = require('../../lib/point3') + +module.exports = (ts) => { + ts.test('case: distance to ray', (t) => { + let point, ray + + point = { x: 0, y: 0, z: 0 } + ray = { x: 0, y: 0, z: 0, dx: 1, dy: 0, dz: 0 } + t.equal( + point3.distanceToRay(point, ray), + 0, + 'trivial zero distance' + ) + + point = { x: -5, y: 0, z: 0 } + ray = { x: 0, y: 0, z: 0, dx: 1, dy: 0, dz: 0 } + t.equal( + point3.distanceToRay(point, ray), + 5, + 'point on the line but behind ray plane' + ) + + point = { x: 0, y: 1, z: 0 } + ray = { x: 0, y: 0, z: 0, dx: 1, dy: 0, dz: 0 } + t.equal( + point3.distanceToRay(point, ray), + 1, + 'simple distance along y' + ) + + point = { x: 0, y: 0, z: 0 } + ray = { x: 1, y: 2, z: 2, dx: 0, dy: 0, dz: 5 } + t.equal( + point3.distanceToRay(point, ray), + 3, + 'simple distance behind ray plane' + ) + + point = { x: 5, y: 5, z: 0 } + ray = { x: 10, y: 0, z: 0, dx: 0, dy: 2, dz: 0 } + t.almostEqual( + point3.distanceToRay(point, ray), + 5, + 'distance along x' + ) + + point = { x: 5, y: 5, z: 0 } + ray = { x: 0, y: 0, z: 2, dx: 2, dy: 2, dz: 0 } + t.almostEqual( + point3.distanceToRay(point, ray), + 2, + 'distance along z' + ) + + t.end() + }) +} diff --git a/test/point3/index.test.js b/test/point3/index.test.js index 87190a4..97582d6 100644 --- a/test/point3/index.test.js +++ b/test/point3/index.test.js @@ -8,6 +8,7 @@ const units = { distance: require('./distance.test'), distanceToLine: require('./distanceToLine.test'), distanceToPlane: require('./distanceToPlane.test'), + distanceToRay: require('./distanceToRay.test'), equal: require('./equal.test'), fromArray: require('./fromArray.test'), homothety: require('./homothety.test'), From fa35db3bb80be97b2a3f5f85b73d2ce70e78cdf8 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 18:29:50 +0300 Subject: [PATCH 14/21] correct point3.offset docs --- lib/point3/offset.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/point3/offset.js b/lib/point3/offset.js index 70a9869..ec3a383 100644 --- a/lib/point3/offset.js +++ b/lib/point3/offset.js @@ -5,7 +5,7 @@ module.exports = (p, dx, dy, dz) => { // // Parameters: // p - // a point2 + // a point3 // dx // a number, the offset along x-axis // dy From 558870973910d783464788398b74405403bc5708 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 18:33:18 +0300 Subject: [PATCH 15/21] implement ray3.offset --- lib/ray3/index.js | 1 + lib/ray3/offset.js | 27 +++++++++++++++++++++++++++ test/ray3/index.test.js | 1 + test/ray3/offset.test.js | 12 ++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 lib/ray3/offset.js create mode 100644 test/ray3/offset.test.js diff --git a/lib/ray3/index.js b/lib/ray3/index.js index acddf8e..7048ffd 100644 --- a/lib/ray3/index.js +++ b/lib/ray3/index.js @@ -25,6 +25,7 @@ exports.getVector = require('./getVector') exports.homothety = require('./homothety') exports.invert = require('./invert') exports.negate = exports.invert +exports.offset = require('./offset') exports.transitFrom = require('./transitFrom') exports.transitTo = require('./transitTo') exports.validate = require('./validate') diff --git a/lib/ray3/offset.js b/lib/ray3/offset.js new file mode 100644 index 0000000..4458b4e --- /dev/null +++ b/lib/ray3/offset.js @@ -0,0 +1,27 @@ +module.exports = (ray, dx, dy, dz) => { + // @affineplane.ray3.offset(ray, dx, dy, dz) + // + // Offset a ray origin by scalars dx, dy, dz. + // + // Parameters: + // ray + // a ray3 + // dx + // a number, the offset along x-axis + // dy + // a number, the offset along y-axis + // dz + // a number, the offset along z-axis + // + // Return + // a ray3 + // + return { + x: ray.x + dx, + y: ray.y + dy, + z: ray.z + dz, + dx: ray.dx, + dy: ray.dy, + dz: ray.dz + } +} diff --git a/test/ray3/index.test.js b/test/ray3/index.test.js index d9a47ad..39d463c 100644 --- a/test/ray3/index.test.js +++ b/test/ray3/index.test.js @@ -7,6 +7,7 @@ const units = { getVector: require('./getVector.test'), homothety: require('./homothety.test'), invert: require('./invert.test'), + offset: require('./offset.test'), transitFrom: require('./transitFrom.test'), transitTo: require('./transitTo.test'), validate: require('./validate.test') diff --git a/test/ray3/offset.test.js b/test/ray3/offset.test.js new file mode 100644 index 0000000..4d35ef1 --- /dev/null +++ b/test/ray3/offset.test.js @@ -0,0 +1,12 @@ +const ray3 = require('../../lib/ray3') + +module.exports = (ts) => { + ts.test('case: ray3 offset', (t) => { + t.deepEqual(ray3.offset( + { x: 0, y: 0, z: 0, dx: 0, dy: 0, dz: 1 }, + 1, 1, 1 + ), { x: 1, y: 1, z: 1, dx: 0, dy: 0, dz: 1 }) + + t.end() + }) +} From be00d49c80dc574094162f1e26ffa81ff60a11b7 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 18:41:18 +0300 Subject: [PATCH 16/21] implement ray3.at --- lib/ray3/at.js | 29 +++++++++++++++++++++++++++++ lib/ray3/index.js | 1 + test/ray3/at.test.js | 21 +++++++++++++++++++++ test/ray3/index.test.js | 1 + 4 files changed, 52 insertions(+) create mode 100644 lib/ray3/at.js create mode 100644 test/ray3/at.test.js diff --git a/lib/ray3/at.js b/lib/ray3/at.js new file mode 100644 index 0000000..a813c63 --- /dev/null +++ b/lib/ray3/at.js @@ -0,0 +1,29 @@ +module.exports = (ray, c) => { + // @affineplane.ray3.at(line, c) + // + // Get a point on the ray at position c from the ray origin. + // For example, c=2 gives a point two ray spans away from the origin. + // Negative positions default to the ray origin. + // + // Parameters: + // ray + // a ray3 + // c + // a number + // + // Return: + // a point3 + // + if (c <= 0) { + return { + x: ray.x, + y: ray.y, + z: ray.z + } + } + return { + x: ray.x + c * ray.dx, + y: ray.y + c * ray.dy, + z: ray.z + c * ray.dz + } +} diff --git a/lib/ray3/index.js b/lib/ray3/index.js index 7048ffd..0c72ec3 100644 --- a/lib/ray3/index.js +++ b/lib/ray3/index.js @@ -18,6 +18,7 @@ exports.ZERO = { x: 0, y: 0, z: 0, dx: 0, dy: 0, dz: 0 } exports.DEFAULT = { x: 0, y: 0, z: 0, dx: 1, dy: 0, dz: 0 } exports.almostEqual = require('./almostEqual') +exports.at = require('./at') exports.copy = require('./copy') exports.create = require('./create') exports.getOrigin = require('./getOrigin') diff --git a/test/ray3/at.test.js b/test/ray3/at.test.js new file mode 100644 index 0000000..12afa30 --- /dev/null +++ b/test/ray3/at.test.js @@ -0,0 +1,21 @@ +const ray3 = require('../../lib/ray3') + +module.exports = (ts) => { + ts.test('case: basic ray at', (t) => { + const ray = { x: 1, y: 1, z: 1, dx: 1, dy: 1, dz: -1 } + + t.deepEqual( + ray3.at(ray, 3), + { x: 4, y: 4, z: -2 }, + 'three spans away from origin' + ) + + t.deepEqual( + ray3.at(ray, -3), + { x: 1, y: 1, z: 1 }, + 'negative position at origin' + ) + + t.end() + }) +} diff --git a/test/ray3/index.test.js b/test/ray3/index.test.js index 39d463c..82598e3 100644 --- a/test/ray3/index.test.js +++ b/test/ray3/index.test.js @@ -1,6 +1,7 @@ // A unit for each method. const units = { almostEqual: require('./almostEqual.test'), + at: require('./at.test'), copy: require('./copy.test'), create: require('./create.test'), getOrigin: require('./getOrigin.test'), From 5ac7556edfbd2a9335fd1a7db5ab2bd835f68c9a Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 18:54:33 +0300 Subject: [PATCH 17/21] document UNIT and ZERO constants in various geometries --- lib/box2/index.js | 10 ++++++++++ lib/box3/index.js | 10 ++++++++++ lib/circle3/index.js | 10 ++++++++++ lib/point2/index.js | 4 ++++ lib/point3/index.js | 4 ++++ lib/sphere2/index.js | 10 ++++++++++ lib/sphere3/index.js | 10 ++++++++++ lib/vec2/index.js | 5 +++++ lib/vec3/index.js | 5 +++++ lib/vec4/index.js | 5 +++++ 10 files changed, 73 insertions(+) diff --git a/lib/box2/index.js b/lib/box2/index.js index a05f6cb..e2f769b 100644 --- a/lib/box2/index.js +++ b/lib/box2/index.js @@ -9,7 +9,17 @@ // - `x,y` provide origin position in the reference basis. // - `w,h` provide box size on the reference basis. // + +// @affineplane.box2.UNIT +// +// An origin-centered box with unit width and height. +// exports.UNIT = { a: 1, b: 0, x: -0.5, y: -0.5, w: 1, h: 1 } + +// @affineplane.box2.ZERO +// +// A zero-size box. +// exports.ZERO = { a: 1, b: 0, x: 0, y: 0, w: 0, h: 0 } exports.almostEqual = require('./almostEqual') diff --git a/lib/box3/index.js b/lib/box3/index.js index 1b151cd..a5bf0e7 100644 --- a/lib/box3/index.js +++ b/lib/box3/index.js @@ -9,7 +9,17 @@ // - `x,y,z` provide origin position in the reference basis. // - `w,h,d` provide box size on the reference basis. // + +// @affineplane.box3.UNIT +// +// An origin-centered box with unit width, height, and depth. +// exports.UNIT = { a: 1, b: 0, x: -0.5, y: -0.5, z: -0.5, w: 1, h: 1, d: 1 } + +// @affineplane.box3.ZERO +// +// A zero-size box. +// exports.ZERO = { a: 1, b: 0, x: 0, y: 0, z: 0, w: 0, h: 0, d: 0 } exports.almostEqual = require('./almostEqual') diff --git a/lib/circle3/index.js b/lib/circle3/index.js index b417bab..715ce64 100644 --- a/lib/circle3/index.js +++ b/lib/circle3/index.js @@ -4,7 +4,17 @@ // // Represented with an object `{ x, y, z, r }` for the origin and the radius. // + +// @affineplane.circle3.UNIT +// +// The unit circle, radius=1. +// exports.UNIT = { x: 0, y: 0, z: 0, r: 1 } + +// @affineplane.circle3.ZERO +// +// A zero-radius circle. +// exports.ZERO = { x: 0, y: 0, z: 0, r: 0 } exports.almostEqual = require('./almostEqual') diff --git a/lib/point2/index.js b/lib/point2/index.js index 21e7c94..fb6582a 100644 --- a/lib/point2/index.js +++ b/lib/point2/index.js @@ -8,6 +8,10 @@ // ![A point](geometry_point.png) // +// @affineplane.point2.ZERO +// +// The zero point +// exports.ZERO = { x: 0, y: 0 } // exports.add diff --git a/lib/point3/index.js b/lib/point3/index.js index 9b7c614..c18bb43 100644 --- a/lib/point3/index.js +++ b/lib/point3/index.js @@ -5,6 +5,10 @@ // translation of the plane on which they are represented. // +// @affineplane.point3.ZERO +// +// The zero point +// exports.ZERO = { x: 0, y: 0, z: 0 } exports.almostEqual = require('./almostEqual') diff --git a/lib/sphere2/index.js b/lib/sphere2/index.js index 40cd4d6..73e7310 100644 --- a/lib/sphere2/index.js +++ b/lib/sphere2/index.js @@ -5,7 +5,17 @@ // // Represented with an object `{ x, y, r }` for the origin and the radius. // + +// @affineplane.sphere2.UNIT +// +// The unit circle, radius=1. +// exports.UNIT = { x: 0, y: 0, r: 1 } + +// @affineplane.sphere2.ZERO +// +// A zero-radius circle. +// exports.ZERO = { x: 0, y: 0, r: 0 } exports.almostEqual = require('./almostEqual') diff --git a/lib/sphere3/index.js b/lib/sphere3/index.js index 20c8d67..ed7990c 100644 --- a/lib/sphere3/index.js +++ b/lib/sphere3/index.js @@ -4,7 +4,17 @@ // // Represented with an object `{ x, y, z, r }` for the origin and the radius. // + +// @affineplane.sphere3.UNIT +// +// The unit sphere, radius=1. +// exports.UNIT = { x: 0, y: 0, z: 0, r: 1 } + +// @affineplane.sphere3.ZERO +// +// A zero-radius sphere. +// exports.ZERO = { x: 0, y: 0, z: 0, r: 0 } exports.almostEqual = require('./almostEqual') diff --git a/lib/vec2/index.js b/lib/vec2/index.js index f9a8ee7..15c4395 100644 --- a/lib/vec2/index.js +++ b/lib/vec2/index.js @@ -6,6 +6,11 @@ // // ![A vector](geometry_vector.png) // + +// @affineplane.vec2.ZERO +// +// The zero vector in 2D +// exports.ZERO = { x: 0, y: 0 } // exports.distance diff --git a/lib/vec3/index.js b/lib/vec3/index.js index cfaada5..8f924cf 100644 --- a/lib/vec3/index.js +++ b/lib/vec3/index.js @@ -5,6 +5,11 @@ // and therefore their coordinates are affected only by plane scale // and rotation when represented on different plane. // + +// @affineplane.vec3.ZERO +// +// The zero vector in 3D +// exports.ZERO = { x: 0, y: 0, z: 0 } exports.add = require('./add') diff --git a/lib/vec4/index.js b/lib/vec4/index.js index a2e9f20..05556f3 100644 --- a/lib/vec4/index.js +++ b/lib/vec4/index.js @@ -2,6 +2,11 @@ // // A vec4 is a 4D vector { x, y, z, w }. // + +// @affineplane.vec4.ZERO +// +// The zero vector in 4D +// exports.ZERO = { x: 0, y: 0, z: 0, w: 0 } exports.add = require('./add') From de34fc2ad4cb25abbc296be4fdbf558563bc3395 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 18:55:55 +0300 Subject: [PATCH 18/21] register ray3 for doc generation --- docs/generate.js | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/generate.js b/docs/generate.js index e432ec3..a9c7469 100644 --- a/docs/generate.js +++ b/docs/generate.js @@ -77,6 +77,7 @@ yamdog.generate({ point3: '#affineplanepoint3', poly2: '#affineplanepoly2', quat4: '#affineplanequat4', + ray3: '#affineplaneray3', rot2: '#affineplanerot2', scalar1: '#affineplanescalar1', scalar2: '#affineplanescalar2', From 9e1f910c83709dc1839e97cc409f612d6bcfa733 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 18:57:24 +0300 Subject: [PATCH 19/21] render docs --- docs/API.md | 467 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 466 insertions(+), 1 deletion(-) diff --git a/docs/API.md b/docs/API.md index e274427..a42297f 100644 --- a/docs/API.md +++ b/docs/API.md @@ -39,6 +39,7 @@ The functions are grouped in the following submodules. - [affineplane.point3](#affineplanepoint3) - [affineplane.poly2](#affineplanepoly2) - [affineplane.quat4](#affineplanequat4) +- [affineplane.ray3](#affineplaneray3) - [affineplane.rect2](#affineplanerect2) - [affineplane.rect3](#affineplanerect3) - [affineplane.rot2](#affineplanerot2) @@ -150,6 +151,8 @@ and thus can be represented in any basis without loss of information.

Contents:

+- [affineplane.box2.UNIT](#affineplanebox2unit) +- [affineplane.box2.ZERO](#affineplanebox2zero) - [affineplane.box2.almostEqual](#affineplanebox2almostequal) - [affineplane.box2.at](#affineplanebox2at) - [affineplane.box2.atBox](#affineplanebox2atbox) @@ -187,6 +190,34 @@ and thus can be represented in any basis without loss of information. Source: [box2/index.js](https://github.com/axelpale/affineplane/blob/main/lib/box2/index.js) + +## [affineplane](#affineplane).[box2](#affineplanebox2).[UNIT](#affineplanebox2unit) + +An origin-centered box with unit width and height. + +Source: [box2/index.js](https://github.com/axelpale/affineplane/blob/main/lib/box2/index.js) + + +## [affineplane](#affineplane).[box2](#affineplanebox2).[ZERO](#affineplanebox2zero) + +A zero-size box. + +Source: [box2/index.js](https://github.com/axelpale/affineplane/blob/main/lib/box2/index.js) + + +## [affineplane](#affineplane).[box3](#affineplanebox3).[UNIT](#affineplanebox3unit) + +An origin-centered box with unit width, height, and depth. + +Source: [box3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/box3/index.js) + + +## [affineplane](#affineplane).[box3](#affineplanebox3).[ZERO](#affineplanebox3zero) + +A zero-size box. + +Source: [box3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/box3/index.js) + ## [affineplane](#affineplane).[box2](#affineplanebox2).[almostEqual](#affineplanebox2almostequal)(b, bb[, tolerance]) @@ -839,6 +870,8 @@ thus can be represented in any basis without loss of information.

Contents:

+- [affineplane.box3.UNIT](#affineplanebox3unit) +- [affineplane.box3.ZERO](#affineplanebox3zero) - [affineplane.box3.almostEqual](#affineplanebox3almostequal) - [affineplane.box3.at](#affineplanebox3at) - [affineplane.box3.atBox](#affineplanebox3atbox) @@ -1483,6 +1516,8 @@ Represented with an object `{ x, y, z, r }` for the origin and the radius.

Contents:

+- [affineplane.circle3.UNIT](#affineplanecircle3unit) +- [affineplane.circle3.ZERO](#affineplanecircle3zero) - [affineplane.circle3.almostEqual](#affineplanecircle3almostequal) - [affineplane.circle3.area](#affineplanecircle3area) - [affineplane.circle3.atCenter](#affineplanecircle3atcenter) @@ -1507,6 +1542,20 @@ Represented with an object `{ x, y, z, r }` for the origin and the radius. - [affineplane.circle3.validate](#affineplanecircle3validate) +Source: [circle3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/circle3/index.js) + + +## [affineplane](#affineplane).[circle3](#affineplanecircle3).[UNIT](#affineplanecircle3unit) + +The unit circle, radius=1. + +Source: [circle3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/circle3/index.js) + + +## [affineplane](#affineplane).[circle3](#affineplanecircle3).[ZERO](#affineplanecircle3zero) + +A zero-radius circle. + Source: [circle3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/circle3/index.js) @@ -6689,6 +6738,7 @@ An affine space does not have origin; `{ x:0, y:0 }` is not an origin.

Contents:

+- [affineplane.point2.ZERO](#affineplanepoint2zero) - [affineplane.point2.almostEqual](#affineplanepoint2almostequal) - [affineplane.point2.average](#affineplanepoint2average) - [affineplane.point2.copy](#affineplanepoint2copy) @@ -6721,6 +6771,13 @@ An affine space does not have origin; `{ x:0, y:0 }` is not an origin. - [affineplane.point2.vectorTo](#affineplanepoint2vectorto) +Source: [point2/index.js](https://github.com/axelpale/affineplane/blob/main/lib/point2/index.js) + + +## [affineplane](#affineplane).[point2](#affineplanepoint2).[ZERO](#affineplanepoint2zero) + +The zero point + Source: [point2/index.js](https://github.com/axelpale/affineplane/blob/main/lib/point2/index.js) @@ -7266,6 +7323,7 @@ translation of the plane on which they are represented.

Contents:

+- [affineplane.point3.ZERO](#affineplanepoint3zero) - [affineplane.point3.almostEqual](#affineplanepoint3almostequal) - [affineplane.point3.average](#affineplanepoint3average) - [affineplane.point3.copy](#affineplanepoint3copy) @@ -7275,7 +7333,9 @@ translation of the plane on which they are represented. - [affineplane.point3.difference](#affineplanepoint3difference) - [affineplane.point3.direction](#affineplanepoint3direction) - [affineplane.point3.distance](#affineplanepoint3distance) +- [affineplane.point3.distanceToLine](#affineplanepoint3distancetoline) - [affineplane.point3.distanceToPlane](#affineplanepoint3distancetoplane) +- [affineplane.point3.distanceToRay](#affineplanepoint3distancetoray) - [affineplane.point3.equal](#affineplanepoint3equal) - [affineplane.point3.equals](#affineplanepoint3equals) - [affineplane.point3.fromArray](#affineplanepoint3fromarray) @@ -7298,6 +7358,13 @@ translation of the plane on which they are represented. - [affineplane.point3.vectorTo](#affineplanepoint3vectorto) +Source: [point3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/point3/index.js) + + +## [affineplane](#affineplane).[point3](#affineplanepoint3).[ZERO](#affineplanepoint3zero) + +The zero point + Source: [point3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/point3/index.js) @@ -7442,6 +7509,27 @@ Euclidean distance between two points. Source: [distance.js](https://github.com/axelpale/affineplane/blob/main/lib/point3/distance.js) + +## [affineplane](#affineplane).[point3](#affineplanepoint3).[distanceToLine](#affineplanepoint3distancetoline)(p, line) + +Compute the smallest euclidean distance between a point and a line. +See also [affineplane.point3.distanceToRay](#affineplanepoint3distancetoray). + +

Parameters:

+ +- *p* + - a [point3](#affineplanepoint3) +- *line* + - a [line3](#affineplaneline3) + + +

Returns:

+ +- a number, a [scalar1](#affineplanescalar1), a [dist3](#affineplanedist3), a distance + + +Source: [distanceToLine.js](https://github.com/axelpale/affineplane/blob/main/lib/point3/distanceToLine.js) + ## [affineplane](#affineplane).[point3](#affineplanepoint3).[distanceToPlane](#affineplanepoint3distancetoplane)(p, plane) @@ -7462,6 +7550,30 @@ Euclidean distance between point and plane. Source: [distanceToPlane.js](https://github.com/axelpale/affineplane/blob/main/lib/point3/distanceToPlane.js) + +## [affineplane](#affineplane).[point3](#affineplanepoint3).[distanceToRay](#affineplanepoint3distancetoray)(p, ray) + +Compute the smallest euclidean distance between a point and a ray. +For half of the space, the closest point on the ray is the ray origin +and therefore, for points inside that half, the computation reduces +to the euclidean distance to the ray origin. +See also [affineplane.point3.distanceToLine](#affineplanepoint3distancetoline). + +

Parameters:

+ +- *p* + - a [point3](#affineplanepoint3) +- *ray* + - a [ray3](#affineplaneray3) + + +

Returns:

+ +- a number, a [scalar1](#affineplanescalar1), a [dist3](#affineplanedist3), a distance + + +Source: [distanceToRay.js](https://github.com/axelpale/affineplane/blob/main/lib/point3/distanceToRay.js) + ## [affineplane](#affineplane).[point3](#affineplanepoint3).[equal](#affineplanepoint3equal)(p, q) @@ -7546,7 +7658,7 @@ Offset a point by scalars dx, dy, dz.

Parameters:

- *p* - - a [point2](#affineplanepoint2) + - a [point3](#affineplanepoint3) - *dx* - a number, the offset along x-axis - *dy* @@ -8080,6 +8192,303 @@ an angle like π/2, the vector will be rotated twice that amount, π. Source: [rotateVector.js](https://github.com/axelpale/affineplane/blob/main/lib/quat4/rotateVector.js) + +## [affineplane](#affineplane).[ray3](#affineplaneray3) + +Ray is like a line but extends into one direction only from the origin. + +We represent ray with object `{ x, y, z, dx, dy, dz }` + + +

Contents:

+ + +- [affineplane.ray3.DEFAULT](#affineplaneray3default) +- [affineplane.ray3.ZERO](#affineplaneray3zero) +- [affineplane.ray3.almostEqual](#affineplaneray3almostequal) +- [affineplane.ray3.at](#affineplaneray3at) +- [affineplane.ray3.copy](#affineplaneray3copy) +- [affineplane.ray3.create](#affineplaneray3create) +- [affineplane.ray3.getOrigin](#affineplaneray3getorigin) +- [affineplane.ray3.getVector](#affineplaneray3getvector) +- [affineplane.ray3.homothety](#affineplaneray3homothety) +- [affineplane.ray3.invert](#affineplaneray3invert) +- [affineplane.ray3.negate](#affineplaneray3negate) +- [affineplane.ray3.offset](#affineplaneray3offset) +- [affineplane.ray3.transitFrom](#affineplaneray3transitfrom) +- [affineplane.ray3.transitTo](#affineplaneray3transitto) +- [affineplane.ray3.validate](#affineplaneray3validate) + + +Source: [ray3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/index.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[DEFAULT](#affineplaneray3default) + +Default ray that has origin at zero and unit span along positive x-axis. + +Source: [ray3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/index.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[ZERO](#affineplaneray3zero) + +Invalid ray with origin at zero and zero spanning vector. + +Source: [ray3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/index.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[almostEqual](#affineplaneray3almostequal)(r, rr[, epsilon]) + +Test if rays are almost equal by the margin of epsilon. + +

Parameters:

+ +- *r* + - a [ray3](#affineplaneray3) +- *rr* + - a [ray3](#affineplaneray3) +- *epsilon* + - Optional number, default to [affineplane.epsilon](#affineplaneepsilon). + - Set to 0 for strict comparison. + + +

Returns:

+ +- a boolean + + +Source: [almostEqual.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/almostEqual.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[at](#affineplaneray3at)(line, c) + +Get a point on the ray at position c from the ray origin. +For example, c=2 gives a point two ray spans away from the origin. +Negative positions default to the ray origin. + +

Parameters:

+ +- *ray* + - a [ray3](#affineplaneray3) +- *c* + - a number + + +

Returns:

+ +- a [point3](#affineplanepoint3) + + +Source: [at.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/at.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[copy](#affineplaneray3copy)(r) + +Copy ray object. + +

Parameters:

+ +- *r* + - a [ray3](#affineplaneray3) + + +

Returns:

+ +- a [ray3](#affineplaneray3) + + +Source: [copy.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/copy.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[create](#affineplaneray3create)(origin, span) + +Create a ray object from origin point and a spanning vector. + +

Parameters:

+ +- *origin* + - a [point3](#affineplanepoint3), the ray starting point. +- *span* + - a [dir3](#affineplanedir3) or [vec3](#affineplanevec3), the ray direction and unit length along the ray. + + +

Returns:

+ +- a [ray3](#affineplaneray3) + + +Source: [create.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/create.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[getOrigin](#affineplaneray3getorigin)(r) + +Get the origin point of the ray. Note that you can also use the ray +object itself as a point. + +

Parameters:

+ +- *r* + - a [ray3](#affineplaneray3) + + +

Returns:

+ +- a [point3](#affineplanepoint3) + + +Source: [getOrigin.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/getOrigin.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[getVector](#affineplaneray3getvector)(r) + +Get the spanning vector of the ray. + +

Parameters:

+ +- *r* + - a [ray3](#affineplaneray3) + + +

Returns:

+ +- a [vec3](#affineplanevec3) + + +Source: [getVector.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/getVector.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[homothety](#affineplaneray3homothety)(ray, origin, ratio) + +Perform a homothety about the origin for the ray. + +

Parameters:

+ +- *ray* + - a [ray3](#affineplaneray3) +- *origin* + - a [point3](#affineplanepoint3) +- *ratio* + - a number, the scaling ratio + + +

Returns:

+ +- a [ray3](#affineplaneray3) + + +Source: [homothety.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/homothety.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[invert](#affineplaneray3invert)(r) + +Get a ray with the same magnitude but to opposite direction. + +

Parameters:

+ +- *r* + - a [ray3](#affineplaneray3) + + +

Returns:

+ +- a [ray3](#affineplaneray3) + + +Aliases: [affineplane.ray3.negate](#affineplaneray3negate) + +Source: [invert.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/invert.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[negate](#affineplaneray3negate) + +Alias of [affineplane.ray3.invert](#affineplaneray3invert) + +Source: [invert.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/invert.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[offset](#affineplaneray3offset)(ray, dx, dy, dz) + +Offset a ray origin by scalars dx, dy, dz. + +

Parameters:

+ +- *ray* + - a [ray3](#affineplaneray3) +- *dx* + - a number, the offset along x-axis +- *dy* + - a number, the offset along y-axis +- *dz* + - a number, the offset along z-axis + + +

Returns:

+ +- a [ray3](#affineplaneray3) + + +Source: [offset.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/offset.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[transitFrom](#affineplaneray3transitfrom)(ray, basis) + +Represent the ray in the reference basis without losing information. + +

Parameters:

+ +- *ray* + - a [ray3](#affineplaneray3), represented in the given basis. +- *basis* + - a [plane3](#affineplaneplane3), represented in the reference basis. + + +

Returns:

+ +- a [ray3](#affineplaneray3), represented in the reference basis. + + +Source: [transitFrom.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/transitFrom.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[transitTo](#affineplaneray3transitto)(ray, basis) + +Represent the ray in another basis +without losing information. + +

Parameters:

+ +- *ray* + - a [ray3](#affineplaneray3), represented in the reference basis. +- *basis* + - a [plane3](#affineplaneplane3), represented in the reference basis. + + +

Returns:

+ +- a [ray3](#affineplaneray3), represented in the given basis. + + +Source: [transitTo.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/transitTo.js) + + +## [affineplane](#affineplane).[ray3](#affineplaneray3).[validate](#affineplaneray3validate)(r) + +Check if object is a valid [ray3](#affineplaneray3). Valid ray has numeric properties +x, y, z, dx, dy, dz and has non-zero spanning vector. + +

Parameters:

+ +- *r* + - an object + + +

Returns:

+ +- a boolean + + +Source: [validate.js](https://github.com/axelpale/affineplane/blob/main/lib/ray3/validate.js) + ## [affineplane](#affineplane).[rect2](#affineplanerect2) @@ -9552,6 +9961,8 @@ Aliases: [affineplane.circle2](#affineplanecircle2)

Contents:

+- [affineplane.sphere2.UNIT](#affineplanesphere2unit) +- [affineplane.sphere2.ZERO](#affineplanesphere2zero) - [affineplane.sphere2.almostEqual](#affineplanesphere2almostequal) - [affineplane.sphere2.area](#affineplanesphere2area) - [affineplane.sphere2.atCenter](#affineplanesphere2atcenter) @@ -9574,6 +9985,20 @@ Aliases: [affineplane.circle2](#affineplanecircle2) - [affineplane.sphere2.validate](#affineplanesphere2validate) +Source: [sphere2/index.js](https://github.com/axelpale/affineplane/blob/main/lib/sphere2/index.js) + + +## [affineplane](#affineplane).[sphere2](#affineplanesphere2).[UNIT](#affineplanesphere2unit) + +The unit circle, radius=1. + +Source: [sphere2/index.js](https://github.com/axelpale/affineplane/blob/main/lib/sphere2/index.js) + + +## [affineplane](#affineplane).[sphere2](#affineplanesphere2).[ZERO](#affineplanesphere2zero) + +A zero-radius circle. + Source: [sphere2/index.js](https://github.com/axelpale/affineplane/blob/main/lib/sphere2/index.js) @@ -9985,6 +10410,8 @@ Represented with an object `{ x, y, z, r }` for the origin and the radius.

Contents:

+- [affineplane.sphere3.UNIT](#affineplanesphere3unit) +- [affineplane.sphere3.ZERO](#affineplanesphere3zero) - [affineplane.sphere3.almostEqual](#affineplanesphere3almostequal) - [affineplane.sphere3.area](#affineplanesphere3area) - [affineplane.sphere3.atCenter](#affineplanesphere3atcenter) @@ -10011,6 +10438,20 @@ Represented with an object `{ x, y, z, r }` for the origin and the radius. - [affineplane.sphere3.volume](#affineplanesphere3volume) +Source: [sphere3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/sphere3/index.js) + + +## [affineplane](#affineplane).[sphere3](#affineplanesphere3).[UNIT](#affineplanesphere3unit) + +The unit sphere, radius=1. + +Source: [sphere3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/sphere3/index.js) + + +## [affineplane](#affineplane).[sphere3](#affineplanesphere3).[ZERO](#affineplanesphere3zero) + +A zero-radius sphere. + Source: [sphere3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/sphere3/index.js) @@ -10504,6 +10945,7 @@ position vectors.

Contents:

+- [affineplane.vec2.ZERO](#affineplanevec2zero) - [affineplane.vec2.add](#affineplanevec2add) - [affineplane.vec2.almostEqual](#affineplanevec2almostequal) - [affineplane.vec2.average](#affineplanevec2average) @@ -10544,6 +10986,27 @@ position vectors. Source: [vec2/index.js](https://github.com/axelpale/affineplane/blob/main/lib/vec2/index.js) + +## [affineplane](#affineplane).[vec2](#affineplanevec2).[ZERO](#affineplanevec2zero) + +The zero vector in 2D + +Source: [vec2/index.js](https://github.com/axelpale/affineplane/blob/main/lib/vec2/index.js) + + +## [affineplane](#affineplane).[vec3](#affineplanevec3).[ZERO](#affineplanevec3zero) + +The zero vector in 3D + +Source: [vec3/index.js](https://github.com/axelpale/affineplane/blob/main/lib/vec3/index.js) + + +## [affineplane](#affineplane).[vec4](#affineplanevec4).[ZERO](#affineplanevec4zero) + +The zero vector in 4D + +Source: [vec4/index.js](https://github.com/axelpale/affineplane/blob/main/lib/vec4/index.js) + ## [affineplane](#affineplane).[vec2](#affineplanevec2).[add](#affineplanevec2add)(v, w) @@ -11241,6 +11704,7 @@ and rotation when represented on different plane.

Contents:

+- [affineplane.vec3.ZERO](#affineplanevec3zero) - [affineplane.vec3.add](#affineplanevec3add) - [affineplane.vec3.almostEqual](#affineplanevec3almostequal) - [affineplane.vec3.average](#affineplanevec3average) @@ -12033,6 +12497,7 @@ A [vec4](#affineplanevec4) is a 4D vector { x, y, z, w }.

Contents:

+- [affineplane.vec4.ZERO](#affineplanevec4zero) - [affineplane.vec4.add](#affineplanevec4add) - [affineplane.vec4.almostEqual](#affineplanevec4almostequal) - [affineplane.vec4.create](#affineplanevec4create) From 59fe0c41a2ab2f0e0ad69f4a01e1fa0822b9a2f1 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 18:59:31 +0300 Subject: [PATCH 20/21] list ray3 in readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 968cc2f..f607d2f 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,10 @@ Shapes with position in space: - [plane3](https://axelpale.github.io/affineplane/docs/API.html#affineplaneplane3), an xy-plane in 3D, `{a,b,x,y,z}` - [point2](https://axelpale.github.io/affineplane/docs/API.html#affineplanepoint2), a location in 2D, `{x,y}` - [point3](https://axelpale.github.io/affineplane/docs/API.html#affineplanepoint3), a location in 3D, `{x,y,z}` +- [ray3](https://axelpale.github.io/affineplane/docs/API.html#affineplaneray3), a ray in 3D, `{x,y,z,dx,dy,dz}` - [scalar1](https://axelpale.github.io/affineplane/docs/API.html#affineplanescalar1), a first-order measure (length), `s` - [scalar2](https://axelpale.github.io/affineplane/docs/API.html#affineplanescalar2), a second-order measure (area), `ss` -- [scalar3](https://axelpale.github.io/affineplane/docs/API.html#affineplanescalar3), a third-order measure (volume), `s` +- [scalar3](https://axelpale.github.io/affineplane/docs/API.html#affineplanescalar3), a third-order measure (volume), `sss` - [segment2](https://axelpale.github.io/affineplane/docs/API.html#affineplanesegment2), a line segment in 2D space, `[{x,y},{x,y}]` - [size2](https://axelpale.github.io/affineplane/docs/API.html#affineplanesize2), a rectangle size in 2D, `{w,h}` - [size3](https://axelpale.github.io/affineplane/docs/API.html#affineplanesize3), a cuboid size in 3D, `{w,h,d}` From ad23e5c10306dd75bd3e9ac485c2c21a21c67d69 Mon Sep 17 00:00:00 2001 From: Akseli Palen Date: Sat, 29 Apr 2023 19:01:15 +0300 Subject: [PATCH 21/21] bump v2.16.0 --- docs/API.md | 2 +- lib/version.js | 2 +- package.json | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/API.md b/docs/API.md index a42297f..df93342 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1,5 +1,5 @@ -# Affineplane API Documentation v2.15.0 +# Affineplane API Documentation v2.16.0 Welcome to affineplane API reference documentation. These docs are generated with [yamdog](https://axelpale.github.io/yamdog/). diff --git a/lib/version.js b/lib/version.js index 9ac6ec2..5679a40 100644 --- a/lib/version.js +++ b/lib/version.js @@ -1,2 +1,2 @@ // Generated by genversion. -module.exports = '2.15.0' +module.exports = '2.16.0' diff --git a/package.json b/package.json index f38a00f..b4df288 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "affineplane", - "version": "2.15.0", + "version": "2.16.0", "description": "Affine geometry library for 2D and 3D spaces", "keywords": [ "affine", @@ -21,7 +21,10 @@ "similarity", "helmert", "perspective", - "projection" + "projection", + "segment", + "line", + "ray" ], "homepage": "https://github.com/axelpale/affineplane", "main": "index.js",