Triangle

Introduction

vtkTriangle is a cell which representant a triangle. It contains static
method to make some computations directly link to triangle.

See Also

vtkCell

Methods

cellBoundary

Get the nearest cell boundary to the specified parametric
coordinates and whether the point is inside or outside the cell.

Argument Type Required Description
subId Number Yes The sub-id of the cell.
pcoords Vector3 Yes The parametric coordinates.
pts Vector2 Yes The points of the cell.

computeNormal

Compute the normalized normal of a triangle composed of three points. The
normal is returned in normal.

Argument Type Required Description
v1 Vector3 Yes The first point coordinate.
v2 Vector3 Yes The second point coordinate.
v3 Vector3 Yes The third point coordinate.
n Vector3 Yes The normal coordinate.

computeNormalDirection

Compute the normal direction according to the three vertex which composed a
triangle. The normal is not normalized. The normal is returned in normal.

Argument Type Required Description
v1 Vector3 Yes The first point coordinate.
v2 Vector3 Yes The second point coordinate.
v3 Vector3 Yes The third point coordinate.
n Vector3 Yes The normal coordinate.

derivatives

Get the derivatives of the triangle at the specified parametric
coordinates.

Argument Type Required Description
subId Number Yes - The sub-id of the triangle.
pcoords Vector3 Yes - The parametric coordinates.
values Array[Number] Yes - The values at the points.
dim Number Yes - The dimension.
derivs Array[Number] Yes - The derivatives.

evaluateLocation

Determine global coordinates (x) from the given subId and parametric
coordinates.

Argument Type Required Description
pcoords Vector3 Yes The parametric coordinates.
x Vector3 Yes The x point coordinate.
weights Array[Number] Yes The number of weights.

evaluatePosition

Evaluates whether the specified point is inside (1), outside (0), or
indeterminate (-1) for the cell; computes parametric coordinates, sub-cell
ID (if applicable), squared distance to the cell (and closest point if
requested), and interpolation weights for the cell.

Argument Type Required Description
x Vector3 Yes The x point coordinate.
closestPoint Vector3 Yes The closest point coordinate.
pcoords Vector3 Yes The parametric coordinates.
weights Array[Number] Yes The number of weights.

extend

Method used to decorate a given object (publicAPI+model) with vtkTriangle characteristics.

Argument Type Required Description
publicAPI Yes object on which methods will be bounds (public)
model Yes object on which data structure will be bounds (protected)
initialValues ITriangleInitialValues No (default: {})

getCellDimension

Get the topological dimensional of the cell (0, 1, 2 or 3).

getParametricDistance

Get the distance of the parametric coordinate provided to the cell.

Argument Type Required Description
pcoords Vector3 Yes The parametric coordinates.

interpolationDerivs

Compute the interpolation functions/derivatives

Argument Type Required Description
derivs Array[Number] Yes - The derivatives.

intersectWithLine

Compute the intersection point of the intersection between triangle and
line defined by p1 and p2. tol Tolerance use for the position evaluation
x is the point which intersect triangle (computed in function) pcoords
parametric coordinates (computed in function) A javascript object is
returned :

{
evaluation: define if the triangle has been intersected or not
subId: always set to 0
t: parametric coordinate along the line.
betweenPoints: Define if the intersection is between input points
}
Argument Type Required Description
p1 Vector3 Yes The first point coordinate.
p2 Vector3 Yes The second point coordinate.
tol Number Yes The tolerance to use.
x Vector3 Yes The point which intersect triangle.
pcoords Vector3 Yes The parametric coordinates.

intersectWithTriangle

Compute the intersection between two triangles.

Argument Type Required Description
p1 Vector3 Yes The first point coordinate of the first triangle.
q1 Vector3 Yes The second point coordinate of the first triangle.
r1 Vector3 Yes The third point coordinate of the first triangle.
p2 Vector3 Yes The first point coordinate of the second triangle.
q2 Vector3 Yes The second point coordinate of the second triangle.
r2 Vector3 Yes The third point coordinate of the second triangle.

newInstance

Method used to create a new instance of vtkTriangle.

Argument Type Required Description
initialValues ITriangleInitialValues No for pre-setting some of its content

Source

index.d.ts
import { Vector2, Vector3 } from '../../../types';
import vtkCell, { ICellInitialValues } from '../Cell';

export interface ITriangleInitialValues extends ICellInitialValues {}

