Skip to content

Commit

Permalink
feat(vtkPlane): Add intersectWithPlane function
Browse files Browse the repository at this point in the history
Similar to, but not the same as the c++ vtk plane logic, where the c++ version assumes a
bound/finite plane and an infinite plane.
  • Loading branch information
Madison Dickson authored and Madison Dickson committed Aug 20, 2019
1 parent 8bf37cf commit 70d7856
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 29 deletions.
70 changes: 41 additions & 29 deletions Sources/Common/DataModel/Plane/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,68 +13,80 @@ Default is [0.0, 0.0, 0.0].

## Methods

evaluateFunction(x)
: Evaluate plane equation for point x
### evaluateFunction(x)
Evaluate plane equation for point x.
Accepts both an array point representation and individual xyz arguments.
```js
plane.evaluateFunction([0.0, 0.0, 0.0]);
plane.evaluateFunction(0.0, 0.0, 0.0);
```

### evaluateGradient(xyz)
Given the point xyz (three floating values) evaluate the equation for the plane gradient. Note that the normal and origin must have already been specified. The method returns an array of three floats.

evaluateGradient(xyz)
: Given the point xyz (three floating values) evaluate the equation for the
plane gradient. Note that the normal and origin must have already been
specified. The method returns an array of three floats.


push(distance)
: Translate the plane in the direction of the normal by the distance
### push(distance)
Translate the plane in the direction of the normal by the distance
specified. Negative values move the plane in the opposite direction.


(static) projectPoint(x, origin, normal, xproj)
projectPoint(x, xproj)
: Project a point x onto plane defined by origin and normal. The
projected point is returned in xproj. NOTE : normal assumed to
### *(static)* projectPoint(x, origin, normal, xproj)
### projectPoint(x, xproj)
Project a point x onto plane defined by origin and normal. The
projected point is returned in xproj. **NOTE:** normal assumed to
have magnitude 1.


(static) projectVector(v, normal, vproj)
projectVector(v, vproj)
: Project a vector v onto plane defined by origin and normal. The
### *(static)* projectVector(v, normal, vproj)
### projectVector(v, vproj)
Project a vector v onto plane defined by origin and normal. The
projected vector is returned in vproj.


(static) generalizedProjectPoint(x, origin, normal, xproj)
generalizedProjectPoint(x, xproj)
: Project a point x onto plane defined by origin and normal. The
projected point is returned in xproj. NOTE : normal does NOT have to
### *(static)* generalizedProjectPoint(x, origin, normal, xproj)
### generalizedProjectPoint(x, xproj)
Project a point x onto plane defined by origin and normal. The
projected point is returned in xproj. **NOTE:** normal does NOT have to
have magnitude 1.


(static) evaluate(normal, origin, x)
: Quick evaluation of plane equation n(x-origin) = 0.
### *(static)* evaluate(normal, origin, x)
Quick evaluation of plane equation n(x-origin) = 0.


(static) distanceToPlane(x, origin, normal)
distanceToPlane(x)
: Return the distance of a point x to a plane defined by n (x-p0) = 0.
### *(static)* distanceToPlane(x, origin, normal)
### distanceToPlane(x)
Return the distance of a point x to a plane defined by n (x-p0) = 0.
The normal n must be magnitude = 1.


(static) intersectWithLine(p1, p2, origin, normal)
intersectWithLine(p1, p2)
: Given a line defined by the two points p1,p2; and a plane defined by the
### *(static)* intersectWithLine(p1, p2, origin, normal)
### intersectWithLine(p1, p2)
Given a line defined by the two points p1,p2; and a plane defined by the
normal n and point p0, compute an intersection.
Return an object:
```js
let obj = {intersection: ..., t: ..., x: ...};
```
where:
- **intersection** (*boolean*): indicates if there the plane and line intersect.
- **intersection** (*boolean*): indicates if the plane and line intersect.
Intersection is true if (0 <= t <= 1), and false otherwise.
- **t** (*Number*): parametric coordinate along the line.
- **x** (*Array*): coordinates of intersection.

If the plane and line are parallel, intersection is false and t is set
to Number.MAX_VALUE.

### *(static)* intersectWithPlane(plane1Origin, plane1Normal, plane2Origin, plane2Normal)
### intersectWithPlane(planeOrigin, planeNormal)
Given a planes defined by the normals n0 & n1 and origin points p0 & p1, compute the line representing the plane intersection.
Return an object:
```js
let obj = {intersection: ..., t: ..., l0: ..., l1: ...};
```
where:
- **intersection** (*boolean*): indicates if the two planes intersect.
Intersection is true if (0 <= t <= 1), and false otherwise.
- **l0** (*Array*): coordinates of point 0 of the intersection line.
- **l1** (*Array*): coordinates of point 1 of the intersection line.
- **error** (*String|null*): Conditional, if the planes do not intersect, is it because they are coplanar ('coincide') or parallel ('disjoint').
77 changes: 77 additions & 0 deletions Sources/Common/DataModel/Plane/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,79 @@ function intersectWithLine(p1, p2, origin, normal) {
return outObj;
}

