Skip to content

Commit

Permalink
Merge pull request #12831 from sebavan/projectOnPlane
Browse files Browse the repository at this point in the history
Reinstate original version of projectOnPlaneToRef with small amendment
  • Loading branch information
sebavan authored Aug 3, 2022
2 parents b5b357c + 2c886c0 commit 1e2aacd
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 61 deletions.
56 changes: 21 additions & 35 deletions packages/dev/core/src/Maths/math.vector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1130,7 +1130,7 @@ export class Vector3 {
}

/**
* Projects the current vector3 to a plane along a ray starting from a specified origin and directed towards the point.
* Projects the current point Vector3 to a plane along a ray starting from a specified origin and passing through the current point Vector3.
* @param plane defines the plane to project to
* @param origin defines the origin of the projection ray
* @returns the projected vector3
Expand All @@ -1144,48 +1144,34 @@ export class Vector3 {
}

/**
* Projects the current vector3 to a plane along a ray starting from a specified origin and directed towards the point.
* Projects the current point Vector3 to a plane along a ray starting from a specified origin and passing through the current point Vector3.
* @param plane defines the plane to project to
* @param origin defines the origin of the projection ray
* @param result defines the Vector3 where to store the result
*/
public projectOnPlaneToRef(plane: Plane, origin: Vector3, result: Vector3): void {
// Use the normal scaled to the plane offset as the origin for the formula
const planeOrigin = MathTmp.Vector3[0];
plane.normal.scaleToRef(-plane.d, planeOrigin);

// Since the normal in Babylon should point toward the viewer, invert it for the dot product
const inverseNormal = MathTmp.Vector3[1];
plane.normal.negateToRef(inverseNormal);

// This vector is the direction
const { x, y, z } = this;

// Calculate how close the direction is to the normal of the plane
const dotProduct = Vector3.Dot(inverseNormal, this);

/*
* Early out in case the direction will never hit the plane.
*
* Epsilon is used to avoid issues with rays very near to parallel with the
* plane, and squared because as of writing the value is not sufficiently
* small for this use case.
*/
if (dotProduct <= Epsilon * Epsilon) {
// No good option for setting the result vector here, so just take the origin of the ray
result.copyFrom(origin);
return;
}
const n = plane.normal;
const d = plane.d;

const V = MathTmp.Vector3[0];

// ray direction
this.subtractToRef(origin, V);

// Calculate the offset
const relativeOrigin = MathTmp.Vector3[2];
planeOrigin.subtractToRef(origin, relativeOrigin);
V.normalize();

// Calculate the length along the direction vector to the hit point
const hitDistance = Vector3.Dot(relativeOrigin, inverseNormal) / dotProduct;
const denom = Vector3.Dot(V, n);

// Apply the hit point by adding the direction scaled by the distance to the origin
result.set(origin.x + x * hitDistance, origin.y + y * hitDistance, origin.z + z * hitDistance);
//When the ray is close to parallel to the plane return infinity vector
if (Math.abs(denom) < Math.pow(10, -10)) {
origin.addToRef(new Vector3(Infinity, Infinity, Infinity), result);
} else {
const t = -(Vector3.Dot(origin, n) + d) / denom;

// P = P0 + t*V
const scaledV = V.scaleInPlace(t);
origin.addToRef(scaledV, result);
}
}

/**
Expand Down
43 changes: 17 additions & 26 deletions packages/dev/core/test/unit/Math/babylon.math.vector.test.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,34 @@
import { Plane, Vector3 } from 'core/Maths'
import { Plane, Vector3 } from "core/Maths";

/**
* Describes the test suite.
*/
describe("Babylon Vectors", () => {
describe("#Vector3", () => {
it("can project a direction vector onto a plane", () => {
it("can project from an origin onto a plane", () => {
// A ground plane at origin
const simplePlane = Plane.FromPositionAndNormal(
Vector3.Zero(),
Vector3.Up(),
);
const simplePlane = Plane.FromPositionAndNormal(Vector3.Zero(), Vector3.Up());

const rayOrigin = new Vector3(0, 10, 0);
const rayAngle = new Vector3(0.5, -0.5, 0);
const rayGoingThrough = new Vector3(1, 8, 0);

/*
* At 45 degrees this should form a perfect right triangle,
* so the result should be the same as the distance to the origin.
*/
const expected = new Vector3(10, 0, 0);
// Going left 1 unit for each 2 units downs
const expected = new Vector3(5, 0, 0);

expect(rayAngle.projectOnPlane(simplePlane, rayOrigin)).toEqual(expected)
})
expect(rayGoingThrough.projectOnPlane(simplePlane, rayOrigin)).toEqual(expected);
});

it("can project a direction vector onto an offset plane", () => {
it("can project from an origin onto an offset plane", () => {
// A ground plane 10 units below origin
const simplePlane = Plane.FromPositionAndNormal(
new Vector3(0, -10, 0),
Vector3.Up(),
);
const simplePlane = Plane.FromPositionAndNormal(new Vector3(0, -10, 0), Vector3.Up());

const rayOrigin = new Vector3(0, 10, 0);
const rayAngle = new Vector3(0.5, -0.5, 0);
const rayGoingThrough = new Vector3(1, 8, 0);

// This is also a right triangle, but the plane is offset so the hit point is offset and the distance increases
const expected = new Vector3(20, -10, 0);
// Going left 1 unit for each 2 units downs
const expected = new Vector3(10, -10, 0);

expect(rayAngle.projectOnPlane(simplePlane, rayOrigin)).toEqual(expected)
})
})
})
expect(rayGoingThrough.projectOnPlane(simplePlane, rayOrigin)).toEqual(expected);
});
});
});

0 comments on commit 1e2aacd

Please sign in to comment.