Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
880 lines (780 sloc) 22.8 KB
/*=========================================================================
Program: Visualization Toolkit
Module: vtkLine.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkLine.h"
#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkIncrementalPointLocator.h"
#include "vtkPoints.h"
vtkStandardNewMacro(vtkLine);
//----------------------------------------------------------------------------
// Construct the line with two points.
vtkLine::vtkLine()
{
this->Points->SetNumberOfPoints(2);
this->PointIds->SetNumberOfIds(2);
for (int i = 0; i < 2; i++)
{
this->Points->SetPoint(i, 0.0, 0.0, 0.0);
this->PointIds->SetId(i,0);
}
}
//----------------------------------------------------------------------------
static const int VTK_NO_INTERSECTION=0;
static const int VTK_YES_INTERSECTION=2;
static const int VTK_ON_LINE=3;
int vtkLine::EvaluatePosition(double x[3], double* closestPoint,
int& subId, double pcoords[3],
double& dist2, double *weights)
{
double a1[3], a2[3];
subId = 0;
pcoords[0] = pcoords[1] = pcoords[2] = 0.0;
this->Points->GetPoint(0, a1);
this->Points->GetPoint(1, a2);
if (closestPoint)
{
// DistanceToLine sets pcoords[0] to a value t, 0 <= t <= 1
dist2 = this->DistanceToLine(x,a1,a2,pcoords[0],closestPoint);
}
// pcoords[0] == t, need weights to be 1-t and t
weights[0] = 1.0 - pcoords[0];
weights[1] = pcoords[0];
if ( pcoords[0] < 0.0 || pcoords[0] > 1.0 )
{
return 0;
}
else
{
return 1;
}
}
//----------------------------------------------------------------------------
void vtkLine::EvaluateLocation(int& vtkNotUsed(subId), double pcoords[3],
double x[3], double *weights)
{
int i;
double a1[3], a2[3];
this->Points->GetPoint(0, a1);
this->Points->GetPoint(1, a2);
for (i=0; i<3; i++)
{
x[i] = a1[i] + pcoords[0]*(a2[i] - a1[i]);
}
weights[0] = 1.0 - pcoords[0];
weights[1] = pcoords[0];
}
//----------------------------------------------------------------------------
// Performs intersection of two finite 3D lines. An intersection is found if
// the projection of the two lines onto the plane perpendicular to the cross
// product of the two lines intersect. The parameters (u,v) are the
// parametric coordinates of the lines at the position of closest approach.
int vtkLine::Intersection (double a1[3], double a2[3],
double b1[3], double b2[3],
double& u, double& v)
{
double a21[3], b21[3], b1a1[3];
double c[2];
double *A[2], row1[2], row2[2];
// Initialize
u = v = 0.0;
// Determine line vectors.
a21[0] = a2[0] - a1[0]; b21[0] = b2[0] - b1[0]; b1a1[0] = b1[0] - a1[0];
a21[1] = a2[1] - a1[1]; b21[1] = b2[1] - b1[1]; b1a1[1] = b1[1] - a1[1];
a21[2] = a2[2] - a1[2]; b21[2] = b2[2] - b1[2]; b1a1[2] = b1[2] - a1[2];
// Compute the system (least squares) matrix.
A[0] = row1;
A[1] = row2;
row1[0] = vtkMath::Dot ( a21, a21 );
row1[1] = -vtkMath::Dot ( a21, b21 );
row2[0] = row1[1];
row2[1] = vtkMath::Dot ( b21, b21 );
// Compute the least squares system constant term.
c[0] = vtkMath::Dot ( a21, b1a1 );
c[1] = -vtkMath::Dot ( b21, b1a1 );
// Solve the system of equations
if ( vtkMath::SolveLinearSystem(A,c,2) == 0 )
{
return VTK_ON_LINE;
}
else
{
u = c[0];
v = c[1];
}
// Check parametric coordinates for intersection.
if ( (0.0 <= u) && (u <= 1.0) && (0.0 <= v) && (v <= 1.0) )
{
return VTK_YES_INTERSECTION;
}
else
{
return VTK_NO_INTERSECTION;
}
}
//----------------------------------------------------------------------------
int vtkLine::CellBoundary(int vtkNotUsed(subId), double pcoords[3],
vtkIdList *pts)
{
pts->SetNumberOfIds(1);
if ( pcoords[0] >= 0.5 )
{
pts->SetId(0,this->PointIds->GetId(1));
if ( pcoords[0] > 1.0 )
{
return 0;
}
else
{
return 1;
}
}
else
{
pts->SetId(0,this->PointIds->GetId(0));
if ( pcoords[0] < 0.0 )
{
return 0;
}
else
{
return 1;
}
}
}
//----------------------------------------------------------------------------
//
// marching lines case table
//
typedef int VERT_LIST;
typedef struct {
VERT_LIST verts[2];
} VERT_CASES;
static VERT_CASES vertCases[4]= {
{{-1,-1}},
{{1,0}},
{{0,1}},
{{-1,-1}}};
//----------------------------------------------------------------------------
void vtkLine::Contour(double value, vtkDataArray *cellScalars,
vtkIncrementalPointLocator *locator, vtkCellArray *verts,
vtkCellArray *vtkNotUsed(lines),
vtkCellArray *vtkNotUsed(polys),
vtkPointData *inPd, vtkPointData *outPd,
vtkCellData *inCd, vtkIdType cellId, vtkCellData *outCd)
{
static int CASE_MASK[2] = {1,2};
int index, i, newCellId;
VERT_CASES *vertCase;
VERT_LIST *vert;
double t, x[3], x1[3], x2[3];
vtkIdType pts[1];
//
// Build the case table
//
for ( i=0, index = 0; i < 2; i++)
{
if (cellScalars->GetComponent(i,0) >= value)
{
index |= CASE_MASK[i];
}
}
vertCase = vertCases + index;
vert = vertCase->verts;
if ( vert[0] > -1 )
{
t = (value - cellScalars->GetComponent(vert[0],0)) /
(cellScalars->GetComponent(vert[1],0) -
cellScalars->GetComponent(vert[0],0));
this->Points->GetPoint(vert[0], x1);
this->Points->GetPoint(vert[1], x2);
for (i=0; i<3; i++)
{
x[i] = x1[i] + t * (x2[i] - x1[i]);
}
if ( locator->InsertUniquePoint(x, pts[0]) )
{
if ( outPd )
{
vtkIdType p1 = this->PointIds->GetId(vert[0]);
vtkIdType p2 = this->PointIds->GetId(vert[1]);
outPd->InterpolateEdge(inPd,pts[0],p1,p2,t);
}
}
newCellId = verts->InsertNextCell(1,pts);
outCd->CopyData(inCd,cellId,newCellId);
}
}
//----------------------------------------------------------------------------
double vtkLine::DistanceBetweenLines(
double l0[3], double l1[3], // line 1
double m0[3], double m1[3], // line 2
double closestPt1[3], double closestPt2[3], // closest points
double &t1, double &t2 ) // parametric coords of the closest points
{
// Part of this function was adapted from "GeometryAlgorithms.com"
//
// Copyright 2001, softSurfer (www.softsurfer.com)
// This code may be freely used and modified for any purpose
// providing that this copyright notice is included with it.
// SoftSurfer makes no warranty for this code, and cannot be held
// liable for any real or imagined damage resulting from its use.
// Users of this code must verify correctness for their application.
const double u[3] = { l1[0]-l0[0], l1[1]-l0[1], l1[2]-l0[2] };
const double v[3] = { m1[0]-m0[0], m1[1]-m0[1], m1[2]-m0[2] };
const double w[3] = { l0[0]-m0[0], l0[1]-m0[1], l0[2]-m0[2] };
const double a = vtkMath::Dot(u,u);
const double b = vtkMath::Dot(u,v);
const double c = vtkMath::Dot(v,v); // always >= 0
const double d = vtkMath::Dot(u,w);
const double e = vtkMath::Dot(v,w);
const double D = a*c - b*b; // always >= 0
// compute the line parameters of the two closest points
if (D < 1e-6)
{ // the lines are almost parallel
t1 = 0.0;
t2 = (b > c ? d/b : e/c); // use the largest denominator
}
else
{
t1 = (b*e - c*d) / D;
t2 = (a*e - b*d) / D;
}
for (unsigned int i = 0; i < 3; i++)
{
closestPt1[i] = l0[i] + t1 * u[i];
closestPt2[i] = m0[i] + t2 * v[i];
}
// Return the distance squared between the lines =
// the mag squared of the distance between the two closest points
// = L1(t1) - L2(t2)
return vtkMath::Distance2BetweenPoints( closestPt1, closestPt2 );
}
//----------------------------------------------------------------------------
double vtkLine::DistanceBetweenLineSegments(
double l0[3], double l1[3], // line segment 1
double m0[3], double m1[3], // line segment 2
double closestPt1[3], double closestPt2[3], // closest points
double &t1, double &t2 ) // parametric coords
// of the closest points
{
// Part of this function was adapted from "GeometryAlgorithms.com"
//
// Copyright 2001, softSurfer (www.softsurfer.com)
// This code may be freely used and modified for any purpose
// providing that this copyright notice is included with it.
// SoftSurfer makes no warranty for this code, and cannot be held
// liable for any real or imagined damage resulting from its use.
// Users of this code must verify correctness for their application.
const double u[3] = { l1[0]-l0[0], l1[1]-l0[1], l1[2]-l0[2] };
const double v[3] = { m1[0]-m0[0], m1[1]-m0[1], m1[2]-m0[2] };
const double w[3] = { l0[0]-m0[0], l0[1]-m0[1], l0[2]-m0[2] };
const double a = vtkMath::Dot(u,u);
const double b = vtkMath::Dot(u,v);
const double c = vtkMath::Dot(v,v); // always >= 0
const double d = vtkMath::Dot(u,w);
const double e = vtkMath::Dot(v,w);
const double D = a*c - b*b; // always >= 0
double sN, sD = D; // sc = sN / sD, default sD = D >= 0
double tN, tD = D; // tc = tN / tD, default tD = D >= 0
// compute the line parameters of the two closest points
if (D < 1e-6)
{
// the lines are almost parallel. Where on the line is the closest point.
// First check if the lines overlap. If they do, then the distance is
// just the distance between the infinite lines..
double dist;
dist = vtkLine::DistanceToLine( l0, m0, m1, t2, closestPt2 );
if (t2 >= 0.0 && t2 <= 1.0)
{
t1 = 0.0;
closestPt1[0] = l0[0];
closestPt1[1] = l0[1];
closestPt1[2] = l0[2];
return dist;
}
dist = vtkLine::DistanceToLine( l1, m0, m1, t2, closestPt2 );
if (t2 >= 0.0 && t2 <= 1.0)
{
t1 = 1.0;
closestPt1[0] = l1[0];
closestPt1[1] = l1[1];
closestPt1[2] = l1[2];
return dist;
}
dist = vtkLine::DistanceToLine( m0, l0, l1, t1, closestPt1 );
if (t1 >= 0.0 && t1 <= 1.0)
{
t1 = 0.0;
closestPt2[0] = m0[0];
closestPt2[1] = m0[1];
closestPt2[2] = m0[2];
return dist;
}
dist = vtkLine::DistanceToLine( m1, l0, l1, t1, closestPt1 );
if (t1 >= 0.0 && t1 <= 1.0)
{
t1 = 1.0;
closestPt2[0] = m1[0];
closestPt2[1] = m1[1];
closestPt2[2] = m1[2];
return dist;
}
// The lines don't overlap.... The shortest distance is the min of the
// distance between the end points.
const double d1 = vtkMath::Distance2BetweenPoints( l0, m0 );
const double d2 = vtkMath::Distance2BetweenPoints( l0, m1 );
const double d3 = vtkMath::Distance2BetweenPoints( l1, m0 );
const double d4 = vtkMath::Distance2BetweenPoints( l1, m1 );
if (d1 <= d2 && d1 <= d3 && d1 <= d4)
{
t1 = t2 = 0.0;
closestPt1[0] = l0[0];
closestPt1[1] = l0[1];
closestPt1[2] = l0[2];
closestPt2[0] = m0[0];
closestPt2[1] = m0[1];
closestPt2[2] = m0[2];
return d1;
}
if (d2 <= d1 && d2 <= d3 && d2 <= d4)
{
t1 = 0.0; t2 = 1.0;
closestPt1[0] = l0[0];
closestPt1[1] = l0[1];
closestPt1[2] = l0[2];
closestPt2[0] = m1[0];
closestPt2[1] = m1[1];
closestPt2[2] = m1[2];
return d2;
}
if (d3 <= d1 && d3 <= d2 && d3 <= d4)
{
t1 = 1.0; t2 = 0.0;
closestPt1[0] = l1[0];
closestPt1[1] = l1[1];
closestPt1[2] = l1[2];
closestPt2[0] = m0[0];
closestPt2[1] = m0[1];
closestPt2[2] = m0[2];
return d3;
}
if (d4 <= d1 && d4 <= d2 && d4 <= d3)
{
t1 = t2 = 1.0;
closestPt1[0] = l1[0];
closestPt1[1] = l1[1];
closestPt1[2] = l1[2];
closestPt2[0] = m1[0];
closestPt2[1] = m1[1];
closestPt2[2] = m1[2];
return d4;
}
// Can never get here.
return 0.0;
}
// The lines aren't parallel.
else
{ // get the closest points on the infinite lines
sN = (b*e - c*d);
tN = (a*e - b*d);
if (sN < 0.0)
{ // sc < 0 => the s=0 edge is visible
sN = 0.0;
tN = e;
tD = c;
}
else if (sN > sD)
{ // sc > 1 => the s=1 edge is visible
sN = sD;
tN = e + b;
tD = c;
}
}
if (tN < 0.0)
{ // tc < 0 => the t=0 edge is visible
tN = 0.0;
// recompute sc for this edge
if (-d < 0.0)
{
sN = 0.0;
}
else if (-d > a)
{
sN = sD;
}
else
{
sN = -d;
sD = a;
}
}
else if (tN > tD)
{ // tc > 1 => the t=1 edge is visible
tN = tD;
// recompute sc for this edge
if ((-d + b) < 0.0)
{
sN = 0;
}
else if ((-d + b) > a)
{
sN = sD;
}
else
{
sN = (-d + b);
sD = a;
}
}
// finally do the division to get sc and tc
t1 = (fabs(sN) < 1e-6 ? 0.0 : sN / sD);
t2 = (fabs(tN) < 1e-6 ? 0.0 : tN / tD);
// Closest Point on segment1 = S1(t1) = l0 + t1*u
// Closest Point on segment2 = S1(t2) = m0 + t2*v
for (unsigned int i = 0; i < 3; i++)
{
closestPt1[i] = l0[i] + t1 * u[i];
closestPt2[i] = m0[i] + t2 * v[i];
}
// Return the distance squared between the lines =
// the mag squared of the distance between the two closest points
// = S1(t1) - S2(t2)
return vtkMath::Distance2BetweenPoints( closestPt1, closestPt2 );
}
//----------------------------------------------------------------------------
// Compute distance to finite line. Returns parametric coordinate t
// and point location on line.
double vtkLine::DistanceToLine(double x[3], double p1[3], double p2[3],
double &t, double closestPoint[3])
{
double p21[3], denom, num;
double *closest;
double tolerance;
//
// Determine appropriate vectors
//
p21[0] = p2[0]- p1[0];
p21[1] = p2[1]- p1[1];
p21[2] = p2[2]- p1[2];
//
// Get parametric location
//
num = p21[0]*(x[0]-p1[0]) + p21[1]*(x[1]-p1[1]) + p21[2]*(x[2]-p1[2]);
denom = vtkMath::Dot(p21,p21);
// trying to avoid an expensive fabs
tolerance = VTK_TOL*num;
if (tolerance < 0.0)
{
tolerance = -tolerance;
}
if ( -tolerance < denom && denom < tolerance ) //numerically bad!
{
closest = p1; //arbitrary, point is (numerically) far away
}
//
// If parametric coordinate is within 0<=p<=1, then the point is closest to
// the line. Otherwise, it's closest to a point at the end of the line.
//
else if ( denom <= 0.0 || (t=num/denom) < 0.0 )
{
closest = p1;
}
else if ( t > 1.0 )
{
closest = p2;
}
else
{
closest = p21;
p21[0] = p1[0] + t*p21[0];
p21[1] = p1[1] + t*p21[1];
p21[2] = p1[2] + t*p21[2];
}
closestPoint[0] = closest[0];
closestPoint[1] = closest[1];
closestPoint[2] = closest[2];
return vtkMath::Distance2BetweenPoints(closest,x);
}
//----------------------------------------------------------------------------
//
// Determine the distance of the current vertex to the edge defined by
// the vertices provided. Returns distance squared. Note: line is assumed
// infinite in extent.
//
double vtkLine::DistanceToLine (double x[3], double p1[3], double p2[3])
{
int i;
double np1[3], p1p2[3], proj, den;
for (i=0; i<3; i++)
{
np1[i] = x[i] - p1[i];
p1p2[i] = p1[i] - p2[i];
}
if ( (den=vtkMath::Norm(p1p2)) != 0.0 )
{
for (i=0; i<3; i++)
{
p1p2[i] /= den;
}
}
else
{
return vtkMath::Dot(np1,np1);
}
proj = vtkMath::Dot(np1,p1p2);
return (vtkMath::Dot(np1,np1) - proj*proj);
}
//----------------------------------------------------------------------------
// Line-line intersection. Intersection has to occur within [0,1] parametric
// coordinates and with specified tolerance.
int vtkLine::IntersectWithLine(double p1[3], double p2[3], double tol, double& t,
double x[3], double pcoords[3], int& subId)
{
double a1[3], a2[3];
double projXYZ[3];
int i;
subId = 0;
pcoords[1] = pcoords[2] = 0.0;
this->Points->GetPoint(0, a1);
this->Points->GetPoint(1, a2);
if ( this->Intersection(p1, p2, a1, a2, t, pcoords[0]) ==
VTK_YES_INTERSECTION )
{
// make sure we are within tolerance
for (i=0; i<3; i++)
{
x[i] = a1[i] + pcoords[0]*(a2[i]-a1[i]);
projXYZ[i] = p1[i] + t*(p2[i]-p1[i]);
}
if ( vtkMath::Distance2BetweenPoints(x,projXYZ) <= tol*tol )
{
return 1;
}
else
{
return 0;
}
}
else //check to see if it lies within tolerance
{
// one of the parametric coords must be outside 0-1
if (t < 0.0)
{
t = 0.0;
if (vtkLine::DistanceToLine(p1,a1,a2,pcoords[0],x) <= tol*tol)
{
return 1;
}
else
{
return 0;
}
}
if (t > 1.0)
{
t = 1.0;
if (vtkLine::DistanceToLine(p2,a1,a2,pcoords[0],x) <= tol*tol)
{
return 1;
}
else
{
return 0;
}
}
if (pcoords[0] < 0.0)
{
pcoords[0] = 0.0;
if (vtkLine::DistanceToLine(a1,p1,p2,t,x) <= tol*tol)
{
return 1;
}
else
{
return 0;
}
}
if (pcoords[0] > 1.0)
{
pcoords[0] = 1.0;
if (vtkLine::DistanceToLine(a2,p1,p2,t,x) <= tol*tol)
{
return 1;
}
else
{
return 0;
}
}
}
return 0;
}
//----------------------------------------------------------------------------
int vtkLine::Triangulate(int vtkNotUsed(index), vtkIdList *ptIds,
vtkPoints *pts)
{
pts->Reset();
ptIds->Reset();
ptIds->InsertId(0,this->PointIds->GetId(0));
pts->InsertPoint(0,this->Points->GetPoint(0));
ptIds->InsertId(1,this->PointIds->GetId(1));
pts->InsertPoint(1,this->Points->GetPoint(1));
return 1;
}
//----------------------------------------------------------------------------
void vtkLine::Derivatives(int vtkNotUsed(subId),
double vtkNotUsed(pcoords)[3],
double *values,
int dim, double *derivs)
{
double x0[3], x1[3], deltaX[3];
int i, j;
this->Points->GetPoint(0, x0);
this->Points->GetPoint(1, x1);
for (i=0; i<3; i++)
{
deltaX[i] = x1[i] - x0[i];
}
for (i=0; i<dim; i++)
{
for (j=0; j<3; j++)
{
if ( deltaX[j] != 0 )
{
derivs[3*i+j] = (values[2*i+1] - values[2*i]) / deltaX[j];
}
else
{
derivs[3*i+j] =0;
}
}
}
}
//----------------------------------------------------------------------------
// support line clipping
typedef int LINE_LIST;
typedef struct {
LINE_LIST lines[2];
} LINE_CASES;
static LINE_CASES lineCases[] = {
{{ -1, -1}}, // 0
{{100, 1}}, // 1
{{ 0, 101}}, // 2
{{100, 101}}}; // 3
// Clip this line using scalar value provided. Like contouring, except
// that it cuts the line to produce other lines.
void vtkLine::Clip(double value, vtkDataArray *cellScalars,
vtkIncrementalPointLocator *locator, vtkCellArray *lines,
vtkPointData *inPd, vtkPointData *outPd,
vtkCellData *inCd, vtkIdType cellId, vtkCellData *outCd,
int insideOut)
{
static int CASE_MASK[3] = {1,2};
LINE_CASES *lineCase;
int i, j, index, *vert, newCellId;
vtkIdType pts[3];
int vertexId;
double t, x1[3], x2[3], x[3];
// Build the case table
if ( insideOut )
{
for ( i=0, index = 0; i < 2; i++)
{
if (cellScalars->GetComponent(i,0) <= value)
{
index |= CASE_MASK[i];
}
}
}
else
{
for ( i=0, index = 0; i < 2; i++)
{
if (cellScalars->GetComponent(i,0) > value)
{
index |= CASE_MASK[i];
}
}
}
// Select the case based on the index and get the list of lines for this case
lineCase = lineCases + index;
vert = lineCase->lines;
// generate clipped line
if ( vert[0] > -1 )
{
for (i=0; i<2; i++) // insert line
{
// vertex exists, and need not be interpolated
if (vert[i] >= 100)
{
vertexId = vert[i] - 100;
this->Points->GetPoint(vertexId, x);
if ( locator->InsertUniquePoint(x, pts[i]) )
{
outPd->CopyData(inPd,this->PointIds->GetId(vertexId),pts[i]);
}
}
else //new vertex, interpolate
{
t = (value - cellScalars->GetComponent(0,0)) /
(cellScalars->GetComponent(1,0) - cellScalars->GetComponent(0,0));
this->Points->GetPoint(0, x1);
this->Points->GetPoint(1, x2);
for (j=0; j<3; j++)
{
x[j] = x1[j] + t * (x2[j] - x1[j]);
}
if ( locator->InsertUniquePoint(x, pts[i]) )
{
vtkIdType p1 = this->PointIds->GetId(0);
vtkIdType p2 = this->PointIds->GetId(1);
outPd->InterpolateEdge(inPd,pts[i],p1,p2,t);
}
}
}
// check for degenerate lines
if ( pts[0] != pts[1] )
{
newCellId = lines->InsertNextCell(2,pts);
outCd->CopyData(inCd,cellId,newCellId);
}
}
}
//----------------------------------------------------------------------------
//
// Compute interpolation functions
//
void vtkLine::InterpolationFunctions(double pcoords[3], double weights[2])
{
weights[0] = 1.0 - pcoords[0];
weights[1] = pcoords[0];
}
//----------------------------------------------------------------------------
void vtkLine::InterpolationDerivs(double pcoords[3], double derivs[2])
{
(void)pcoords;
derivs[0] = -1.0;
derivs[1] = 1;
}
//----------------------------------------------------------------------------
static double vtkLineCellPCoords[6] = {0.0,0.0,0.0, 1.0,0.0,0.0};
double *vtkLine::GetParametricCoords()
{
return vtkLineCellPCoords;
}
//----------------------------------------------------------------------------
void vtkLine::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
}