You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
504 lines
14 KiB
504 lines
14 KiB
2 years ago
|
/*=========================================================================
|
||
|
|
||
|
Program: Visualization Toolkit
|
||
|
Module: $RCSfile: vtkRuledSurfaceFilter.cxx,v $
|
||
|
|
||
|
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 "vtkRuledSurfaceFilter.h"
|
||
|
|
||
|
#include "vtkCellArray.h"
|
||
|
#include "vtkMath.h"
|
||
|
#include "vtkInformation.h"
|
||
|
#include "vtkInformationVector.h"
|
||
|
#include "vtkObjectFactory.h"
|
||
|
#include "vtkPointData.h"
|
||
|
#include "vtkPolyData.h"
|
||
|
#include "vtkPolyLine.h"
|
||
|
|
||
|
vtkCxxRevisionMacro(vtkRuledSurfaceFilter, "$Revision: 1.24 $");
|
||
|
vtkStandardNewMacro(vtkRuledSurfaceFilter);
|
||
|
|
||
|
vtkRuledSurfaceFilter::vtkRuledSurfaceFilter()
|
||
|
{
|
||
|
this->DistanceFactor = 3.0;
|
||
|
this->OnRatio = 1;
|
||
|
this->Offset = 0;
|
||
|
this->CloseSurface = 0;
|
||
|
this->RuledMode = VTK_RULED_MODE_RESAMPLE;
|
||
|
this->Resolution[0] = 1;
|
||
|
this->Resolution[1] = 1;
|
||
|
this->PassLines = 0;
|
||
|
|
||
|
this->Ids = vtkIdList::New();
|
||
|
this->Ids->SetNumberOfIds(4);
|
||
|
}
|
||
|
|
||
|
vtkRuledSurfaceFilter::~vtkRuledSurfaceFilter()
|
||
|
{
|
||
|
this->Ids->Delete();
|
||
|
}
|
||
|
|
||
|
int vtkRuledSurfaceFilter::RequestData(
|
||
|
vtkInformation *vtkNotUsed(request),
|
||
|
vtkInformationVector **inputVector,
|
||
|
vtkInformationVector *outputVector)
|
||
|
{
|
||
|
// get the info objects
|
||
|
vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
|
||
|
vtkInformation *outInfo = outputVector->GetInformationObject(0);
|
||
|
|
||
|
// get the input and ouptut
|
||
|
vtkPolyData *input = vtkPolyData::SafeDownCast(
|
||
|
inInfo->Get(vtkDataObject::DATA_OBJECT()));
|
||
|
vtkPolyData *output = vtkPolyData::SafeDownCast(
|
||
|
outInfo->Get(vtkDataObject::DATA_OBJECT()));
|
||
|
|
||
|
vtkPoints *inPts, *newPts = NULL;
|
||
|
vtkIdType i, numPts, numLines;
|
||
|
vtkCellArray *inLines, *newPolys, *newStrips;
|
||
|
vtkIdType *pts = 0;
|
||
|
vtkIdType *pts2 = 0;
|
||
|
vtkIdType npts = 0;
|
||
|
vtkIdType npts2 = 0;
|
||
|
vtkPointData *inPD=input->GetPointData(), *outPD=output->GetPointData();
|
||
|
|
||
|
// Check input, pass data if requested
|
||
|
//
|
||
|
vtkDebugMacro(<<"Creating a ruled surface");
|
||
|
|
||
|
inPts = input->GetPoints();
|
||
|
inLines = input->GetLines();
|
||
|
if ( !inPts || !inLines)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
numLines=inLines->GetNumberOfCells();
|
||
|
numPts = inPts->GetNumberOfPoints();
|
||
|
if (numPts < 1 || numLines < 2 )
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if ( this->PassLines )
|
||
|
{
|
||
|
output->SetLines(inLines);
|
||
|
}
|
||
|
|
||
|
if ( this->RuledMode == VTK_RULED_MODE_RESAMPLE ) //generating new points
|
||
|
{
|
||
|
newPts = vtkPoints::New();
|
||
|
output->SetPoints(newPts);
|
||
|
outPD->InterpolateAllocate(inPD,numPts);
|
||
|
if ( this->PassLines ) //need to copy input points
|
||
|
{
|
||
|
newPts->DeepCopy(inPts);
|
||
|
for (i=0; i<numPts; i++)
|
||
|
{
|
||
|
outPD->CopyData(inPD,i,i);
|
||
|
}
|
||
|
}
|
||
|
newPts->Delete();
|
||
|
newStrips = vtkCellArray::New();
|
||
|
newStrips->Allocate(
|
||
|
2*(this->Resolution[1]+1)*this->Resolution[0]*(numLines-1));
|
||
|
output->SetStrips(newStrips);
|
||
|
newStrips->Delete();
|
||
|
}
|
||
|
else //using original points
|
||
|
{
|
||
|
output->SetPoints(inPts);
|
||
|
output->GetPointData()->PassData(input->GetPointData());
|
||
|
newPolys = vtkCellArray::New();
|
||
|
newPolys->Allocate(2*numPts);
|
||
|
output->SetPolys(newPolys);
|
||
|
newPolys->Delete();
|
||
|
}
|
||
|
|
||
|
// For each pair of lines (as selected by Offset and OnRatio), create a
|
||
|
// stripe (a ruled surfac between two lines).
|
||
|
//
|
||
|
inLines->InitTraversal();
|
||
|
inLines->GetNextCell(npts,pts);
|
||
|
for (i=0; i<numLines; i++)
|
||
|
{
|
||
|
//abort/progress methods
|
||
|
this->UpdateProgress ((double)i/numLines);
|
||
|
if (this->GetAbortExecute())
|
||
|
{
|
||
|
break; //out of line loop
|
||
|
}
|
||
|
|
||
|
inLines->GetNextCell(npts2,pts2); //get the next edge
|
||
|
|
||
|
// Determine whether this stripe should be generated
|
||
|
if ( (i-this->Offset) >= 0 && !((i - this->Offset ) % this->OnRatio) &&
|
||
|
npts >= 2 && npts2 >= 2)
|
||
|
{
|
||
|
switch (this->RuledMode)
|
||
|
{
|
||
|
case VTK_RULED_MODE_RESAMPLE:
|
||
|
this->Resample(output, input, inPts, newPts, npts, pts, npts2, pts2);
|
||
|
break;
|
||
|
case VTK_RULED_MODE_POINT_WALK:
|
||
|
this->PointWalk(output, inPts, npts, pts, npts2, pts2);
|
||
|
break;
|
||
|
}//switch
|
||
|
}//generate this stripe
|
||
|
|
||
|
//Get the next line for generating the next stripe
|
||
|
npts = npts2;
|
||
|
pts = pts2;
|
||
|
if ( i == (numLines-2) )
|
||
|
{
|
||
|
if ( this->CloseSurface )
|
||
|
{
|
||
|
inLines->InitTraversal();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
i++; //will cause the loop to end
|
||
|
}
|
||
|
}//add far boundary of surface
|
||
|
}//for all selected line pairs
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
void vtkRuledSurfaceFilter::Resample(vtkPolyData *output, vtkPolyData *input,
|
||
|
vtkPoints *inPts, vtkPoints *newPts,
|
||
|
int npts, vtkIdType *pts,
|
||
|
int npts2, vtkIdType *pts2)
|
||
|
{
|
||
|
vtkIdType offset, id;
|
||
|
int i, j;
|
||
|
double length, length2;
|
||
|
vtkCellArray *newStrips;
|
||
|
vtkPointData *inPD=input->GetPointData();
|
||
|
vtkPointData *outPD=output->GetPointData();
|
||
|
double v, uu, vv;
|
||
|
double s, t;
|
||
|
double deltaV;
|
||
|
double deltaS, deltaT;
|
||
|
double d0, d1, l0, l1;
|
||
|
double pt[3], pt0[3], pt1[3], pt00[3], pt01[3], pt10[3], pt11[3];
|
||
|
int i00, i01, i10, i11;
|
||
|
|
||
|
if (this->Resolution[0] < 1)
|
||
|
{
|
||
|
vtkErrorMacro(<< "Resolution[0] must be greater than 0");
|
||
|
return;
|
||
|
}
|
||
|
if (this->Resolution[1] < 1)
|
||
|
{
|
||
|
vtkErrorMacro(<< "Resolution[1] must be greater than 0");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Measure the length of each boundary line
|
||
|
//
|
||
|
// first line
|
||
|
length = 0.0;
|
||
|
for (i=0; i < npts-1; i++)
|
||
|
{
|
||
|
inPts->GetPoint(pts[i], pt00);
|
||
|
inPts->GetPoint(pts[i+1], pt01);
|
||
|
length += sqrt(vtkMath::Distance2BetweenPoints(pt00, pt01));
|
||
|
}
|
||
|
|
||
|
// second line
|
||
|
length2 = 0.0;
|
||
|
for (i=0; i < npts2-1; i++)
|
||
|
{
|
||
|
inPts->GetPoint(pts2[i], pt00);
|
||
|
inPts->GetPoint(pts2[i+1], pt01);
|
||
|
length2 += sqrt(vtkMath::Distance2BetweenPoints(pt00, pt01));
|
||
|
}
|
||
|
|
||
|
// Create the ruled surface as a set of triangle strips. Allocate
|
||
|
// additional memory for new points.
|
||
|
//
|
||
|
// forces allocation so that SetPoint() can be safely used
|
||
|
offset = newPts->GetNumberOfPoints();
|
||
|
newPts->InsertPoint(offset+(this->Resolution[0]+1)*(this->Resolution[1]+1)-1,
|
||
|
0.0, 0.0, 0.0);
|
||
|
newStrips = output->GetStrips();
|
||
|
|
||
|
// We'll construct the points for the ruled surface in column major order,
|
||
|
// i.e. all the points between the first point of the two polylines.
|
||
|
for (i=0; i < this->Resolution[0]; i++)
|
||
|
{
|
||
|
newStrips->InsertNextCell( 2*(this->Resolution[1]+1) );
|
||
|
for (j=0; j < this->Resolution[1] + 1; j++)
|
||
|
{
|
||
|
newStrips->InsertCellPoint(offset + i*(this->Resolution[1]+1)+j);
|
||
|
newStrips->InsertCellPoint(offset + (i+1)*(this->Resolution[1]+1)+j);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now, compute all the points.
|
||
|
//
|
||
|
// parametric delta
|
||
|
deltaV = 1.0 / (double) (this->Resolution[1]);
|
||
|
|
||
|
// arc-length deltas
|
||
|
deltaS = length / (double) (this->Resolution[0]);
|
||
|
deltaT = length2 / (double) (this->Resolution[0]);
|
||
|
|
||
|
t = 0.0;
|
||
|
d0 = d1 = 0.0;
|
||
|
l0 = l1 = 0.0;
|
||
|
i00 = 0;
|
||
|
i01 = 1;
|
||
|
i10 = 0;
|
||
|
i11 = 1;
|
||
|
|
||
|
inPts->GetPoint(pts[0], pt00);
|
||
|
inPts->GetPoint(pts[1], pt01);
|
||
|
inPts->GetPoint(pts2[0], pt10);
|
||
|
inPts->GetPoint(pts2[1], pt11);
|
||
|
|
||
|
for (i=0; i < this->Resolution[0]+1; i++)
|
||
|
{
|
||
|
// compute the end points a rule, one point from the first polyline,
|
||
|
// one point from the second line
|
||
|
s = i*deltaS;
|
||
|
t = i*deltaT;
|
||
|
|
||
|
// find for the interval containing s
|
||
|
while (s > l0 && i00 < (npts - 1))
|
||
|
{
|
||
|
inPts->GetPoint(pts[i00], pt00);
|
||
|
inPts->GetPoint(pts[i01], pt01);
|
||
|
d0 = sqrt(vtkMath::Distance2BetweenPoints(pt00, pt01));
|
||
|
// doubleing point discrepancy: sgi needs the following test to be
|
||
|
// s <= length while win32 needs it to be s < length. We account
|
||
|
// for this by using the <= test here and adjusting the maximum parameter
|
||
|
// value below (see #1)
|
||
|
if ((s > l0 + d0) && (s <= length))
|
||
|
{
|
||
|
// s's interval is still to the right
|
||
|
l0 += d0;
|
||
|
i00++;
|
||
|
i01++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// found the correct interval
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// compute the point at s on the first polyline
|
||
|
if (i01 > (npts - 1))
|
||
|
{
|
||
|
i00--;
|
||
|
i01--;
|
||
|
}
|
||
|
this->Ids->SetId(0,pts[i00]);
|
||
|
this->Ids->SetId(1,pts[i01]);
|
||
|
if (d0 == 0.0)
|
||
|
{
|
||
|
uu = 0.0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
uu = (s - l0) / d0;
|
||
|
}
|
||
|
// #1: fix to address the win32/sgi doubleing point differences
|
||
|
if (s >= length)
|
||
|
{
|
||
|
uu = 1.0;
|
||
|
}
|
||
|
pt0[0] = (1.0-uu) * pt00[0] + uu * pt01[0];
|
||
|
pt0[1] = (1.0-uu) * pt00[1] + uu * pt01[1];
|
||
|
pt0[2] = (1.0-uu) * pt00[2] + uu * pt01[2];
|
||
|
|
||
|
// find for the interval containing t
|
||
|
while (t > l1 && i10 < (npts2 - 1))
|
||
|
{
|
||
|
inPts->GetPoint(pts2[i10], pt10);
|
||
|
inPts->GetPoint(pts2[i11], pt11);
|
||
|
d1 = sqrt(vtkMath::Distance2BetweenPoints(pt10, pt11));
|
||
|
// doubleing point discrepancy: sgi needs the following test to be
|
||
|
// t <= length2 while win32 needs it to be t < length2. We account
|
||
|
// for this by using the <= test here and adjusting the maximum parameter
|
||
|
// value below (see #1)
|
||
|
if ((t > l1 + d1) && (t <= length2))
|
||
|
{
|
||
|
// t's interval is still to the right
|
||
|
l1 += d1;
|
||
|
i10++;
|
||
|
i11++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// found the correct interval
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// compute the point at t on the second polyline
|
||
|
if (i11 > npts2 - 1)
|
||
|
{
|
||
|
i10--;
|
||
|
i11--;
|
||
|
}
|
||
|
this->Ids->SetId(2,pts2[i10]);
|
||
|
this->Ids->SetId(3,pts2[i11]);
|
||
|
if (d1 == 0.0)
|
||
|
{
|
||
|
vv = 0.0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vv = (t - l1) / d1;
|
||
|
}
|
||
|
// #1: fix to address the win32/sgi doubleing point differences
|
||
|
if (t >= length2)
|
||
|
{
|
||
|
vv = 1.0;
|
||
|
}
|
||
|
pt1[0] = (1.0-vv) * pt10[0] + vv * pt11[0];
|
||
|
pt1[1] = (1.0-vv) * pt10[1] + vv * pt11[1];
|
||
|
pt1[2] = (1.0-vv) * pt10[2] + vv * pt11[2];
|
||
|
|
||
|
// Now, compute the points along the rule
|
||
|
for (j=0; j < this->Resolution[1]+1; j++)
|
||
|
{
|
||
|
v = j*deltaV;
|
||
|
pt[0] = (1.0 - v) * pt0[0] + v * pt1[0];
|
||
|
pt[1] = (1.0 - v) * pt0[1] + v * pt1[1];
|
||
|
pt[2] = (1.0 - v) * pt0[2] + v * pt1[2];
|
||
|
|
||
|
id = offset + i*(this->Resolution[1] + 1) + j;
|
||
|
newPts->SetPoint(id, pt);
|
||
|
this->Weights[0] = (1.0 - v)*(1.0 - uu);
|
||
|
this->Weights[1] = (1.0 - v)*uu;
|
||
|
this->Weights[2] = v*(1.0 - vv);
|
||
|
this->Weights[3] = v*vv;
|
||
|
outPD->InterpolatePoint(inPD, id, this->Ids, this->Weights);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void vtkRuledSurfaceFilter::PointWalk(vtkPolyData *output, vtkPoints *inPts,
|
||
|
int npts, vtkIdType *pts,
|
||
|
int npts2, vtkIdType *pts2)
|
||
|
{
|
||
|
int loc, loc2;
|
||
|
vtkCellArray *newPolys=output->GetPolys();
|
||
|
double x[3], y[3], a[3], b[3], xa, xb, ya, distance2;
|
||
|
|
||
|
// Compute distance factor based on first two points
|
||
|
//
|
||
|
inPts->GetPoint(pts[0],x);
|
||
|
inPts->GetPoint(pts2[0],y);
|
||
|
distance2 = vtkMath::Distance2BetweenPoints(x,y) *
|
||
|
this->DistanceFactor * this->DistanceFactor;
|
||
|
|
||
|
// Walk "edge" along the two lines maintaining closest distance
|
||
|
// and generating triangles as we go.
|
||
|
loc = loc2 = 0;
|
||
|
while ( loc < (npts-1) || loc2 < (npts2-1) )
|
||
|
{
|
||
|
if ( loc >= (npts-1) ) //clamped at end of first line
|
||
|
{
|
||
|
inPts->GetPoint(pts[loc],x);
|
||
|
inPts->GetPoint(pts2[loc2],a);
|
||
|
inPts->GetPoint(pts2[loc2+1],b);
|
||
|
xa = vtkMath::Distance2BetweenPoints(x,a);
|
||
|
xb = vtkMath::Distance2BetweenPoints(x,b);
|
||
|
if ( xa <= distance2 && xb <= distance2 )
|
||
|
{
|
||
|
newPolys->InsertNextCell(3);
|
||
|
newPolys->InsertCellPoint(pts[loc]); //x
|
||
|
newPolys->InsertCellPoint(pts2[loc2+1]); //b
|
||
|
newPolys->InsertCellPoint(pts2[loc2]); //a
|
||
|
}
|
||
|
loc2++;
|
||
|
}
|
||
|
else if ( loc2 >= (npts2-1) ) //clamped at end of second line
|
||
|
{
|
||
|
inPts->GetPoint(pts[loc],x);
|
||
|
inPts->GetPoint(pts[loc+1],y);
|
||
|
inPts->GetPoint(pts2[loc2],a);
|
||
|
xa = vtkMath::Distance2BetweenPoints(x,a);
|
||
|
ya = vtkMath::Distance2BetweenPoints(y,a);
|
||
|
if ( xa <= distance2 && ya <= distance2 )
|
||
|
{
|
||
|
newPolys->InsertNextCell(3);
|
||
|
newPolys->InsertCellPoint(pts[loc]); //x
|
||
|
newPolys->InsertCellPoint(pts[loc+1]); //y
|
||
|
newPolys->InsertCellPoint(pts2[loc2]); //a
|
||
|
}
|
||
|
loc++;
|
||
|
}
|
||
|
else //not at either end
|
||
|
{
|
||
|
inPts->GetPoint(pts[loc],x);
|
||
|
inPts->GetPoint(pts[loc+1],y);
|
||
|
inPts->GetPoint(pts2[loc2],a);
|
||
|
inPts->GetPoint(pts2[loc2+1],b);
|
||
|
xa = vtkMath::Distance2BetweenPoints(x,a);
|
||
|
xb = vtkMath::Distance2BetweenPoints(x,b);
|
||
|
ya = vtkMath::Distance2BetweenPoints(a,y);
|
||
|
if ( xb <= ya )
|
||
|
{
|
||
|
if ( xb <= distance2 && xa <= distance2 )
|
||
|
{
|
||
|
newPolys->InsertNextCell(3);
|
||
|
newPolys->InsertCellPoint(pts[loc]); //x
|
||
|
newPolys->InsertCellPoint(pts2[loc2+1]); //b
|
||
|
newPolys->InsertCellPoint(pts2[loc2]); //a
|
||
|
}
|
||
|
loc2++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( ya <= distance2 && xa <= distance2 )
|
||
|
{
|
||
|
newPolys->InsertNextCell(3);
|
||
|
newPolys->InsertCellPoint(pts[loc]); //x
|
||
|
newPolys->InsertCellPoint(pts[loc+1]); //y
|
||
|
newPolys->InsertCellPoint(pts2[loc2]); //a
|
||
|
}
|
||
|
loc++;
|
||
|
}
|
||
|
}//where in the lines
|
||
|
}//while still building the stripe
|
||
|
}
|
||
|
|
||
|
const char *vtkRuledSurfaceFilter::GetRuledModeAsString(void)
|
||
|
{
|
||
|
if ( this->RuledMode == VTK_RULED_MODE_RESAMPLE )
|
||
|
{
|
||
|
return "Resample";
|
||
|
}
|
||
|
else //if ( this->RuledMode == VTK_RULED_MODE_POINT_WALK )
|
||
|
{
|
||
|
return "PointWalk";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void vtkRuledSurfaceFilter::PrintSelf(ostream& os, vtkIndent indent)
|
||
|
{
|
||
|
this->Superclass::PrintSelf(os,indent);
|
||
|
|
||
|
os << indent << "Distance Factor: " << this->DistanceFactor << "\n";
|
||
|
os << indent << "On Ratio: " << this->OnRatio << "\n";
|
||
|
os << indent << "Offset: " << this->Offset << "\n";
|
||
|
os << indent << "Close Surface: " << (this->CloseSurface ? "On\n" : "Off\n");
|
||
|
os << indent << "Ruled Mode: " << this->GetRuledModeAsString() << "\n";
|
||
|
os << indent << "Resolution: (" << this->Resolution[0]
|
||
|
<< ", " << this->Resolution[1] << ")" << endl;
|
||
|
os << indent << "Pass Lines: " << (this->PassLines ? "On\n" : "Off\n");
|
||
|
}
|
||
|
|