export interface IIntersectWithLine {
intersect: number;
t: number;
subId: number;
evaluation?: number;
betweenPoints?: boolean;
}

export interface IIntersectWithTriangle {
intersect: boolean;
coplanar: boolean;
pt1: Vector3;
pt2: Vector3;
surfaceId: number;
}

export interface vtkTriangle extends vtkCell {
/**
* Get the topological dimensional of the cell (0, 1, 2 or 3).
*/
getCellDimension(): number;

/**
* Compute the intersection point of the intersection between triangle and
* line defined by p1 and p2. tol Tolerance use for the position evaluation
* x is the point which intersect triangle (computed in function) pcoords
* parametric coordinates (computed in function) A javascript object is
* returned :
*
* ```js
* {
* evaluation: define if the triangle has been intersected or not
* subId: always set to 0
* t: parametric coordinate along the line.
* betweenPoints: Define if the intersection is between input points
* }
* ```
*
* @param {Vector3} p1 The first point coordinate.
* @param {Vector3} p2 The second point coordinate.
* @param {Number} tol The tolerance to use.
* @param {Vector3} x The point which intersect triangle.
* @param {Vector3} pcoords The parametric coordinates.
*/
intersectWithLine(
p1: Vector3,
p2: Vector3,
tol: number,
x: Vector3,
pcoords: Vector3
): IIntersectWithLine;

/**
* Get the nearest cell boundary to the specified parametric
* coordinates and whether the point is inside or outside the cell.
* @param {Number} subId The sub-id of the cell.
* @param {Vector3} pcoords The parametric coordinates.
* @param {Vector2} pts The points of the cell.
*/
cellBoundary(subId: number, pcoords: Vector3, pts: Vector2): boolean;

/**
* Get the derivatives of the triangle at the specified parametric
* coordinates.
* @param {Number} subId - The sub-id of the triangle.
* @param {Vector3} pcoords - The parametric coordinates.
* @param {Number[]} values - The values at the points.
* @param {Number} dim - The dimension.
* @param {Number[]} derivs - The derivatives.
*/
derivatives(
subId: number,
pcoords: Vector3,
values: any,
dim: number,
derivs: number[]
): void;

/**
* Evaluates whether the specified point is inside (1), outside (0), or
* indeterminate (-1) for the cell; computes parametric coordinates, sub-cell
* ID (if applicable), squared distance to the cell (and closest point if
* requested), and interpolation weights for the cell.
*
* @param {Vector3} x The x point coordinate.
* @param {Vector3} closestPoint The closest point coordinate.
* @param {Vector3} pcoords The parametric coordinates.
* @param {Number[]} weights The number of weights.
*/
evaluatePosition(
x: Vector3,
closestPoint: Vector3,
pcoords: Vector3,
weights: number[]
): IIntersectWithLine;

/**
* Determine global coordinates (x) from the given subId and parametric
* coordinates.
* @param {Vector3} pcoords The parametric coordinates.
* @param {Vector3} x The x point coordinate.
* @param {Number[]} weights The number of weights.
*/
evaluateLocation(pcoords: Vector3, x: Vector3, weights: number[]): void;

/**
* Get the distance of the parametric coordinate provided to the cell.
* @param {Vector3} pcoords The parametric coordinates.
*/
getParametricDistance(pcoords: Vector3): number;
}

/**
* Method used to decorate a given object (publicAPI+model) with vtkTriangle characteristics.
*
* @param publicAPI object on which methods will be bounds (public)
* @param model object on which data structure will be bounds (protected)
* @param {ITriangleInitialValues} [initialValues] (default: {})
*/
export function extend(
publicAPI: object,
model: object,
initialValues?: ITriangleInitialValues
): void;

/**
* Method used to create a new instance of vtkTriangle.
* @param {ITriangleInitialValues} [initialValues] for pre-setting some of its content
*/
export function newInstance(
initialValues?: ITriangleInitialValues
): vtkTriangle;

/**
* Compute the normal direction according to the three vertex which composed a
* triangle. The normal is not normalized. The normal is returned in normal.
* @param {Vector3} v1 The first point coordinate.
* @param {Vector3} v2 The second point coordinate.
* @param {Vector3} v3 The third point coordinate.
* @param {Vector3} n The normal coordinate.
*/
export function computeNormalDirection(
v1: Vector3,
v2: Vector3,
v3: Vector3,
n: Vector3
): void;