function intersectWithPlane(
plane1Origin,
plane1Normal,
plane2Origin,
plane2Normal
) {
const outObj = {
intersection: false,
l0: [],
l1: [],
error: null,
};

const cross = [];
vtkMath.cross(plane1Normal, plane2Normal, cross);
const absCross = cross.map((n) => Math.abs(n));

// test if the two planes are parallel
if (absCross[0] + absCross[1] + absCross[2] < PLANE_TOLERANCE) {
// test if disjoint or coincide
const v = [];
vtkMath.subtract(plane1Origin, plane2Origin, v);
if (vtkMath.dot(plane1Normal, v) === 0) {
outObj.error = 'coincide';
} else {
outObj.error = 'disjoint';
}
return outObj;
}

// Plane1 and Plane2 intersect in a line
// first determine max abs coordinate of the cross product
let maxc;
if (absCross[0] > absCross[1] && absCross[0] > absCross[2]) {
maxc = 'x';
} else if (absCross[1] > absCross[2]) {
maxc = 'y';
} else {
maxc = 'z';
}

// To get a point on the intersect line, zero the max coord, and solve for the other two
const iP = []; // intersectionPoint
// the constants in the 2 plane equations
const d1 = -vtkMath.dot(plane1Normal, plane1Origin);
const d2 = -vtkMath.dot(plane2Normal, plane2Origin);

// eslint-disable-next-line default-case
switch (maxc) {
case 'x': // intersect with x=0
iP[0] = 0;
iP[1] = (d2 * plane1Normal[2] - d1 * plane2Normal[2]) / cross[0];
iP[2] = (d1 * plane2Normal[1] - d2 * plane1Normal[1]) / cross[0];
break;
case 'y': // intersect with y=0
iP[0] = (d1 * plane2Normal[2] - d2 * plane1Normal[2]) / cross[1];
iP[1] = 0;
iP[2] = (d2 * plane1Normal[0] - d1 * plane2Normal[0]) / cross[1];
break;
case 'z': // intersect with z=0
iP[0] = (d2 * plane1Normal[1] - d1 * plane2Normal[1]) / cross[2];
iP[1] = (d1 * plane2Normal[0] - d2 * plane1Normal[0]) / cross[2];
iP[2] = 0;
break;
}

outObj.l0 = iP;
vtkMath.add(iP, cross, outObj.l1);
outObj.intersection = true;

return outObj;
}

// ----------------------------------------------------------------------------
// Static API
// ----------------------------------------------------------------------------
Expand All @@ -122,6 +195,7 @@ export const STATIC = {
projectVector,
generalizedProjectPoint,
intersectWithLine,
intersectWithPlane,
};

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -178,6 +252,9 @@ export function vtkPlane(publicAPI, model) {

publicAPI.intersectWithLine = (p1, p2) =>
intersectWithLine(p1, p2, model.origin, model.normal);

publicAPI.intersectWithPlane = (planeOrigin, planeNormal) =>
intersectWithPlane(planeOrigin, planeNormal, model.origin, model.normal);
}

// ----------------------------------------------------------------------------
Expand Down
39 changes: 39 additions & 0 deletions Sources/Common/DataModel/Plane/test/testPlane.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,45 @@ test('Test vtkPlane intersectWithLine', (t) => {
t.end();
});

test('Test vtkPlane intersectWithPlane', (t) => {
const plane = vtkPlane.newInstance();
plane.setOrigin(0.0, 0.0, 0.0);
plane.setNormal(0.0, 0.0, 1.0);

// test where a plane is parallel to plane
let origin = [2.0, 2.0, 2.0];
let normal = [0.0, 0.0, 1.0];
let res = plane.intersectWithPlane(origin, normal);
t.equal(res.intersection, false);
t.equal(res.error, 'disjoint');
t.equal(res.l0.length, 0);
t.equal(res.l1.length, 0);

// test where a plane is coplaner with plane
origin = [1.0, 0.0, 0.0];
normal = [0.0, 0.0, 1.0];
res = plane.intersectWithPlane(origin, normal);
t.equal(res.intersection, false);
t.equal(res.error, 'coincide');

// test where plane does intersect plane
origin = [2.0, 0.0, 0.0];
normal = [1.0, 0.0, 0.0];
res = plane.intersectWithPlane(origin, normal);
t.equal(res.intersection, true);
t.equal(res.l0.length, 3);
t.equal(res.l1.length, 3);
const l0 = [2, 0, -0];
const l1 = [2, -1, 0];
for (let i = 0; i < 3; i++) {
t.equal(res.l0[i], l0[i]);
}
for (let i = 0; i < 3; i++) {
t.equal(res.l1[i], l1[i]);
}
t.end();
});

test('Test vtkPlane evaluateFunction', (t) => {
const plane = vtkPlane.newInstance();
plane.setOrigin(0.0, 0.0, 0.0);
Expand Down

0 comments on commit 70d7856

Please sign in to comment.