diff --git a/source/engine/geometry/coord2d.js b/source/engine/geometry/coord2d.js index a7ace92e..1b07766c 100644 --- a/source/engine/geometry/coord2d.js +++ b/source/engine/geometry/coord2d.js @@ -33,3 +33,8 @@ export function CoordDistance2D (a, b) { return Math.sqrt ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); } + +export function DotVector2D (a, b) +{ + return a.x * b.x + a.y * b.y; +} diff --git a/source/engine/geometry/line2d.js b/source/engine/geometry/line2d.js new file mode 100644 index 00000000..4a4a8647 --- /dev/null +++ b/source/engine/geometry/line2d.js @@ -0,0 +1,39 @@ +import { Coord2D, CoordDistance2D, DotVector2D, SubCoord2D } from './coord2d.js'; +import { IsZero } from './geometry.js'; + +export class Segment2D +{ + constructor (beg, end) + { + this.beg = beg; + this.end = end; + } + + Clone () + { + return new Segment2D (this.beg, this.end); + } +} + +export function ProjectPointToSegment2D (segment, point) +{ + let begToEndVec = SubCoord2D (segment.end, segment.beg); + let begToPointVec = SubCoord2D (point, segment.beg); + let nom = DotVector2D (begToEndVec, begToPointVec); + let denom = DotVector2D (begToEndVec, begToEndVec); + if (IsZero (denom)) { + return segment.beg.Clone (); + } + let t = nom / denom; + t = Math.max (0.0, Math.min (1.0, t)); + return new Coord2D ( + segment.beg.x + t * begToEndVec.x, + segment.beg.y + t * begToEndVec.y + ); +} + +export function SegmentPointDistance2D (segment, point) +{ + let projected = ProjectPointToSegment2D (segment, point); + return CoordDistance2D (projected, point); +} diff --git a/source/engine/main.js b/source/engine/main.js index 066feaf3..b58b3cfb 100644 --- a/source/engine/main.js +++ b/source/engine/main.js @@ -12,10 +12,11 @@ import { ExporterOff } from './export/exporteroff.js'; import { ExporterPly } from './export/exporterply.js'; import { ExporterStl } from './export/exporterstl.js'; import { Box3D, BoundingBoxCalculator3D } from './geometry/box3d.js'; -import { Coord2D, CoordIsEqual2D, AddCoord2D, SubCoord2D, CoordDistance2D } from './geometry/coord2d.js'; +import { Coord2D, CoordIsEqual2D, AddCoord2D, SubCoord2D, CoordDistance2D, DotVector2D } from './geometry/coord2d.js'; import { Coord3D, CoordIsEqual3D, AddCoord3D, SubCoord3D, CoordDistance3D, DotVector3D, VectorAngle3D, CrossVector3D, VectorLength3D, ArrayToCoord3D } from './geometry/coord3d.js'; import { Coord4D } from './geometry/coord4d.js'; import { IsZero, IsLower, IsGreater, IsLowerOrEqual, IsGreaterOrEqual, IsEqual, IsEqualEps, IsPositive, IsNegative, Eps, BigEps, RadDeg, DegRad, Direction } from './geometry/geometry.js'; +import { Segment2D, ProjectPointToSegment2D, SegmentPointDistance2D } from './geometry/line2d.js'; import { Matrix, MatrixIsEqual } from './geometry/matrix.js'; import { OctreeNode, Octree } from './geometry/octree.js'; import { Quaternion, QuaternionIsEqual, ArrayToQuaternion, QuaternionFromAxisAngle, QuaternionFromXYZ } from './geometry/quaternion.js'; @@ -105,6 +106,7 @@ export { AddCoord2D, SubCoord2D, CoordDistance2D, + DotVector2D, Coord3D, CoordIsEqual3D, AddCoord3D, @@ -130,6 +132,9 @@ export { RadDeg, DegRad, Direction, + Segment2D, + ProjectPointToSegment2D, + SegmentPointDistance2D, Matrix, MatrixIsEqual, OctreeNode, diff --git a/test/tests/geometry_test.js b/test/tests/geometry_test.js index b6bc1826..afa8782a 100644 --- a/test/tests/geometry_test.js +++ b/test/tests/geometry_test.js @@ -63,6 +63,62 @@ describe ('Coord', function () { }); }); +describe ('Segment', function () { + it ('Projected point', function () { + let segment = new OV.Segment2D (new OV.Coord2D (0.0, 0.0), new OV.Coord2D (10.0, 0.0)); + + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (-2.0, 0.0)), new OV.Coord2D (0.0, 0.0))); + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (0.0, 0.0)), new OV.Coord2D (0.0, 0.0))); + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (2.0, 0.0)), new OV.Coord2D (2.0, 0.0))); + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (8.0, 0.0)), new OV.Coord2D (8.0, 0.0))); + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (10.0, 0.0)), new OV.Coord2D (10.0, 0.0))); + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (12.0, 0.0)), new OV.Coord2D (10.0, 0.0))); + + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (-2.0, 10.0)), new OV.Coord2D (0.0, 0.0))); + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (0.0, 10.0)), new OV.Coord2D (0.0, 0.0))); + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (2.0, 10.0)), new OV.Coord2D (2.0, 0.0))); + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (8.0, 10.0)), new OV.Coord2D (8.0, 0.0))); + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (10.0, 10.0)), new OV.Coord2D (10.0, 0.0))); + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (12.0, 10.0)), new OV.Coord2D (10.0, 0.0))); + }); + + it ('Projected point on invalid segment', function () { + let segment = new OV.Segment2D (new OV.Coord2D (0.0, 0.0), new OV.Coord2D (0.0, 0.0)); + + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (0.0, 0.0)), new OV.Coord2D (0.0, 0.0))); + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (10.0, 0.0)), new OV.Coord2D (0.0, 0.0))); + assert.ok (OV.CoordIsEqual2D (OV.ProjectPointToSegment2D (segment, new OV.Coord2D (0.0, 10.0)), new OV.Coord2D (0.0, 0.0))); + }); + + it ('Segment and point distance', function () { + let segment = new OV.Segment2D (new OV.Coord2D (0.0, 0.0), new OV.Coord2D (0.0, 10.0)); + + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (0.0, 0.0)), 0.0)); + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (0.0, 2.0)), 0.0)); + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (0.0, 8.0)), 0.0)); + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (0.0, 10.0)), 0.0)); + + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (-2.0, 0.0)), 2.0)); + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (-4.0, 2.0)), 4.0)); + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (6.0, 8.0)), 6.0)); + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (8.0, 10.0)), 8.0)); + + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (0.0, -2.0)), 2.0)); + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (0.0, 12.0)), 2.0)); + }); + + it ('Segment and point distance 2', function () { + let segment = new OV.Segment2D (new OV.Coord2D (2.0, 2.0), new OV.Coord2D (4.0, 4.0)); + + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (2.0, 2.0)), 0.0)); + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (3.0, 3.0)), 0.0)); + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (4.0, 4.0)), 0.0)); + + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (4.0, 5.0)), 1.0)); + assert.ok (OV.IsEqual (OV.SegmentPointDistance2D (segment, new OV.Coord2D (0.0, 2.0)), 2.0)); + }); +}); + describe ('Quaternion', function () { it ('Create Quaternion', function () { let q1 = OV.QuaternionFromAxisAngle (new OV.Coord3D (1.0, 0.0, 0.0), Math.PI / 2.0);