/**
* Compute the normalized normal of a triangle composed of three points. The
* normal is returned in normal.
* @param {Vector3} v1 The first point coordinate.
* @param {Vector3} v2 The second point coordinate.
* @param {Vector3} v3 The third point coordinate.
* @param {Vector3} n The normal coordinate.
*/
export function computeNormal(
v1: Vector3,
v2: Vector3,
v3: Vector3,
n: Vector3
): void;

/**
* Compute the interpolation functions/derivatives
* @param {Number[]} derivs - The derivatives.
*/
export function interpolationDerivs(derivs: number[]): void;

/**
* Compute the intersection between two triangles.
* @param {Vector3} p1 The first point coordinate of the first triangle.
* @param {Vector3} q1 The second point coordinate of the first triangle.
* @param {Vector3} r1 The third point coordinate of the first triangle.
* @param {Vector3} p2 The first point coordinate of the second triangle.
* @param {Vector3} q2 The second point coordinate of the second triangle.
* @param {Vector3} r2 The third point coordinate of the second triangle.
*/
export function intersectWithTriangle(
p1: Vector3,
q1: Vector3,
r1: Vector3,
p2: Vector3,
q2: Vector3,
r2: Vector3,
tolerance?: number
): IIntersectWithTriangle;

/**
* vtkTriangle is a cell which representant a triangle. It contains static
* method to make some computations directly link to triangle.
*
* @see vtkCell
*/
export declare const vtkTriangle: {
newInstance: typeof newInstance;
extend: typeof extend;
computeNormalDirection: typeof computeNormalDirection;
computeNormal: typeof computeNormal;
interpolationDerivs: typeof interpolationDerivs;
intersectWithTriangle: typeof intersectWithTriangle;
};
export default vtkTriangle;
index.js
import macro, { TYPED_ARRAYS } from 'vtk.js/Sources/macros';
import vtkCell from 'vtk.js/Sources/Common/DataModel/Cell';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
import vtkLine from 'vtk.js/Sources/Common/DataModel/Line';
import vtkPlane from 'vtk.js/Sources/Common/DataModel/Plane';

// ----------------------------------------------------------------------------
// Global methods
// ----------------------------------------------------------------------------

function computeNormalDirection(v1, v2, v3, n) {
// order is important!!! maintain consistency with triangle vertex order
const ax = v3[0] - v2[0];
const ay = v3[1] - v2[1];
const az = v3[2] - v2[2];
const bx = v1[0] - v2[0];
const by = v1[1] - v2[1];
const bz = v1[2] - v2[2];

n[0] = ay * bz - az * by;
n[1] = az * bx - ax * bz;
n[2] = ax * by - ay * bx;
}

function computeNormal(v1, v2, v3, n) {
computeNormalDirection(v1, v2, v3, n);
const length = Math.sqrt(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
if (length !== 0.0) {
n[0] /= length;
n[1] /= length;
n[2] /= length;
}
}

function interpolationDerivs(derivs) {
// Order: [dN1/dr, dN2/dr, dN3/dr, dN1/ds, dN2/ds, dN3/ds]
// r-derivatives
derivs[0] = -1.0;
derivs[1] = 1.0;
derivs[2] = 0.0;

// s-derivatives
derivs[3] = -1.0;
derivs[4] = 0.0;
derivs[5] = 1.0;
}

function intersectWithTriangle(p1, q1, r1, p2, q2, r2, tolerance = 1e-6) {
let coplanar = false;
const pt1 = [];
const pt2 = [];
const surfaceId = [];

const n1 = [];
const n2 = [];

// Compute supporting plane normals.
computeNormal(p1, q1, r1, n1);
computeNormal(p2, q2, r2, n2);
const s1 = -vtkMath.dot(n1, p1);
const s2 = -vtkMath.dot(n2, p2);

// Compute signed distances of points p1, q1, r1 from supporting
// plane of second triangle.
const dist1 = [
vtkMath.dot(n2, p1) + s2,
vtkMath.dot(n2, q1) + s2,
vtkMath.dot(n2, r1) + s2,
];

// If signs of all points are the same, all the points lie on the
// same side of the supporting plane, and we can exit early.
if (dist1[0] * dist1[1] > tolerance && dist1[0] * dist1[2] > tolerance) {
// vtkDebugMacro(<<"Same side supporting plane 1!");
return { intersect: false, coplanar, pt1, pt2, surfaceId };
}
// Do the same for p2, q2, r2 and supporting plane of first
// triangle.
const dist2 = [
vtkMath.dot(n1, p2) + s1,
vtkMath.dot(n1, q2) + s1,
vtkMath.dot(n1, r2) + s1,
];

// If signs of all points are the same, all the points lie on the
// same side of the supporting plane, and we can exit early.
if (dist2[0] * dist2[1] > tolerance && dist2[0] * dist2[2] > tolerance) {
// vtkDebugMacro(<<"Same side supporting plane 2!");
return { intersect: false, coplanar, pt1, pt2, surfaceId };
}
// Check for coplanarity of the supporting planes.
if (
Math.abs(n1[0] - n2[0]) < 1e-9 &&
Math.abs(n1[1] - n2[1]) < 1e-9 &&
Math.abs(n1[2] - n2[2]) < 1e-9 &&
Math.abs(s1 - s2) < 1e-9
) {
coplanar = true;
// vtkDebugMacro(<<"Coplanar!");
return { intersect: false, coplanar, pt1, pt2, surfaceId };
}

// There are more efficient ways to find the intersection line (if
// it exists), but this is clear enough.
const pts1 = [p1, q1, r1];
const pts2 = [p2, q2, r2];

// Find line of intersection (L = p + t*v) between two planes.
const n1n2 = vtkMath.dot(n1, n2);
const a = (s1 - s2 * n1n2) / (n1n2 * n1n2 - 1.0);
const b = (s2 - s1 * n1n2) / (n1n2 * n1n2 - 1.0);
const p = [
a * n1[0] + b * n2[0],
a * n1[1] + b * n2[1],
a * n1[2] + b * n2[2],
];
const v = vtkMath.cross(n1, n2, []);
vtkMath.normalize(v);

let index1 = 0;
let index2 = 0;
const t1 = [];
const t2 = [];
let ts1 = 50;
let ts2 = 50;
for (let i = 0; i < 3; i++) {
const id1 = i;
const id2 = (i + 1) % 3;

// Find t coordinate on line of intersection between two planes.
const val1 = vtkPlane.intersectWithLine(pts1[id1], pts1[id2], p2, n2);
if (val1.intersection && val1.t > 0 - tolerance && val1.t < 1 + tolerance) {
if (val1.t < 1 + tolerance && val1.t > 1 - tolerance) {
ts1 = index1;
}
t1[index1++] = vtkMath.dot(val1.x, v) - vtkMath.dot(p, v);
}

const val2 = vtkPlane.intersectWithLine(pts2[id1], pts2[id2], p1, n1);
if (val2.intersection && val2.t > 0 - tolerance && val2.t < 1 + tolerance) {
if (val2.t < 1 + tolerance && val2.t > 1 - tolerance) {
ts2 = index2;
}
t2[index2++] = vtkMath.dot(val2.x, v) - vtkMath.dot(p, v);
}
}

// If the value of the index is greater than 2, the intersecting point
// actually is intersected by all three edges. In this case, set the two
// edges to the two edges where the intersecting point is not the end point
if (index1 > 2) {
index1--;
// swap
const t12 = t1[2];
t1[2] = t1[ts1];
t1[ts1] = t12;
}
if (index2 > 2) {
index2--;
const t22 = t2[2];
t2[2] = t2[ts2];
t2[ts2] = t22;
}
// Check if only one edge or all edges intersect the supporting
// planes intersection.
if (index1 !== 2 || index2 !== 2) {
// vtkDebugMacro(<<"Only one edge intersecting!");
return { intersect: false, coplanar, pt1, pt2, surfaceId };
}

// Check for NaNs
if (
Number.isNaN(t1[0]) ||
Number.isNaN(t1[1]) ||
Number.isNaN(t2[0]) ||
Number.isNaN(t2[1])
) {
// vtkWarningMacro(<<"NaNs!");
return { intersect: false, coplanar, pt1, pt2, surfaceId };
}

if (t1[0] > t1[1]) {
// swap
const t11 = t1[1];
t1[1] = t1[0];
t1[0] = t11;
}
if (t2[0] > t2[1]) {
// swap
const t21 = t2[1];
t2[1] = t2[0];
t2[0] = t21;
}
// Handle the different interval configuration cases.
let tt1;
let tt2;
if (t1[1] < t2[0] || t2[1] < t1[0]) {
// vtkDebugMacro(<<"No Overlap!");
return { intersect: false, coplanar, pt1, pt2, surfaceId }; // No overlap
}
if (t1[0] < t2[0]) {
if (t1[1] < t2[1]) {
// First point on surface 2, second point on surface 1
surfaceId[0] = 2;
surfaceId[1] = 1;
tt1 = t2[0];
tt2 = t1[1];
} else {
// Both points belong to lines on surface 2
surfaceId[0] = 2;
surfaceId[1] = 2;
tt1 = t2[0];
tt2 = t2[1];
}
} // t1[0] >= t2[0]
else if (t1[1] < t2[1]) {
// Both points belong to lines on surface 1
surfaceId[0] = 1;
surfaceId[1] = 1;
tt1 = t1[0];
tt2 = t1[1];
} else {
// First point on surface 1, second point on surface 2
surfaceId[0] = 1;
surfaceId[1] = 2;
tt1 = t1[0];
tt2 = t2[1];
}

// Create actual intersection points.
vtkMath.multiplyAccumulate(p, v, tt1, pt1);
vtkMath.multiplyAccumulate(p, v, tt2, pt2);

return { intersect: true, coplanar, pt1, pt2, surfaceId };
}

// ----------------------------------------------------------------------------
// Static API
// ----------------------------------------------------------------------------

export const STATIC = {
computeNormalDirection,
computeNormal,
interpolationDerivs,
intersectWithTriangle,
};

// ----------------------------------------------------------------------------
// vtkTriangle methods
// ----------------------------------------------------------------------------

function vtkTriangle(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkTriangle');

publicAPI.getCellDimension = () => 2;

publicAPI.intersectWithLine = (p1, p2, tol, x, pcoords) => {
const outObj = {
subId: 0,
t: Number.MAX_VALUE,
intersect: 0,
betweenPoints: false,
};
pcoords[2] = 0.0;
const closestPoint = [];
const tol2 = tol * tol;

// Get normal for triangle
const pt1 = [];
const pt2 = [];
const pt3 = [];
model.points.getPoint(0, pt1);
model.points.getPoint(1, pt2);
model.points.getPoint(2, pt3);
const n = [];
const weights = [];
computeNormal(pt1, pt2, pt3, n);
if (n[0] !== 0 || n[1] !== 0 || n[2] !== 0) {
// Intersect plane of triangle with line
const plane = vtkPlane.intersectWithLine(p1, p2, pt1, n);
outObj.betweenPoints = plane.betweenPoints;
outObj.t = plane.t;
x[0] = plane.x[0];
x[1] = plane.x[1];
x[2] = plane.x[2];
if (!plane.intersection) {
pcoords[0] = 0.0;
pcoords[1] = 0.0;
outObj.intersect = 0;
return outObj;
}

// Evaluate position
const inside = publicAPI.evaluatePosition(
x,
closestPoint,
pcoords,
weights
);
if (inside.evaluation >= 0) {
if (inside.dist2 <= tol2) {
outObj.intersect = 1;
return outObj;
}
outObj.intersect = inside.evaluation;
return outObj;
}
}

// Normals are null, so the triangle is degenerated and
// we still need to check intersection between line and
// the longest edge.
const dist2Pt1Pt2 = vtkMath.distance2BetweenPoints(pt1, pt2);
const dist2Pt2Pt3 = vtkMath.distance2BetweenPoints(pt2, pt3);
const dist2Pt3Pt1 = vtkMath.distance2BetweenPoints(pt3, pt1);
if (!model.line) {
model.line = vtkLine.newInstance();
}
if (dist2Pt1Pt2 > dist2Pt2Pt3 && dist2Pt1Pt2 > dist2Pt3Pt1) {
model.line.getPoints().setPoint(0, pt1);
model.line.getPoints().setPoint(1, pt2);
} else if (dist2Pt2Pt3 > dist2Pt3Pt1 && dist2Pt2Pt3 > dist2Pt1Pt2) {
model.line.getPoints().setPoint(0, pt2);
model.line.getPoints().setPoint(1, pt3);
} else {
model.line.getPoints().setPoint(0, pt3);
model.line.getPoints().setPoint(1, pt1);
}

const intersectLine = model.line.intersectWithLine(p1, p2, tol, x, pcoords);
outObj.betweenPoints = intersectLine.betweenPoints;
outObj.t = intersectLine.t;
if (intersectLine.intersect) {
const pt3Pt1 = [];
const pt3Pt2 = [];
const pt3X = [];
// Compute r and s manually, using dot and norm.
for (let i = 0; i < 3; i++) {
pt3Pt1[i] = pt1[i] - pt3[i];
pt3Pt2[i] = pt2[i] - pt3[i];
pt3X[i] = x[i] - pt3[i];
}
pcoords[0] = vtkMath.dot(pt3X, pt3Pt1) / dist2Pt3Pt1;
pcoords[1] = vtkMath.dot(pt3X, pt3Pt2) / dist2Pt2Pt3;
outObj.intersect = 1;
return outObj;
}

pcoords[0] = 0.0;
pcoords[1] = 0.0;
outObj.intersect = 0;
return outObj;
};

/**
* Evaluates whether the specified point is inside (1), outside (0), or
* indeterminate (-1) for the cell; computes parametric coordinates, sub-cell
* ID (if applicable), squared distance to the cell (and closest point if
* requested), and interpolation weights for the cell.
*
* @param {Vector3} x The x point coordinate.
* @param {Vector3} closestPoint The closest point coordinate.
* @param {Vector3} pcoords The parametric coordinates.
* @param {Number[]} weights The number of weights.
*/
publicAPI.evaluatePosition = (x, closestPoint, pcoords, weights) => {
// will return obj
const outObj = { subId: 0, dist2: 0, evaluation: -1 };
let i;
let j;
const pt1 = [];
const pt2 = [];
const pt3 = [];
const n = [];
let fabsn;
const rhs = [];
const c1 = [];
const c2 = [];
let det = 0;
let idx = 0;
const indices = [];
let dist2Point;
let dist2Line1;
let dist2Line2;
let closest = [];
const closestPoint1 = [];
const closestPoint2 = [];
const cp = [];

outObj.subId = 0;
pcoords[2] = 0.0;

// Get normal for triangle, only the normal direction is needed, i.e. the
// normal need not be normalized (unit length)
//
model.points.getPoint(1, pt1);
model.points.getPoint(2, pt2);
model.points.getPoint(0, pt3);

computeNormalDirection(pt1, pt2, pt3, n);

// Project point to plane
vtkPlane.generalizedProjectPoint(x, pt1, n, cp);

// Construct matrices. Since we have over determined system, need to find
// which 2 out of 3 equations to use to develop equations. (Any 2 should
// work since we've projected point to plane.)
let maxComponent = 0.0;
for (i = 0; i < 3; i++) {
// trying to avoid an expensive call to fabs()
if (n[i] < 0) {
fabsn = -n[i];
} else {
fabsn = n[i];
}
if (fabsn > maxComponent) {
maxComponent = fabsn;
idx = i;
}
}

for (j = 0, i = 0; i < 3; i++) {
if (i !== idx) {
indices[j++] = i;
}
}

for (i = 0; i < 2; i++) {
rhs[i] = cp[indices[i]] - pt3[indices[i]];
c1[i] = pt1[indices[i]] - pt3[indices[i]];
c2[i] = pt2[indices[i]] - pt3[indices[i]];
}
det = vtkMath.determinant2x2(c1, c2);
if (det === 0.0) {
pcoords[0] = 0.0;
pcoords[1] = 0.0;
outObj.evaluation = -1;
return outObj;
}

pcoords[0] = vtkMath.determinant2x2(rhs, c2) / det;
pcoords[1] = vtkMath.determinant2x2(c1, rhs) / det;

// Okay, now find closest point to element
weights[0] = 1 - (pcoords[0] + pcoords[1]);
weights[1] = pcoords[0];
weights[2] = pcoords[1];

if (
weights[0] >= 0.0 &&
weights[0] <= 1.0 &&
weights[1] >= 0.0 &&
weights[1] <= 1.0 &&
weights[2] >= 0.0 &&
weights[2] <= 1.0
) {
// projection distance
if (closestPoint) {
outObj.dist2 = vtkMath.distance2BetweenPoints(cp, x);
closestPoint[0] = cp[0];
closestPoint[1] = cp[1];
closestPoint[2] = cp[2];
}
outObj.evaluation = 1;
} else {
let t;
if (closestPoint) {
if (weights[1] < 0.0 && weights[2] < 0.0) {
dist2Point = vtkMath.distance2BetweenPoints(x, pt3);
dist2Line1 = vtkLine.distanceToLine(x, pt1, pt3, t, closestPoint1);
dist2Line2 = vtkLine.distanceToLine(x, pt3, pt2, t, closestPoint2);
if (dist2Point < dist2Line1) {
outObj.dist2 = dist2Point;
closest = pt3;
} else {
outObj.dist2 = dist2Line1;
closest = closestPoint1;
}
if (dist2Line2 < outObj.dist2) {
outObj.dist2 = dist2Line2;
closest = closestPoint2;
}
for (i = 0; i < 3; i++) {
closestPoint[i] = closest[i];
}
} else if (weights[2] < 0.0 && weights[0] < 0.0) {
dist2Point = vtkMath.distance2BetweenPoints(x, pt1);
dist2Line1 = vtkLine.distanceToLine(x, pt1, pt3, t, closestPoint1);
dist2Line2 = vtkLine.distanceToLine(x, pt1, pt2, t, closestPoint2);
if (dist2Point < dist2Line1) {
outObj.dist2 = dist2Point;
closest = pt1;
} else {
outObj.dist2 = dist2Line1;
closest = closestPoint1;
}
if (dist2Line2 < outObj.dist2) {
outObj.dist2 = dist2Line2;
closest = closestPoint2;
}
for (i = 0; i < 3; i++) {
closestPoint[i] = closest[i];
}
} else if (weights[1] < 0.0 && weights[0] < 0.0) {
dist2Point = vtkMath.distance2BetweenPoints(x, pt2);
dist2Line1 = vtkLine.distanceToLine(x, pt2, pt3, t, closestPoint1);
dist2Line2 = vtkLine.distanceToLine(x, pt1, pt2, t, closestPoint2);
if (dist2Point < dist2Line1) {
outObj.dist2 = dist2Point;
closest = pt2;
} else {
outObj.dist2 = dist2Line1;
closest = closestPoint1;
}
if (dist2Line2 < outObj.dist2) {
outObj.dist2 = dist2Line2;
closest = closestPoint2;
}
for (i = 0; i < 3; i++) {
closestPoint[i] = closest[i];
}
} else if (weights[0] < 0.0) {
const lineDistance = vtkLine.distanceToLine(
x,
pt1,
pt2,
closestPoint
);
outObj.dist2 = lineDistance.distance;
} else if (weights[1] < 0.0) {
const lineDistance = vtkLine.distanceToLine(
x,
pt2,
pt3,
closestPoint
);
outObj.dist2 = lineDistance.distance;
} else if (weights[2] < 0.0) {
const lineDistance = vtkLine.distanceToLine(
x,
pt1,
pt3,
closestPoint
);
outObj.dist2 = lineDistance.distance;
}
}
outObj.evaluation = 0;
}

return outObj;
};

/**
* Determine global coordinates (x) from the given subId and parametric
* coordinates.
* @param {Vector3} pcoords The parametric coordinates.
* @param {Vector3} x The x point coordinate.
* @param {Number[]} weights The number of weights.
*/
publicAPI.evaluateLocation = (pcoords, x, weights) => {
const p0 = [];
const p1 = [];
const p2 = [];
model.points.getPoint(0, p0);
model.points.getPoint(1, p1);
model.points.getPoint(2, p2);
const u3 = 1.0 - pcoords[0] - pcoords[1];

for (let i = 0; i < 3; i++) {
x[i] = p0[i] * u3 + p1[i] * pcoords[0] + p2[i] * pcoords[1];
}

weights[0] = u3;
weights[1] = pcoords[0];
weights[2] = pcoords[1];
};

/**
* Get the distance of the parametric coordinate provided to the cell.
* @param {Vector3} pcoords The parametric coordinates.
*/
publicAPI.getParametricDistance = (pcoords) => {
let pDist;
let pDistMax = 0.0;
const pc = [];
pc[0] = pcoords[0];
pc[1] = pcoords[1];
pc[2] = 1.0 - pcoords[0] - pcoords[1];

for (let i = 0; i < 3; i++) {
if (pc[i] < 0.0) {
pDist = -pc[i];
} else if (pc[i] > 1.0) {
pDist = pc[i] - 1.0;
} else {
// inside the cell in the parametric direction
pDist = 0.0;
}
if (pDist > pDistMax) {
pDistMax = pDist;
}
}
return pDistMax;
};

/**
* Get the derivatives of the triangle strip.
* @param {Number} subId - The sub-id of the triangle.
* @param {Vector3} pcoords - The parametric coordinates.
* @param {Number[]} values - The values at the points.
* @param {Number} dim - The dimension.
* @param {Number[]} derivs - The derivatives.
*/
publicAPI.derivatives = (subId, pcoords, values, dim, derivs) => {
const x0 = model.points.getPoint(0);
const x1 = model.points.getPoint(1);
const x2 = model.points.getPoint(2);

const n = [];
const v10 = [];
const v20 = [];
const v = [];
computeNormal(x0, x1, x2, n);

vtkMath.subtract(x1, x0, v10);
vtkMath.subtract(x2, x0, v);
vtkMath.cross(n, v10, v20);

const lenX = vtkMath.normalize(v10); // check for degenerate triangle

if (lenX <= 0.0 || vtkMath.normalize(v20) <= 0.0) {
// degenerate
for (let j = 0; j < dim; j++) {
for (let i = 0; i < 3; i++) {
derivs[j * dim + i] = 0.0;
}
}
return;
}

// 2D coordinates
const v0 = [0, 0];
const v1 = [lenX, 0];
const v2 = [vtkMath.dot(v, v10), vtkMath.dot(v, v20)];

const functionDerivs = new Array(6);
interpolationDerivs(functionDerivs);

// Compute Jacobian: Jacobian is constant for a triangle.
const J = [v1[0] - v0[0], v1[1] - v0[1], v2[0] - v0[0], v2[1] - v0[1]];

// Compute inverse Jacobian (expects flat array)
const JI = macro.newTypedArray(TYPED_ARRAYS.Float64Array, 4);
vtkMath.invertMatrix(J, JI, 2); // returns flat array [JI00, JI01, JI10, JI11]

// Compute derivatives
for (let j = 0; j < dim; j++) {
let sum0 = 0.0;
let sum1 = 0.0;
for (let i = 0; i < 3; i++) {
sum0 += functionDerivs[i] * values[dim * i + j];
sum1 += functionDerivs[3 + i] * values[dim * i + j];
}
const dBydx = sum0 * JI[0] + sum1 * JI[1];
const dBydy = sum0 * JI[2] + sum1 * JI[3];

// Transform into global system (dot product with global axes)
derivs[3 * j] = dBydx * v10[0] + dBydy * v20[0];
derivs[3 * j + 1] = dBydx * v10[1] + dBydy * v20[1];
derivs[3 * j + 2] = dBydx * v10[2] + dBydy * v20[2];
}
};

/**
* Get the nearest cell boundary to the specified parametric
* coordinates and whether the point is inside or outside the cell.
* @param {Number} subId The sub-id of the cell.
* @param {Vector3} pcoords The parametric coordinates.
* @param {Vector2} pts The points of the cell.
*/
publicAPI.cellBoundary = (subId, pcoords, pts) => {
const t1 = pcoords[0] - pcoords[1];
const t2 = 0.5 * (1.0 - pcoords[0]) - pcoords[1];
const t3 = 2.0 * pcoords[0] + pcoords[1] - 1.0;

// compare against three lines in parametric space that divide element
// into three pieces
if (t1 >= 0.0 && t2 >= 0.0) {
pts[0] = model.pointsIds[0];
pts[1] = model.pointsIds[1];
} else if (t2 < 0.0 && t3 >= 0.0) {
pts[0] = model.pointsIds[1];
pts[1] = model.pointsIds[2];
} // ( t1 < 0.0 && t3 < 0.0 )
else {
pts[0] = model.pointsIds[2];
pts[1] = model.pointsIds[0];
}

if (
pcoords[0] < 0.0 ||
pcoords[1] < 0.0 ||
pcoords[0] > 1.0 ||
pcoords[1] > 1.0 ||
1.0 - pcoords[0] - pcoords[1] < 0.0
) {
return false; // outside of triangle
}

return true; // inside triangle
};
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);

vtkCell.extend(publicAPI, model, initialValues);

vtkTriangle(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = macro.newInstance(extend, 'vtkTriangle');

// ----------------------------------------------------------------------------

export default { newInstance, extend, ...STATIC };