|
|
/*=========================================================================
|
|
|
|
|
|
Program: Visualization Toolkit
|
|
|
Module: $RCSfile: vtkUnstructuredGridVolumeZSweepMapper.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 "vtkUnstructuredGridVolumeZSweepMapper.h"
|
|
|
|
|
|
#include "vtkObjectFactory.h"
|
|
|
#include "vtkUnstructuredGrid.h"
|
|
|
#include "vtkTimerLog.h"
|
|
|
#include "vtkVolume.h"
|
|
|
#include "vtkRenderer.h"
|
|
|
#include "vtkRenderWindow.h"
|
|
|
#include "vtkRayCastImageDisplayHelper.h"
|
|
|
#include "vtkTransform.h"
|
|
|
#include "vtkCamera.h"
|
|
|
#include "vtkGenericCell.h"
|
|
|
#include "vtkPriorityQueue.h"
|
|
|
#include "vtkIdList.h"
|
|
|
#include "vtkVolumeProperty.h"
|
|
|
#include "vtkColorTransferFunction.h"
|
|
|
#include "vtkPiecewiseFunction.h"
|
|
|
#include "vtkUnstructuredGridPreIntegration.h"
|
|
|
#include "vtkUnstructuredGridPartialPreIntegration.h"
|
|
|
#include "vtkUnstructuredGridHomogeneousRayIntegrator.h"
|
|
|
#include "vtkDoubleArray.h"
|
|
|
#include "vtkDataArray.h"
|
|
|
|
|
|
#include "vtkPolyData.h"
|
|
|
#include "vtkCellArray.h"
|
|
|
#include "vtkXMLPolyDataWriter.h"
|
|
|
#include "vtkPointData.h"
|
|
|
|
|
|
#include <assert.h>
|
|
|
#include <string.h> // memset()
|
|
|
#include <vtkstd/vector>
|
|
|
#include <vtkstd/list>
|
|
|
|
|
|
// do not remove the following line:
|
|
|
//#define BACK_TO_FRONT
|
|
|
|
|
|
enum
|
|
|
{
|
|
|
VTK_VALUES_X_INDEX=0, // world coordinate
|
|
|
VTK_VALUES_Y_INDEX, // world coordinate
|
|
|
VTK_VALUES_Z_INDEX, // world coordinate
|
|
|
VTK_VALUES_SCALAR_INDEX,
|
|
|
VTK_VALUES_SIZE // size of a value array
|
|
|
};
|
|
|
|
|
|
// Internal classes
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Store the result of the scan conversion at some pixel.
|
|
|
class vtkPixelListEntry
|
|
|
{
|
|
|
public:
|
|
|
vtkPixelListEntry()
|
|
|
{
|
|
|
}
|
|
|
|
|
|
void Init(double values[VTK_VALUES_SIZE],
|
|
|
double zView)
|
|
|
{
|
|
|
this->Zview=zView;
|
|
|
int i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Values[i]=values[i];
|
|
|
++i;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
// Return the interpolated values at this pixel.
|
|
|
double *GetValues() { return this->Values; }
|
|
|
// Return the interpolated z coordinate in view space at this pixel.
|
|
|
double GetZview() const { return this->Zview; }
|
|
|
|
|
|
vtkPixelListEntry *GetPrevious() { return this->Previous; }
|
|
|
vtkPixelListEntry *GetNext() { return this->Next; }
|
|
|
|
|
|
void SetPrevious(vtkPixelListEntry *e) { this->Previous=e; }
|
|
|
void SetNext(vtkPixelListEntry *e) { this->Next=e; }
|
|
|
|
|
|
protected:
|
|
|
double Values[VTK_VALUES_SIZE];
|
|
|
double Zview;
|
|
|
|
|
|
// List structure: both for the free block list (one-way) and any
|
|
|
// pixel list (two-way)
|
|
|
vtkPixelListEntry *Next;
|
|
|
// List structure: only for the pixel list (two-way)
|
|
|
vtkPixelListEntry *Previous;
|
|
|
private:
|
|
|
vtkPixelListEntry(const vtkPixelListEntry &other);
|
|
|
vtkPixelListEntry &operator=(const vtkPixelListEntry &other);
|
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Cache the projection of a vertex
|
|
|
class vtkVertexEntry
|
|
|
{
|
|
|
public:
|
|
|
vtkVertexEntry() {}
|
|
|
|
|
|
vtkVertexEntry(int screenX,
|
|
|
int screenY,
|
|
|
double xWorld,
|
|
|
double yWorld,
|
|
|
double zWorld,
|
|
|
double zView,
|
|
|
double scalar,
|
|
|
double invW)
|
|
|
:ScreenX(screenX),ScreenY(screenY),Zview(zView),InvW(invW)
|
|
|
{
|
|
|
this->Values[VTK_VALUES_X_INDEX]=xWorld;
|
|
|
this->Values[VTK_VALUES_Y_INDEX]=yWorld;
|
|
|
this->Values[VTK_VALUES_Z_INDEX]=zWorld;
|
|
|
this->Values[VTK_VALUES_SCALAR_INDEX]=scalar;
|
|
|
}
|
|
|
|
|
|
void Set(int screenX,
|
|
|
int screenY,
|
|
|
double xWorld,
|
|
|
double yWorld,
|
|
|
double zWorld,
|
|
|
double zView,
|
|
|
double scalar,
|
|
|
double invW)
|
|
|
{
|
|
|
this->ScreenX=screenX;
|
|
|
this->ScreenY=screenY;
|
|
|
this->Zview=zView;
|
|
|
this->Values[VTK_VALUES_X_INDEX]=xWorld;
|
|
|
this->Values[VTK_VALUES_Y_INDEX]=yWorld;
|
|
|
this->Values[VTK_VALUES_Z_INDEX]=zWorld;
|
|
|
this->Values[VTK_VALUES_SCALAR_INDEX]=scalar;
|
|
|
this->InvW=invW;
|
|
|
}
|
|
|
|
|
|
int GetScreenX()
|
|
|
{
|
|
|
return this->ScreenX;
|
|
|
}
|
|
|
int GetScreenY()
|
|
|
{
|
|
|
return this->ScreenY;
|
|
|
}
|
|
|
double *GetValues()
|
|
|
{
|
|
|
return this->Values;
|
|
|
}
|
|
|
double GetZview()
|
|
|
{
|
|
|
return this->Zview;
|
|
|
}
|
|
|
double GetInvW()
|
|
|
{
|
|
|
return this->InvW;
|
|
|
}
|
|
|
|
|
|
vtkVertexEntry(const vtkVertexEntry &vtkNotUsed(other)) {}
|
|
|
|
|
|
protected:
|
|
|
int ScreenX;
|
|
|
int ScreenY;
|
|
|
double Values[VTK_VALUES_SIZE];
|
|
|
double Zview;
|
|
|
double InvW;
|
|
|
|
|
|
private:
|
|
|
vtkVertexEntry &operator=(const vtkVertexEntry &other);
|
|
|
};
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Abstract interface for an edge of a triangle in the screen space.
|
|
|
// Used during scan-conversion.
|
|
|
class vtkScreenEdge
|
|
|
{
|
|
|
public:
|
|
|
// If the edge is a composite edge (top+bottom) switch to the bottom edge.
|
|
|
// Otherwise, do nothing.
|
|
|
virtual void OnBottom(int skipped, int y)
|
|
|
{
|
|
|
if(!skipped)
|
|
|
{
|
|
|
this->NextLine(y);
|
|
|
}
|
|
|
}
|
|
|
// Increment edge state to the next line.
|
|
|
virtual void NextLine(int y)=0;
|
|
|
// Increment edge state to the next deltaY line.
|
|
|
virtual void SkipLines(int deltaY,
|
|
|
int y)=0;
|
|
|
// Return the abscissa for the current line.
|
|
|
virtual int GetX()=0;
|
|
|
// Return the projected values for the current line. They are linearly
|
|
|
// incrementally interpolated in view space. The actual values are
|
|
|
// given by projectedValue/InvW. This is the way the values in world space
|
|
|
// are incrementally (not linearly) interpolated in view space.
|
|
|
virtual double *GetPValues()=0;
|
|
|
// Return 1/W, linearly interpolated in view space.
|
|
|
virtual double GetInvW()=0;
|
|
|
// Return Z in view coordinates, linearly interpolated in view space.
|
|
|
virtual double GetZview()=0;
|
|
|
protected:
|
|
|
// Destructor.
|
|
|
virtual ~vtkScreenEdge() {}
|
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Do an incremental traversing of an edge based on an Y increment.
|
|
|
enum
|
|
|
{
|
|
|
VTK_CASE_VERTICAL=0,
|
|
|
VTK_CASE_MOSTLY_VERTICAL,
|
|
|
VTK_CASE_DIAGONAL,
|
|
|
VTK_CASE_HORIZONTAL_BEGIN,
|
|
|
VTK_CASE_HORIZONTAL_END,
|
|
|
VTK_CASE_HORIZONTAL_MS, // most significant pixel
|
|
|
VTK_CASE_VERTICAL_IN_TO_OUT, // with edge equation
|
|
|
VTK_CASE_VERTICAL_OUT_TO_IN,
|
|
|
VTK_CASE_HORIZONTAL_IN_TO_OUT,
|
|
|
VTK_CASE_HORIZONTAL_OUT_TO_IN
|
|
|
};
|
|
|
|
|
|
// We use an edge equation as described in:
|
|
|
// Juan Pineda
|
|
|
// A Parallel Algorithm for Polygon Rasterization
|
|
|
// In Computer Graphics, Volume 22, Number 4, August 1988
|
|
|
// SIGGRAPH'88, Atlanta, August 1-5, 1988.
|
|
|
// pages 17--20.
|
|
|
|
|
|
// or in:
|
|
|
|
|
|
// Marc Olano and Trey Greer
|
|
|
// Triangle Scan Conversion using 2D Homogeneous Coordinates
|
|
|
// In 1997 SIGGRAPH/Eurographics Workshop
|
|
|
// pages 89--95.
|
|
|
// http://www.cs.unc.edu/~olano/papers/2dh-tri/2dh-tri.pdf
|
|
|
|
|
|
#define MOST_SIGNIFICANT
|
|
|
#define EDGE_EQUATION
|
|
|
#define HORI_EDGE_EQUATION
|
|
|
//#define STRICTLY_INSIDE
|
|
|
|
|
|
class vtkSimpleScreenEdge
|
|
|
: public vtkScreenEdge
|
|
|
{
|
|
|
public:
|
|
|
// Initialize the edge by the vertices v0 and v2 (ordered in y)
|
|
|
// `onRight' is true if the edge in on the right side of the triangle.
|
|
|
void Init(vtkVertexEntry *v0,
|
|
|
vtkVertexEntry *v2,
|
|
|
int dx20,
|
|
|
int dy20,
|
|
|
int onRight)
|
|
|
{
|
|
|
double z0=v0->GetZview();
|
|
|
double z2=v2->GetZview();
|
|
|
|
|
|
double invW0=v0->GetInvW();
|
|
|
double invW2=v2->GetInvW();
|
|
|
|
|
|
double pv0[VTK_VALUES_SIZE];
|
|
|
double pv2[VTK_VALUES_SIZE];
|
|
|
int i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
pv0[i]=v0->GetValues()[i]*invW0;
|
|
|
this->PValues[i]=pv0[i];
|
|
|
pv2[i]=v2->GetValues()[i]*invW2;
|
|
|
++i;
|
|
|
}
|
|
|
|
|
|
this->InvW=invW0;
|
|
|
this->Zview=z0;
|
|
|
|
|
|
int x0=v0->GetScreenX();
|
|
|
int x2=v2->GetScreenX();
|
|
|
|
|
|
this->X0=x0;
|
|
|
this->X2=x2;
|
|
|
|
|
|
this->V2=v2;
|
|
|
|
|
|
this->X=x0;
|
|
|
|
|
|
if(dx20==0)
|
|
|
{
|
|
|
this->Case=VTK_CASE_VERTICAL;
|
|
|
double invDy20=1.0/dy20;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDy20;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDy20;
|
|
|
this->Dz=(z2-z0)*invDy20;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(dx20>0)
|
|
|
{
|
|
|
this->IncX=1;
|
|
|
if(dx20>dy20)
|
|
|
{
|
|
|
// Mostly horizontal
|
|
|
#ifdef HORI_EDGE_EQUATION
|
|
|
if(onRight)
|
|
|
{
|
|
|
this->Case=VTK_CASE_HORIZONTAL_IN_TO_OUT;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->Case=VTK_CASE_HORIZONTAL_OUT_TO_IN;
|
|
|
}
|
|
|
this->Error=0;
|
|
|
this->SDy=dy20;
|
|
|
this->XStep=dx20/dy20; // integral division
|
|
|
this->Dx=dx20-this->XStep*this->SDy;
|
|
|
|
|
|
double invDy20=1.0/dy20;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDy20;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDy20;
|
|
|
this->Dz=(z2-z0)*invDy20;
|
|
|
#else
|
|
|
#ifdef MOST_SIGNIFICANT
|
|
|
this->Case=VTK_CASE_HORIZONTAL_MS;
|
|
|
this->XStep=dx20/dy20; // integral division
|
|
|
this->Dy=dy20;
|
|
|
this->Dy2=dy20<<1; // *2
|
|
|
this->Error=0;
|
|
|
this->ErrorStep=(dx20-this->XStep*dy20)<<1; // 2*r, dx=q*dy+r, r<dy
|
|
|
double invDx20=1.0/dx20;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDx20;
|
|
|
this->PValuesStep[i]=this->Dpv[i]*this->XStep;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDx20;
|
|
|
this->Dz=(z2-z0)*invDx20;
|
|
|
this->InvWStep=this->DinvW*this->XStep;
|
|
|
this->ZStep=this->Dz*this->XStep;
|
|
|
|
|
|
#else
|
|
|
if(!onRight)
|
|
|
{
|
|
|
this->Case=VTK_CASE_HORIZONTAL_BEGIN;
|
|
|
this->First=1;
|
|
|
this->Dy2=dy20<<1; // *2
|
|
|
this->Dx2=dx20<<1; // *2
|
|
|
this->Error=dx20;
|
|
|
|
|
|
this->XStep=dx20/dy20; // integral division
|
|
|
|
|
|
this->ErrorStep=this->XStep*this->Dy2;
|
|
|
|
|
|
double invDx20=1.0/dx20;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDx20;
|
|
|
this->PValuesStep[i]=this->Dpv[i]*this->XStep;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDx20;
|
|
|
this->Dz=(z2-z0)*invDx20;
|
|
|
this->InvWStep=this->DinvW*this->XStep;
|
|
|
this->ZStep=this->Dz*this->XStep;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->Case=VTK_CASE_HORIZONTAL_END;
|
|
|
|
|
|
this->InvW2=invW2;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues2[i]=pv2[i];
|
|
|
++i;
|
|
|
}
|
|
|
|
|
|
this->Zview2=z2;
|
|
|
|
|
|
this->Dy2=dy20<<1; // *2
|
|
|
this->Dx2=dx20<<1; // *2
|
|
|
this->Error=dx20;
|
|
|
this->XStep=dx20/dy20;
|
|
|
this->ErrorStep=this->XStep*this->Dy2;
|
|
|
|
|
|
double invDx20=1.0/dx20;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDx20;
|
|
|
this->PValuesStep[i]=this->Dpv[i]*this->XStep;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDx20;
|
|
|
this->Dz=(z2-z0)*invDx20;
|
|
|
this->InvWStep=this->DinvW*this->XStep;
|
|
|
this->ZStep=this->Dz*this->XStep;
|
|
|
|
|
|
while(this->Error<this->Dx2)
|
|
|
{
|
|
|
this->X+=this->IncX;
|
|
|
this->InvW+= this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
|
|
|
this->Error+=this->Dy2;
|
|
|
}
|
|
|
this->Error-=this->Dx2;
|
|
|
this->X-=this->IncX;
|
|
|
this->InvW-= this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]-=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview-=this->Dz;
|
|
|
}
|
|
|
#endif
|
|
|
#endif // EDGE_EQUATION
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(dx20==dy20)
|
|
|
{
|
|
|
this->Case=VTK_CASE_DIAGONAL;
|
|
|
|
|
|
double invDy20=1.0/dy20;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDy20;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDy20;
|
|
|
this->Dz=(z2-z0)*invDy20;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
#ifdef EDGE_EQUATION
|
|
|
if(onRight)
|
|
|
{
|
|
|
this->Case=VTK_CASE_VERTICAL_IN_TO_OUT;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->Case=VTK_CASE_VERTICAL_OUT_TO_IN;
|
|
|
}
|
|
|
this->Error=0;
|
|
|
this->SDy=dy20;
|
|
|
this->Dx=dx20;
|
|
|
|
|
|
double invDy20=1.0/dy20;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDy20;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDy20;
|
|
|
this->Dz=(z2-z0)*invDy20;
|
|
|
#else
|
|
|
this->Case=VTK_CASE_MOSTLY_VERTICAL;
|
|
|
this->Dx2=dx20<<1; // *2
|
|
|
this->Dy2=dy20<<1; // *2
|
|
|
this->Error=dy20;
|
|
|
|
|
|
double invDy20=1.0/dy20;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDy20;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDy20;
|
|
|
this->Dz=(z2-z0)*invDy20;
|
|
|
#endif
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->IncX=-1;
|
|
|
if(-dx20>dy20)
|
|
|
{
|
|
|
// Mostly horizontal
|
|
|
#ifdef HORI_EDGE_EQUATION
|
|
|
if(onRight)
|
|
|
{
|
|
|
this->Case=VTK_CASE_HORIZONTAL_OUT_TO_IN;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->Case=VTK_CASE_HORIZONTAL_IN_TO_OUT;
|
|
|
}
|
|
|
this->Error=0;
|
|
|
this->SDy=-dy20;
|
|
|
this->XStep=dx20/dy20; // integral division
|
|
|
this->Dx=dx20+this->XStep*this->SDy;
|
|
|
|
|
|
double invDy20=1.0/dy20;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDy20;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDy20;
|
|
|
this->Dz=(z2-z0)*invDy20;
|
|
|
#else
|
|
|
#ifdef MOST_SIGNIFICANT
|
|
|
this->Case=VTK_CASE_HORIZONTAL_MS;
|
|
|
this->XStep=dx20/dy20; // integral division
|
|
|
this->Dy=dy20;
|
|
|
this->Dy2=dy20<<1; // *2
|
|
|
this->Error=0;
|
|
|
this->ErrorStep=(dx20+this->XStep*dy20)<<1; // 2*r, dx=q*dy+r, r<dy
|
|
|
double invDx20=-1.0/dx20;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDx20;
|
|
|
this->PValuesStep[i]=-this->Dpv[i]*this->XStep;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDx20;
|
|
|
this->Dz=(z2-z0)*invDx20;
|
|
|
this->InvWStep=-this->DinvW*this->XStep;
|
|
|
this->ZStep=-this->Dz*this->XStep;
|
|
|
|
|
|
|
|
|
#else
|
|
|
if(onRight)
|
|
|
{
|
|
|
this->Case=VTK_CASE_HORIZONTAL_BEGIN;
|
|
|
this->First=1;
|
|
|
this->Dy2=dy20<<1; // *2
|
|
|
this->Dx2=(-dx20)<<1; // *2
|
|
|
this->Error=-dx20;
|
|
|
this->XStep=dx20/dy20;
|
|
|
this->ErrorStep=-this->XStep*this->Dy2;
|
|
|
|
|
|
double invDx20=-1.0/dx20;
|
|
|
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDx20;
|
|
|
this->PValuesStep[i]=-this->Dpv[i]*this->XStep;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDx20;
|
|
|
this->Dz=(z2-z0)*invDx20;
|
|
|
this->InvWStep=-this->DinvW*this->XStep;
|
|
|
this->ZStep=-this->Dz*this->XStep;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->Case=VTK_CASE_HORIZONTAL_END;
|
|
|
|
|
|
this->InvW2=invW2;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues2[i]=pv2[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview2=z2;
|
|
|
|
|
|
this->Dy2=dy20<<1; // *2
|
|
|
this->Dx2=(-dx20)<<1; // *2
|
|
|
this->Error=-dx20;
|
|
|
this->XStep=dx20/dy20;
|
|
|
this->ErrorStep=-this->XStep*this->Dy2;
|
|
|
|
|
|
double invDx20=-1.0/dx20;
|
|
|
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDx20;
|
|
|
this->PValuesStep[i]=-this->Dpv[i]*this->XStep;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDx20;
|
|
|
this->Dz=(z2-z0)*invDx20;
|
|
|
this->InvWStep=-this->DinvW*this->XStep;
|
|
|
this->ZStep=-this->Dz*this->XStep;
|
|
|
|
|
|
while(this->Error<this->Dx2)
|
|
|
{
|
|
|
this->X+=this->IncX;
|
|
|
this->InvW+= this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
|
|
|
this->Error+=this->Dy2;
|
|
|
}
|
|
|
this->Error-=this->Dx2;
|
|
|
this->X-=this->IncX;
|
|
|
|
|
|
this->InvW-= this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]-=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview-=this->Dz;
|
|
|
}
|
|
|
#endif
|
|
|
#endif // EDGE_EQUATION
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(dx20==-dy20)
|
|
|
{
|
|
|
this->Case=VTK_CASE_DIAGONAL;
|
|
|
|
|
|
double invDy20=1.0/dy20;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDy20;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDy20;
|
|
|
this->Dz=(z2-z0)*invDy20;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
#ifdef EDGE_EQUATION
|
|
|
if(onRight)
|
|
|
{
|
|
|
this->Case=VTK_CASE_VERTICAL_OUT_TO_IN;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->Case=VTK_CASE_VERTICAL_IN_TO_OUT;
|
|
|
}
|
|
|
this->Error=0;
|
|
|
this->SDy=-dy20;
|
|
|
this->Dx=dx20;
|
|
|
|
|
|
double invDy20=1.0/dy20;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDy20;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDy20;
|
|
|
this->Dz=(z2-z0)*invDy20;
|
|
|
#else
|
|
|
this->Case=VTK_CASE_MOSTLY_VERTICAL;
|
|
|
this->Dx2=(-dx20)<<1; // *2
|
|
|
this->Dy2=dy20<<1; // *2
|
|
|
this->Error=dy20;
|
|
|
|
|
|
double invDy20=1.0/dy20;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pv2[i]-pv0[i])*invDy20;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW2-invW0)*invDy20;
|
|
|
this->Dz=(z2-z0)*invDy20;
|
|
|
#endif
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Check that the current abscissa is in the range given by the vertices.
|
|
|
int ValidXRange()
|
|
|
{
|
|
|
if(this->X0<=this->X2)
|
|
|
{
|
|
|
return (this->X>=this->X0) && (this->X<=this->X2);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return (this->X>=this->X2) && (this->X<=this->X0);
|
|
|
}
|
|
|
}
|
|
|
int GetX()
|
|
|
{
|
|
|
// assert("pre: valid_range" && ValidXRange() );
|
|
|
return this->X;
|
|
|
}
|
|
|
double GetInvW() { return this->InvW; }
|
|
|
double *GetPValues() { return this->PValues; }
|
|
|
double GetZview() { return this->Zview; }
|
|
|
|
|
|
void NextLine(int y)
|
|
|
{
|
|
|
int i;
|
|
|
switch(this->Case)
|
|
|
{
|
|
|
case VTK_CASE_VERTICAL:
|
|
|
// nothing to do with X
|
|
|
this->InvW+=this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
break;
|
|
|
case VTK_CASE_DIAGONAL:
|
|
|
// X
|
|
|
this->X+=this->IncX;
|
|
|
this->InvW+=this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
break;
|
|
|
case VTK_CASE_MOSTLY_VERTICAL:
|
|
|
// X
|
|
|
this->Error+=this->Dx2;
|
|
|
if(this->Error>=this->Dy2)
|
|
|
{
|
|
|
this->Error-=this->Dy2;
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
this->InvW+=this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
break;
|
|
|
case VTK_CASE_VERTICAL_OUT_TO_IN:
|
|
|
this->Error-=this->Dx;
|
|
|
if(this->SDy>0)
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
if(this->Error<=0)
|
|
|
#else
|
|
|
if(this->Error<0) // we are no more on the right side
|
|
|
#endif
|
|
|
{
|
|
|
this->Error+=this->SDy;
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
assert("check: positive_equation" && this->Error>0);
|
|
|
#else
|
|
|
assert("check: positive_equation" && this->Error>=0);
|
|
|
#endif
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
if(this->Error>=0) // we are no more on the left side
|
|
|
#else
|
|
|
if(this->Error>0) // we are no more on the left side
|
|
|
#endif
|
|
|
{
|
|
|
this->Error+=this->SDy;
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
// assert("check: negative_equation" && this->Error>0);
|
|
|
#else
|
|
|
assert("check: negative_equation" && this->Error<=0);
|
|
|
#endif
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
// Interpolate the values on inc y
|
|
|
this->InvW+=this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
break;
|
|
|
case VTK_CASE_VERTICAL_IN_TO_OUT:
|
|
|
this->Error+=this->SDy-this->Dx;
|
|
|
if(this->SDy<0)
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
if(this->Error<=0) // out: too far on left
|
|
|
#else
|
|
|
if(this->Error<0) // out: too far on left
|
|
|
#endif
|
|
|
{
|
|
|
this->Error-=this->SDy;
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
assert("check: positive_equation" && this->Error>0);
|
|
|
#else
|
|
|
assert("check: positive_equation" && this->Error>=0);
|
|
|
#endif
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
if(this->Error>=0) // out: too far on right
|
|
|
#else
|
|
|
if(this->Error>0) // out: too far on right
|
|
|
#endif
|
|
|
{
|
|
|
this->Error-=this->SDy;
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
assert("check: negative_equation" && this->Error<0);
|
|
|
#else
|
|
|
assert("check: negative_equation" && this->Error<=0);
|
|
|
#endif
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
// Interpolate the values on inc y
|
|
|
this->InvW+=this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
break;
|
|
|
|
|
|
case VTK_CASE_HORIZONTAL_OUT_TO_IN:
|
|
|
this->Error-=this->Dx;
|
|
|
this->X+=this->XStep;
|
|
|
if(this->SDy>0)
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
if(this->Error<=0) // we are no more on the right side
|
|
|
#else
|
|
|
if(this->Error<0) // we are no more on the right side
|
|
|
#endif
|
|
|
{
|
|
|
this->Error+=this->SDy;
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
assert("check: positive_equation" && this->Error>0);
|
|
|
#else
|
|
|
assert("check: positive_equation" && this->Error>=0);
|
|
|
#endif
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
if(this->Error>=0) // we are no more on the left side
|
|
|
#else
|
|
|
if(this->Error>0) // we are no more on the left side
|
|
|
#endif
|
|
|
{
|
|
|
this->Error+=this->SDy;
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
assert("check: negative_equation" && this->Error<0);
|
|
|
#else
|
|
|
assert("check: negative_equation" && this->Error<=0);
|
|
|
#endif
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
// Interpolate the values on inc y
|
|
|
this->InvW+=this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
break;
|
|
|
|
|
|
case VTK_CASE_HORIZONTAL_IN_TO_OUT:
|
|
|
this->Error+=this->SDy-this->Dx;
|
|
|
this->X+=this->XStep;
|
|
|
if(this->SDy<0)
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
if(this->Error<=0) // out: too far on left
|
|
|
#else
|
|
|
if(this->Error<0) // out: too far on left
|
|
|
#endif
|
|
|
{
|
|
|
this->Error-=this->SDy;
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
// assert("check: positive_equation" && this->Error>0);
|
|
|
#else
|
|
|
assert("check: positive_equation" && this->Error>=0);
|
|
|
#endif
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
if(this->Error>=0) // out: too far on right
|
|
|
#else
|
|
|
if(this->Error>0) // out: too far on right
|
|
|
#endif
|
|
|
{
|
|
|
this->Error-=this->SDy;
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
// assert("check: negative_equation" && this->Error<0);
|
|
|
#else
|
|
|
assert("check: negative_equation" && this->Error<=0);
|
|
|
#endif
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
// Interpolate the values on inc y
|
|
|
this->InvW+=this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
break;
|
|
|
|
|
|
case VTK_CASE_HORIZONTAL_BEGIN:
|
|
|
if(this->First)
|
|
|
{
|
|
|
this->First=0;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->X+=this->XStep;
|
|
|
|
|
|
this->InvW+=this->InvWStep;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->PValuesStep[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->ZStep;
|
|
|
this->Error+=this->ErrorStep;
|
|
|
}
|
|
|
while(this->Error<this->Dx2)
|
|
|
{
|
|
|
this->X+=this->IncX;
|
|
|
this->InvW+=this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
|
|
|
this->Error+=this->Dy2;
|
|
|
}
|
|
|
this->Error-=this->Dx2;
|
|
|
break;
|
|
|
case VTK_CASE_HORIZONTAL_END:
|
|
|
if(y==this->V2->GetScreenY())
|
|
|
{
|
|
|
this->X=this->V2->GetScreenX();
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]=this->PValues2[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview=this->Zview2;
|
|
|
this->InvW=this->InvW2;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->X+=this->XStep;
|
|
|
|
|
|
this->InvW+=this->InvWStep;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->PValuesStep[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->ZStep;
|
|
|
|
|
|
this->Error+=this->ErrorStep;
|
|
|
|
|
|
while(this->Error<this->Dx2)
|
|
|
{
|
|
|
this->X+=this->IncX;
|
|
|
this->InvW+=this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
|
|
|
this->Error+=this->Dy2;
|
|
|
}
|
|
|
this->Error-=this->Dx2;
|
|
|
}
|
|
|
break;
|
|
|
case VTK_CASE_HORIZONTAL_MS:
|
|
|
this->Error+=this->ErrorStep;
|
|
|
if(this->Error>=this->Dy)
|
|
|
{
|
|
|
this->Error-=this->Dy2;
|
|
|
this->X+=this->XStep+this->IncX;
|
|
|
this->InvW+=this->InvWStep+this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->PValuesStep[i]+this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->ZStep+this->Dz;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->X+=this->XStep;
|
|
|
this->InvW+=this->InvWStep;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->PValuesStep[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->ZStep;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void SkipLines(int deltaY,
|
|
|
int y)
|
|
|
{
|
|
|
if(deltaY==1)
|
|
|
{
|
|
|
this->NextLine(0);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
int firstDeltaY;
|
|
|
int i;
|
|
|
switch(this->Case)
|
|
|
{
|
|
|
case VTK_CASE_VERTICAL:
|
|
|
// nothing to do with X
|
|
|
this->InvW+=this->DinvW*deltaY;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i]*deltaY;
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz*deltaY;
|
|
|
break;
|
|
|
case VTK_CASE_DIAGONAL:
|
|
|
// X
|
|
|
this->X+=this->IncX*deltaY;
|
|
|
this->InvW+=this->DinvW*deltaY;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i]*deltaY;
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz*deltaY;
|
|
|
break;
|
|
|
case VTK_CASE_MOSTLY_VERTICAL:
|
|
|
// X
|
|
|
this->Error+=this->Dx2*deltaY;
|
|
|
while(this->Error>=this->Dy2)
|
|
|
{
|
|
|
this->Error-=this->Dy2;
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
this->InvW+=this->DinvW*deltaY;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i]*deltaY;
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz*deltaY;
|
|
|
break;
|
|
|
case VTK_CASE_VERTICAL_OUT_TO_IN:
|
|
|
this->Error-=this->Dx*deltaY;
|
|
|
if(this->SDy>0)
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
while(this->Error<=0) // we are no more on the right side
|
|
|
#else
|
|
|
while(this->Error<0) // we are no more on the right side
|
|
|
#endif
|
|
|
{
|
|
|
this->Error+=this->SDy;
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
while(this->Error>=0) // we are no more on the left side
|
|
|
#else
|
|
|
while(this->Error>0) // we are no more on the left side
|
|
|
#endif
|
|
|
{
|
|
|
this->Error+=this->SDy;
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
// Interpolate the values on inc y
|
|
|
this->InvW+=this->DinvW*deltaY;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i]*deltaY;
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz*deltaY;
|
|
|
break;
|
|
|
case VTK_CASE_VERTICAL_IN_TO_OUT:
|
|
|
this->Error+=(this->SDy-this->Dx)*deltaY;
|
|
|
this->X+=this->IncX*deltaY;
|
|
|
if(this->SDy<0)
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
while(this->Error<=0) // out: too far on left
|
|
|
#else
|
|
|
while(this->Error<0) // out: too far on left
|
|
|
#endif
|
|
|
{
|
|
|
this->Error-=this->SDy;
|
|
|
this->X-=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
while(this->Error>=0) // out: too far on right
|
|
|
#else
|
|
|
while(this->Error>0) // out: too far on right
|
|
|
#endif
|
|
|
{
|
|
|
this->Error-=this->SDy;
|
|
|
this->X-=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
// Interpolate the values on inc y
|
|
|
this->InvW+=this->DinvW*deltaY;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i]*deltaY;
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz*deltaY;
|
|
|
break;
|
|
|
|
|
|
case VTK_CASE_HORIZONTAL_OUT_TO_IN:
|
|
|
this->Error-=this->Dx*deltaY;
|
|
|
this->X+=this->XStep*deltaY;
|
|
|
if(this->SDy>0)
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
while(this->Error<=0) // we are no more on the right side
|
|
|
#else
|
|
|
while(this->Error<0) // we are no more on the right side
|
|
|
#endif
|
|
|
{
|
|
|
this->Error+=this->SDy;
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
while(this->Error>=0) // we are no more on the left side
|
|
|
#else
|
|
|
while(this->Error>0) // we are no more on the left side
|
|
|
#endif
|
|
|
{
|
|
|
this->Error+=this->SDy;
|
|
|
this->X+=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
// Interpolate the values on inc y
|
|
|
this->InvW+=this->DinvW*deltaY;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i]*deltaY;
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz*deltaY;
|
|
|
break;
|
|
|
case VTK_CASE_HORIZONTAL_IN_TO_OUT:
|
|
|
this->Error+=(this->SDy-this->Dx)*deltaY;
|
|
|
this->X+=(this->XStep+this->IncX)*deltaY;
|
|
|
// this->X+=this->IncX*deltaY;
|
|
|
if(this->SDy<0)
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
while(this->Error<=0) // out: too far on left
|
|
|
#else
|
|
|
while(this->Error<0) // out: too far on left
|
|
|
#endif
|
|
|
{
|
|
|
this->Error-=this->SDy;
|
|
|
this->X-=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
#ifdef STRICTLY_INSIDE
|
|
|
while(this->Error>=0) // out: too far on right
|
|
|
#else
|
|
|
while(this->Error>0) // out: too far on right
|
|
|
#endif
|
|
|
{
|
|
|
this->Error-=this->SDy;
|
|
|
this->X-=this->IncX;
|
|
|
}
|
|
|
}
|
|
|
// Interpolate the values on inc y
|
|
|
this->InvW+=this->DinvW*deltaY;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i]*deltaY;
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz*deltaY;
|
|
|
break;
|
|
|
|
|
|
case VTK_CASE_HORIZONTAL_BEGIN:
|
|
|
|
|
|
if(this->First)
|
|
|
{
|
|
|
this->First=0;
|
|
|
firstDeltaY=deltaY-1;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
firstDeltaY=deltaY;
|
|
|
}
|
|
|
|
|
|
this->X+=this->XStep*firstDeltaY;
|
|
|
|
|
|
this->InvW+=this->InvWStep*firstDeltaY;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->PValuesStep[i]*firstDeltaY;
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->ZStep*firstDeltaY;
|
|
|
this->Error+=this->ErrorStep*firstDeltaY;
|
|
|
|
|
|
while(this->Error<this->Dx2)
|
|
|
{
|
|
|
this->X+=this->IncX;
|
|
|
this->InvW+=this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
|
|
|
this->Error+=this->Dy2;
|
|
|
}
|
|
|
this->Error-=this->Dx2;
|
|
|
break;
|
|
|
case VTK_CASE_HORIZONTAL_END:
|
|
|
if(y==this->V2->GetScreenY())
|
|
|
{
|
|
|
this->X=this->V2->GetScreenX();
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]=this->PValues2[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview=this->Zview2;
|
|
|
this->InvW=this->InvW2;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->X+=this->XStep*deltaY;
|
|
|
|
|
|
this->InvW+=this->InvWStep*deltaY;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->PValuesStep[i]*deltaY;
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->ZStep*deltaY;
|
|
|
|
|
|
this->Error+=this->ErrorStep*deltaY;
|
|
|
|
|
|
while(this->Error<this->Dx2)
|
|
|
{
|
|
|
this->X+=this->IncX;
|
|
|
this->InvW+=this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
|
|
|
this->Error+=this->Dy2;
|
|
|
}
|
|
|
this->Error-=this->Dx2;
|
|
|
}
|
|
|
break;
|
|
|
case VTK_CASE_HORIZONTAL_MS:
|
|
|
this->Error+=this->ErrorStep*deltaY;
|
|
|
this->X+=this->XStep*deltaY;
|
|
|
this->InvW+=this->InvWStep*deltaY;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->PValuesStep[i]*deltaY;
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->ZStep*deltaY;
|
|
|
|
|
|
while(this->Error>=this->Dy)
|
|
|
{
|
|
|
this->Error-=this->Dy2;
|
|
|
this->X+=this->IncX;
|
|
|
this->InvW+=this->DinvW;
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected:
|
|
|
int Case;
|
|
|
int Error; // error to the mid-point
|
|
|
int Dx2; // 2*dx
|
|
|
int Dy2; // 2*dy
|
|
|
int First; // use only with VTK_CASE_HORIZONTAL_BEGIN case
|
|
|
int XStep; // dx/dy
|
|
|
int ErrorStep; // XStep*Dy2
|
|
|
|
|
|
vtkVertexEntry *V2;
|
|
|
|
|
|
int IncX; // -1 or 1
|
|
|
|
|
|
int X; // Current abscissa
|
|
|
|
|
|
int X0; // for debugging
|
|
|
int X2; // for debugging
|
|
|
|
|
|
// Slope of 1/w
|
|
|
double DinvW;
|
|
|
// Current 1/W
|
|
|
double InvW;
|
|
|
// DinvW*XStep
|
|
|
double InvWStep;
|
|
|
// 1/W at the end vertex
|
|
|
double InvW2;
|
|
|
|
|
|
// Slope of the z coordinate in view space
|
|
|
double Dz;
|
|
|
// current z in view space
|
|
|
double Zview;
|
|
|
// Dz*XStep
|
|
|
double ZStep;
|
|
|
// z coordinate in view space at the end vertex
|
|
|
double Zview2;
|
|
|
|
|
|
// Slope of each projected values on the edge
|
|
|
double Dpv[VTK_VALUES_SIZE];
|
|
|
|
|
|
|
|
|
// Current projected values
|
|
|
double PValues[VTK_VALUES_SIZE];
|
|
|
// Dpv*XStep
|
|
|
double PValuesStep[VTK_VALUES_SIZE];
|
|
|
// Values at the end vertex.
|
|
|
double PValues2[VTK_VALUES_SIZE];
|
|
|
|
|
|
int Dy; // VTK_HORIZONTAL_MS
|
|
|
int SDy; // VTK_VERTICAL_LEFT/RIGHT
|
|
|
int Dx; // VTK_VERTICAL_LEFT/RIGHT
|
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// During rasterization of a triangle, there is always one side with two
|
|
|
// edges and the other side with a single edge.
|
|
|
// This class manages the side with the two edges called top and bottom edges.
|
|
|
class vtkDoubleScreenEdge
|
|
|
:public vtkScreenEdge
|
|
|
{
|
|
|
public:
|
|
|
void Init(vtkVertexEntry *v0,
|
|
|
vtkVertexEntry *v1,
|
|
|
vtkVertexEntry *v2,
|
|
|
int dx10,
|
|
|
int dy10,
|
|
|
int onRight)
|
|
|
{
|
|
|
this->Current=0;
|
|
|
if(dy10!=0)
|
|
|
{
|
|
|
this->Top.Init(v0,v1,dx10,dy10,onRight);
|
|
|
this->Current=&this->Top;
|
|
|
}
|
|
|
|
|
|
int dx21=v2->GetScreenX()-v1->GetScreenX();
|
|
|
int dy21=v2->GetScreenY()-v1->GetScreenY();
|
|
|
|
|
|
if(dy21!=0)
|
|
|
{
|
|
|
this->Bottom.Init(v1,v2,dx21,dy21,onRight);
|
|
|
if(this->Current==0)
|
|
|
{
|
|
|
this->Current=&this->Bottom;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int GetX() { return this->Current->GetX(); }
|
|
|
double GetInvW() { return this->Current->GetInvW(); }
|
|
|
double GetZview() { return this->Current->GetZview(); }
|
|
|
double *GetPValues() { return this->Current->GetPValues(); }
|
|
|
|
|
|
void OnBottom(int skipped, int y)
|
|
|
{
|
|
|
this->Current=&this->Bottom;
|
|
|
this->Current->OnBottom(skipped,y);
|
|
|
}
|
|
|
|
|
|
void NextLine(int y)
|
|
|
{
|
|
|
this->Current->NextLine(y);
|
|
|
}
|
|
|
void SkipLines(int deltaY,
|
|
|
int y)
|
|
|
{
|
|
|
this->Current->SkipLines(deltaY,y);
|
|
|
}
|
|
|
|
|
|
protected:
|
|
|
vtkSimpleScreenEdge Top;
|
|
|
vtkSimpleScreenEdge Bottom;
|
|
|
vtkScreenEdge *Current;
|
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Horizontal span between two points of two edges.
|
|
|
// Used during scan-conversion.
|
|
|
// It interpolates the values along the span.
|
|
|
|
|
|
class vtkSpan
|
|
|
{
|
|
|
public:
|
|
|
// Initialize the span from the left abcissa x0 and the right absissa x1 and
|
|
|
// from 1/W, the projected values and the z coordinate in view space at
|
|
|
// thoses points. Set the current state to the left point.
|
|
|
void Init(int x0,
|
|
|
double invW0,
|
|
|
double pValues0[VTK_VALUES_SIZE], // projected values
|
|
|
double zView0,
|
|
|
int x1,
|
|
|
double invW1,
|
|
|
double pValues1[VTK_VALUES_SIZE], // projected values
|
|
|
double zView1)
|
|
|
{
|
|
|
// assert("pre: dx>=0" && x1-x0>=0);
|
|
|
// x0=x1: the span is just a point
|
|
|
|
|
|
int i;
|
|
|
if(x0!=x1)
|
|
|
{
|
|
|
|
|
|
double invDx10=1.0/(x1-x0);
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=(pValues1[i]-pValues0[i])*invDx10;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=(invW1-invW0)*invDx10;
|
|
|
this->Dz=(zView1-zView0)*invDx10;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
i=0;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->Dpv[i]=0;
|
|
|
++i;
|
|
|
}
|
|
|
this->DinvW=0;
|
|
|
this->Dz=0;
|
|
|
}
|
|
|
|
|
|
this->Zview=zView0;
|
|
|
this->InvW=invW0;
|
|
|
i=0;
|
|
|
double w=1/this->InvW;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]=pValues0[i];
|
|
|
this->Values[i]=this->PValues[i]*w;
|
|
|
++i;
|
|
|
}
|
|
|
this->X=x0;
|
|
|
this->X1=x1;
|
|
|
}
|
|
|
|
|
|
// Is the current state after the right point?
|
|
|
int IsAtEnd()
|
|
|
{
|
|
|
return this->X>this->X1;
|
|
|
}
|
|
|
|
|
|
// Current abscissa.
|
|
|
int GetX() { return this->X; }
|
|
|
// Current values.
|
|
|
double *GetValues() { return this->Values; }
|
|
|
// Current z coordinate in view space.
|
|
|
double GetZview() { return this->Zview; }
|
|
|
|
|
|
// Go the next abscissa from left to right.
|
|
|
void NextPixel()
|
|
|
{
|
|
|
++this->X;
|
|
|
|
|
|
this->InvW+=this->DinvW;
|
|
|
int i=0;
|
|
|
double w=1/this->InvW;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
this->PValues[i]+=this->Dpv[i];
|
|
|
this->Values[i]=this->PValues[i]*w;
|
|
|
++i;
|
|
|
}
|
|
|
this->Zview+=this->Dz;
|
|
|
}
|
|
|
|
|
|
protected:
|
|
|
int X1; // abscissa at the right point.
|
|
|
|
|
|
int X; // current abscissa
|
|
|
|
|
|
// Slope of 1/w
|
|
|
double DinvW;
|
|
|
// current 1/W
|
|
|
double InvW;
|
|
|
|
|
|
// Slope of the z coordinate in view space
|
|
|
double Dz;
|
|
|
// current z coordinate in view space
|
|
|
double Zview;
|
|
|
|
|
|
// Slope of each projected values on the span
|
|
|
double Dpv[VTK_VALUES_SIZE];
|
|
|
// Current projected values
|
|
|
double PValues[VTK_VALUES_SIZE];
|
|
|
|
|
|
// Current values: Values=PValues/InvW
|
|
|
double Values[VTK_VALUES_SIZE];
|
|
|
};
|
|
|
|
|
|
|
|
|
// Pimpl (i.e. private implementation) idiom
|
|
|
|
|
|
//typedef vtkstd::list<vtkPixelListEntry *> vtkPixelList;
|
|
|
|
|
|
class vtkPixelListEntryBlock
|
|
|
{
|
|
|
public:
|
|
|
vtkPixelListEntryBlock(vtkIdType size)
|
|
|
{
|
|
|
assert("pre: positive_size" && size>0);
|
|
|
this->Size=size;
|
|
|
this->Next=0;
|
|
|
this->Array=new vtkPixelListEntry[size];
|
|
|
this->Last=this->Array+size-1;
|
|
|
// link each entry to the next one
|
|
|
vtkPixelListEntry *p;
|
|
|
vtkPixelListEntry *q;
|
|
|
p=this->Array;
|
|
|
q=p+1;
|
|
|
vtkIdType i=1;
|
|
|
while(i<size)
|
|
|
{
|
|
|
p->SetNext(q);
|
|
|
++i;
|
|
|
p=q;
|
|
|
++q;
|
|
|
}
|
|
|
p->SetNext(0);
|
|
|
}
|
|
|
~vtkPixelListEntryBlock()
|
|
|
{
|
|
|
delete[] this->Array;
|
|
|
}
|
|
|
vtkIdType GetSize() { return this->Size; }
|
|
|
vtkPixelListEntryBlock *GetNext() { return this->Next; }
|
|
|
vtkPixelListEntry *GetFirst() { return this->Array; }
|
|
|
vtkPixelListEntry *GetLast() { return this->Last; }
|
|
|
void SetNext(vtkPixelListEntryBlock *other) { this->Next=other; }
|
|
|
|
|
|
protected:
|
|
|
vtkIdType Size;
|
|
|
vtkPixelListEntryBlock *Next;
|
|
|
vtkPixelListEntry *Array;
|
|
|
vtkPixelListEntry *Last;
|
|
|
};
|
|
|
|
|
|
const vtkIdType VTK_PIXEL_BLOCK_SIZE=64;
|
|
|
|
|
|
class vtkPixelListEntryMemory
|
|
|
{
|
|
|
public:
|
|
|
vtkPixelListEntryMemory()
|
|
|
{
|
|
|
this->FirstBlock=new vtkPixelListEntryBlock(VTK_PIXEL_BLOCK_SIZE);
|
|
|
this->FirstFreeElement=this->FirstBlock->GetFirst();
|
|
|
this->Size=VTK_PIXEL_BLOCK_SIZE;
|
|
|
}
|
|
|
~vtkPixelListEntryMemory()
|
|
|
{
|
|
|
vtkPixelListEntryBlock *p=this->FirstBlock;
|
|
|
vtkPixelListEntryBlock *q;
|
|
|
while(p!=0)
|
|
|
{
|
|
|
q=p->GetNext();
|
|
|
delete p;
|
|
|
p=q;
|
|
|
}
|
|
|
}
|
|
|
vtkPixelListEntry *AllocateEntry()
|
|
|
{
|
|
|
if(this->FirstFreeElement==0)
|
|
|
{
|
|
|
this->AllocateBlock(this->Size<<1);
|
|
|
// this->AllocateBlock(BLOCK_SIZE);
|
|
|
}
|
|
|
vtkPixelListEntry *result=this->FirstFreeElement;
|
|
|
this->FirstFreeElement=result->GetNext();
|
|
|
assert("post: result_exists" && result!=0);
|
|
|
return result;
|
|
|
}
|
|
|
void FreeEntry(vtkPixelListEntry *e)
|
|
|
{
|
|
|
assert("pre: e_exists" && e!=0);
|
|
|
|
|
|
// the following line works even if this->FirstFreeElement==0
|
|
|
e->SetNext(this->FirstFreeElement);
|
|
|
this->FirstFreeElement=e;
|
|
|
}
|
|
|
void FreeSubList(vtkPixelListEntry *first,
|
|
|
vtkPixelListEntry *last)
|
|
|
{
|
|
|
assert("pre: first_exists" && first!=0);
|
|
|
assert("pre: last_exists" && last!=0);
|
|
|
// pre: first==last can be true
|
|
|
// the following line works even if this->FirstFreeElement==0
|
|
|
last->SetNext(this->FirstFreeElement);
|
|
|
this->FirstFreeElement=first;
|
|
|
}
|
|
|
protected:
|
|
|
|
|
|
void AllocateBlock(vtkIdType size)
|
|
|
{
|
|
|
assert("pre: positive_size" && size>0);
|
|
|
vtkPixelListEntryBlock *b=new vtkPixelListEntryBlock(size);
|
|
|
this->Size+=size;
|
|
|
// Update the block linked list: starts with the new block
|
|
|
b->SetNext(this->FirstBlock);
|
|
|
this->FirstBlock=b;
|
|
|
|
|
|
// Update the free element linked list.
|
|
|
// It works even if this->FirstFreeElement==0
|
|
|
b->GetLast()->SetNext(this->FirstFreeElement);
|
|
|
this->FirstFreeElement=b->GetFirst();
|
|
|
}
|
|
|
|
|
|
vtkPixelListEntryBlock *FirstBlock;
|
|
|
vtkPixelListEntry *FirstFreeElement;
|
|
|
vtkIdType Size; // overall size, in number of elements, not in bytes
|
|
|
};
|
|
|
|
|
|
|
|
|
class vtkPixelList
|
|
|
{
|
|
|
public:
|
|
|
vtkPixelList()
|
|
|
{
|
|
|
this->Size=0;
|
|
|
}
|
|
|
vtkPixelListEntry *GetFirst()
|
|
|
{
|
|
|
assert("pre: not_empty" && this->Size>0);
|
|
|
return this->First;
|
|
|
}
|
|
|
vtkIdType GetSize() { return this->Size; }
|
|
|
|
|
|
void AddAndSort(vtkPixelListEntry *p)
|
|
|
{
|
|
|
assert("pre: p_exists" && p!=0);
|
|
|
if(this->Size==0)
|
|
|
{
|
|
|
p->SetPrevious(0);
|
|
|
p->SetNext(0);
|
|
|
this->First=p;
|
|
|
this->Last=p;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
vtkPixelListEntry *it=this->Last;
|
|
|
int sorted=0;
|
|
|
double z=p->GetZview();
|
|
|
while(!sorted && it!=0)
|
|
|
{
|
|
|
#ifdef BACK_TO_FRONT
|
|
|
sorted=it->GetZview()>=z;
|
|
|
#else
|
|
|
sorted=it->GetZview()<=z;
|
|
|
#endif
|
|
|
if(!sorted)
|
|
|
{
|
|
|
it=it->GetPrevious();
|
|
|
}
|
|
|
}
|
|
|
if(it==0) // first element
|
|
|
{
|
|
|
p->SetPrevious(0);
|
|
|
p->SetNext(this->First);
|
|
|
// this->First==0 is handled by case size==0
|
|
|
this->First->SetPrevious(p);
|
|
|
this->First=p;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(it->GetNext()==0) // last element
|
|
|
{
|
|
|
it->SetNext(p);
|
|
|
p->SetPrevious(it);
|
|
|
p->SetNext(0);
|
|
|
this->Last=p;
|
|
|
}
|
|
|
else // general case
|
|
|
{
|
|
|
vtkPixelListEntry *q=it->GetNext();
|
|
|
q->SetPrevious(p);
|
|
|
p->SetNext(q);
|
|
|
p->SetPrevious(it);
|
|
|
it->SetNext(p);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
++this->Size;
|
|
|
}
|
|
|
|
|
|
// the return pointer is used by the memory manager.
|
|
|
void RemoveFirst(vtkPixelListEntryMemory *mm)
|
|
|
{
|
|
|
assert("pre: not_empty" && this->Size>0);
|
|
|
assert("pre: mm_exists" && mm!=0);
|
|
|
|
|
|
vtkPixelListEntry *p=this->First;
|
|
|
if(this->Size>1)
|
|
|
{
|
|
|
this->First=p->GetNext();
|
|
|
this->First->SetPrevious(0);
|
|
|
}
|
|
|
--this->Size;
|
|
|
mm->FreeEntry(p);
|
|
|
}
|
|
|
|
|
|
// the return pointer on the first element is used by the memory manager.
|
|
|
void Clear(vtkPixelListEntryMemory *mm)
|
|
|
{
|
|
|
assert("pre: mm_exists" && mm!=0);
|
|
|
if(this->Size>0)
|
|
|
{
|
|
|
// it works even if first==last
|
|
|
mm->FreeSubList(this->First,this->Last);
|
|
|
this->Size=0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected:
|
|
|
vtkIdType Size;
|
|
|
vtkPixelListEntry *First;
|
|
|
vtkPixelListEntry *Last;
|
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Store the pixel lists for all the frame.
|
|
|
class vtkPixelListFrame
|
|
|
{
|
|
|
public:
|
|
|
typedef vtkstd::vector<vtkPixelList> VectorType;
|
|
|
|
|
|
vtkPixelListFrame(int size)
|
|
|
:Vector(size)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
// Return width*height
|
|
|
vtkIdType GetSize() { return this->Vector.size(); }
|
|
|
|
|
|
// Return the size of the list at pixel `i'.
|
|
|
vtkIdType GetListSize(int i)
|
|
|
{
|
|
|
assert("pre: valid_i" && i>=0 && i<this->GetSize());
|
|
|
return this->Vector[i].GetSize();
|
|
|
}
|
|
|
|
|
|
// Add a value the pixel list of pixel `i' and sort it in the list.
|
|
|
void AddAndSort(int i,
|
|
|
vtkPixelListEntry *pixelEntry)
|
|
|
{
|
|
|
assert("pre: valid_i" && i>=0 && i<this->GetSize());
|
|
|
assert("pre: pixelEntry_exists" && pixelEntry!=0);
|
|
|
|
|
|
this->Vector[i].AddAndSort(pixelEntry);
|
|
|
}
|
|
|
|
|
|
// Return the first entry for pixel `i'.
|
|
|
vtkPixelListEntry *GetFront(int i)
|
|
|
{
|
|
|
assert("pre: valid_i" && i>=0 && i<this->GetSize());
|
|
|
assert("pre: not_empty" && this->GetListSize(i)>0);
|
|
|
return this->Vector[i].GetFirst();
|
|
|
}
|
|
|
|
|
|
// Remove the first entry for pixel `i'.
|
|
|
void PopFront(int i,
|
|
|
vtkPixelListEntryMemory *mm)
|
|
|
{
|
|
|
assert("pre: valid_i" && i>=0 && i<this->GetSize());
|
|
|
assert("pre: not_empty" && this->GetListSize(i)>0);
|
|
|
assert("pre: mm_exists" && mm!=0);
|
|
|
this->Vector[i].RemoveFirst(mm);
|
|
|
}
|
|
|
|
|
|
// Return the begin iterator for pixel `i'.
|
|
|
vtkPixelListEntry *GetFirst(int i)
|
|
|
{
|
|
|
assert("pre: valid_i" && i>=0 && i<this->GetSize());
|
|
|
return this->Vector[i].GetFirst();
|
|
|
}
|
|
|
#if 0
|
|
|
// Return the end iterator for pixel `i'.
|
|
|
vtkstd::list<vtkPixelListEntry *>::iterator GetEndIterator(int i)
|
|
|
{
|
|
|
assert("pre: valid_i" && i>=0 && i<this->GetSize());
|
|
|
return this->Vector[i].end();
|
|
|
}
|
|
|
#endif
|
|
|
// Clear the list of each pixel of the frame.
|
|
|
void Clean(vtkPixelListEntryMemory *mm)
|
|
|
{
|
|
|
assert("pre: mm_exists" && mm!=0);
|
|
|
vtkIdType i=0;
|
|
|
vtkIdType c=this->Vector.size();
|
|
|
while(i<c)
|
|
|
{
|
|
|
vtkPixelList *l=&(Vector[i]);
|
|
|
l->Clear(mm);
|
|
|
++i;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Destructor.
|
|
|
~vtkPixelListFrame()
|
|
|
{
|
|
|
#if 0
|
|
|
vtkIdType i=0;
|
|
|
vtkIdType c=this->Vector.size();
|
|
|
while(i<c)
|
|
|
{
|
|
|
vtkPixelList *l=&(Vector[i]);
|
|
|
while(!l->empty())
|
|
|
{
|
|
|
delete l->front();
|
|
|
l->pop_front();
|
|
|
}
|
|
|
++i;
|
|
|
}
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
vtkPixelList *GetList(int i)
|
|
|
{
|
|
|
assert("pre: valid_i" && i>=0 && i<this->GetSize());
|
|
|
return &(this->Vector[i]);
|
|
|
}
|
|
|
|
|
|
protected:
|
|
|
VectorType Vector;
|
|
|
|
|
|
// the STL specification claims that
|
|
|
// size() on a std: :list is permitted to be O(n)!!!!
|
|
|
// vtkstd::vector<vtkIdType> Sizes;
|
|
|
|
|
|
// vtkstd::list<vtkPixelListEntry *>::iterator It;
|
|
|
// vtkstd::list<vtkPixelListEntry *>::iterator PreviousIt;
|
|
|
// vtkstd::list<vtkPixelListEntry *>::iterator ItEnd;
|
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Store a triangle face. Ids are in increasing order. Orientation does not
|
|
|
// matter for the algorithm.
|
|
|
class vtkFace
|
|
|
{
|
|
|
public:
|
|
|
// Initialization from face ids in increasing order.
|
|
|
vtkFace(vtkIdType faceIds[3])
|
|
|
{
|
|
|
assert("pre: ordered ids" && faceIds[0]<faceIds[1]
|
|
|
&& faceIds[1]<faceIds[2]);
|
|
|
this->FaceIds[0]=faceIds[0];
|
|
|
this->FaceIds[1]=faceIds[1];
|
|
|
this->FaceIds[2]=faceIds[2];
|
|
|
this->Count=0;
|
|
|
}
|
|
|
|
|
|
// Return the 3 face ids.
|
|
|
vtkIdType *GetFaceIds() { return this->FaceIds; }
|
|
|
|
|
|
// Are `this' and faceIds equal?
|
|
|
int IsEqual(vtkIdType faceIds[3])
|
|
|
{
|
|
|
return (this->FaceIds[0]==faceIds[0])&&(this->FaceIds[1]==faceIds[1])
|
|
|
&&(this->FaceIds[2]==faceIds[2]);
|
|
|
}
|
|
|
|
|
|
void Ref() { ++this->Count; }
|
|
|
void Unref()
|
|
|
{
|
|
|
--this->Count;
|
|
|
if(this->Count==0)
|
|
|
{
|
|
|
delete this;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int GetRendered() { return this->Rendered; }
|
|
|
void SetRendered(int value) { this->Rendered=value; }
|
|
|
|
|
|
protected:
|
|
|
vtkIdType FaceIds[3];
|
|
|
int Count;
|
|
|
int Rendered;
|
|
|
|
|
|
private:
|
|
|
vtkFace(); // not implemented
|
|
|
vtkFace(const vtkFace &other); // not implemented
|
|
|
vtkFace &operator=(const vtkFace &other); // not implemented
|
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// For each vertex, store the list of faces incident on this vertex.
|
|
|
// It is view independent.
|
|
|
class vtkUseSet
|
|
|
{
|
|
|
public:
|
|
|
typedef vtkstd::vector<vtkstd::list<vtkFace *> *> VectorType;
|
|
|
VectorType Vector;
|
|
|
|
|
|
vtkstd::list<vtkFace *> AllFaces; // to set up rendering to false.
|
|
|
|
|
|
// Initialize with the number of vertices.
|
|
|
vtkUseSet(int size)
|
|
|
:Vector(size)
|
|
|
{
|
|
|
vtkIdType i=0;
|
|
|
vtkIdType c=this->Vector.size();
|
|
|
while(i<c)
|
|
|
{
|
|
|
this->Vector[i]=0;
|
|
|
++i;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Destructor.
|
|
|
~vtkUseSet()
|
|
|
{
|
|
|
vtkIdType i=0;
|
|
|
vtkIdType c=this->Vector.size();
|
|
|
while(i<c)
|
|
|
{
|
|
|
if(this->Vector[i]!=0)
|
|
|
{
|
|
|
while(!this->Vector[i]->empty())
|
|
|
{
|
|
|
(*this->Vector[i]->begin())->Unref();
|
|
|
this->Vector[i]->pop_front();
|
|
|
}
|
|
|
delete this->Vector[i];
|
|
|
}
|
|
|
++i;
|
|
|
}
|
|
|
while(!this->AllFaces.empty())
|
|
|
{
|
|
|
(*this->AllFaces.begin())->Unref();
|
|
|
this->AllFaces.pop_front();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// For each vertex, clear the list of faces incident to it.
|
|
|
void Clear()
|
|
|
{
|
|
|
vtkIdType i=0;
|
|
|
vtkIdType c=this->Vector.size();
|
|
|
while(i<c)
|
|
|
{
|
|
|
if(this->Vector[i]!=0)
|
|
|
{
|
|
|
while(!this->Vector[i]->empty())
|
|
|
{
|
|
|
(*this->Vector[i]->begin())->Unref();
|
|
|
this->Vector[i]->pop_front();
|
|
|
}
|
|
|
delete this->Vector[i];
|
|
|
this->Vector[i]=0;
|
|
|
}
|
|
|
++i;
|
|
|
}
|
|
|
while(!this->AllFaces.empty())
|
|
|
{
|
|
|
(*this->AllFaces.begin())->Unref();
|
|
|
this->AllFaces.pop_front();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Add face to each vertex only if the useset does not have the face yet.
|
|
|
void AddFace(vtkIdType faceIds[3])
|
|
|
{
|
|
|
assert("pre: ordered ids" && faceIds[0]<faceIds[1]
|
|
|
&& faceIds[1]<faceIds[2]);
|
|
|
if(!this->HasFace(faceIds))
|
|
|
{
|
|
|
vtkFace *f=new vtkFace(faceIds);
|
|
|
this->AllFaces.push_back(f);
|
|
|
f->Ref();
|
|
|
// All the vertices of this face need to be fed
|
|
|
int i=0;
|
|
|
while(i<3)
|
|
|
{
|
|
|
vtkstd::list<vtkFace *> *p=this->Vector[faceIds[i]];
|
|
|
if(p==0)
|
|
|
{
|
|
|
p=new vtkstd::list<vtkFace *>;
|
|
|
this->Vector[faceIds[i]]=p;
|
|
|
}
|
|
|
p->push_back(f);
|
|
|
f->Ref();
|
|
|
++i;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void SetNotRendered()
|
|
|
{
|
|
|
vtkstd::list<vtkFace *>::iterator it;
|
|
|
vtkstd::list<vtkFace *>::iterator end;
|
|
|
it=this->AllFaces.begin();
|
|
|
end=this->AllFaces.end();
|
|
|
while(it!=end)
|
|
|
{
|
|
|
(*it)->SetRendered(0);
|
|
|
++it;
|
|
|
}
|
|
|
}
|
|
|
protected:
|
|
|
// Does the use set of vertex faceIds[0] have face faceIds?
|
|
|
int HasFace(vtkIdType faceIds[3])
|
|
|
{
|
|
|
vtkstd::list<vtkFace *> *useSet=this->Vector[faceIds[0]];
|
|
|
int result=0;
|
|
|
|
|
|
if(useSet!=0)
|
|
|
{
|
|
|
this->It=(*useSet).begin();
|
|
|
this->ItEnd=(*useSet).end();
|
|
|
while(!result && this->It!=this->ItEnd)
|
|
|
{
|
|
|
result=(*this->It)->IsEqual(faceIds);
|
|
|
++this->It;
|
|
|
}
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
|
|
|
// Used in HasFace()
|
|
|
vtkstd::list<vtkFace *>::iterator It;
|
|
|
vtkstd::list<vtkFace *>::iterator ItEnd;
|
|
|
};
|
|
|
|
|
|
// For each vertex, store its projection. It is view-dependent.
|
|
|
class vtkVertices
|
|
|
{
|
|
|
public:
|
|
|
typedef vtkstd::vector<vtkVertexEntry> VectorType;
|
|
|
VectorType Vector;
|
|
|
|
|
|
// Initialize with the number of vertices.
|
|
|
vtkVertices(int size)
|
|
|
:Vector(size)
|
|
|
{
|
|
|
}
|
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Implementation of the public class.
|
|
|
|
|
|
vtkCxxRevisionMacro(vtkUnstructuredGridVolumeZSweepMapper, "$Revision: 1.3 $");
|
|
|
vtkStandardNewMacro(vtkUnstructuredGridVolumeZSweepMapper);
|
|
|
|
|
|
vtkCxxSetObjectMacro(vtkUnstructuredGridVolumeZSweepMapper, RayIntegrator,
|
|
|
vtkUnstructuredGridVolumeRayIntegrator);
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Description:
|
|
|
// Set MaxPixelListSize to 32.
|
|
|
vtkUnstructuredGridVolumeZSweepMapper::vtkUnstructuredGridVolumeZSweepMapper()
|
|
|
{
|
|
|
this->MaxPixelListSize=64; // default value.
|
|
|
|
|
|
this->ImageSampleDistance = 1.0;
|
|
|
this->MinimumImageSampleDistance = 1.0;
|
|
|
this->MaximumImageSampleDistance = 10.0;
|
|
|
this->AutoAdjustSampleDistances = 1;
|
|
|
|
|
|
this->ImageMemorySize[0] = 0;
|
|
|
this->ImageMemorySize[1] = 0;
|
|
|
|
|
|
this->Image = NULL;
|
|
|
this->RealRGBAImage=0;
|
|
|
|
|
|
this->RenderTimeTable = NULL;
|
|
|
this->RenderVolumeTable = NULL;
|
|
|
this->RenderRendererTable = NULL;
|
|
|
this->RenderTableSize = 0;
|
|
|
this->RenderTableEntries = 0;
|
|
|
|
|
|
this->ZBuffer = NULL;
|
|
|
this->ZBufferSize[0] = 0;
|
|
|
this->ZBufferSize[1] = 0;
|
|
|
this->ZBufferOrigin[0] = 0;
|
|
|
this->ZBufferOrigin[1] = 0;
|
|
|
|
|
|
this->IntermixIntersectingGeometry = 1;
|
|
|
|
|
|
this->ImageDisplayHelper = vtkRayCastImageDisplayHelper::New();
|
|
|
|
|
|
this->ScalarMode = VTK_SCALAR_MODE_DEFAULT;
|
|
|
this->ArrayName = new char[1];
|
|
|
this->ArrayName[0] = '\0';
|
|
|
this->ArrayId = -1;
|
|
|
this->ArrayAccessMode = VTK_GET_ARRAY_BY_ID;
|
|
|
|
|
|
this->PixelListFrame=0;
|
|
|
|
|
|
this->Cell=vtkGenericCell::New();
|
|
|
|
|
|
this->EventList=vtkPriorityQueue::New();
|
|
|
|
|
|
this->UseSet=0;
|
|
|
this->Vertices=0;
|
|
|
|
|
|
this->PerspectiveTransform = vtkTransform::New();
|
|
|
this->PerspectiveMatrix = vtkMatrix4x4::New();
|
|
|
|
|
|
this->SimpleEdge=new vtkSimpleScreenEdge;
|
|
|
this->DoubleEdge=new vtkDoubleScreenEdge;
|
|
|
|
|
|
this->Span=new vtkSpan;
|
|
|
|
|
|
this->RayIntegrator = NULL;
|
|
|
this->RealRayIntegrator = NULL;
|
|
|
|
|
|
this->IntersectionLengths=vtkDoubleArray::New();
|
|
|
this->IntersectionLengths->SetNumberOfValues(1);
|
|
|
this->NearIntersections=vtkDoubleArray::New();
|
|
|
this->NearIntersections->SetNumberOfValues(1);
|
|
|
this->FarIntersections=vtkDoubleArray::New();
|
|
|
this->FarIntersections->SetNumberOfValues(1);
|
|
|
|
|
|
this->MemoryManager=0;
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
vtkUnstructuredGridVolumeZSweepMapper::~vtkUnstructuredGridVolumeZSweepMapper()
|
|
|
{
|
|
|
if(this->MemoryManager!=0)
|
|
|
{
|
|
|
delete this->MemoryManager;
|
|
|
}
|
|
|
if(this->PixelListFrame!=0)
|
|
|
{
|
|
|
delete this->PixelListFrame;
|
|
|
}
|
|
|
this->Cell->Delete();
|
|
|
this->EventList->Delete();
|
|
|
|
|
|
this->ImageDisplayHelper->Delete();
|
|
|
|
|
|
delete[] this->ArrayName;
|
|
|
|
|
|
if(this->UseSet!=0)
|
|
|
{
|
|
|
delete this->UseSet;
|
|
|
}
|
|
|
|
|
|
if(this->Vertices!=0)
|
|
|
{
|
|
|
delete this->Vertices;
|
|
|
}
|
|
|
|
|
|
this->PerspectiveTransform->Delete();
|
|
|
this->PerspectiveMatrix->Delete();
|
|
|
|
|
|
delete this->SimpleEdge;
|
|
|
delete this->DoubleEdge;
|
|
|
delete this->Span;
|
|
|
|
|
|
if ( this->Image )
|
|
|
{
|
|
|
delete [] this->Image;
|
|
|
delete [] this->RealRGBAImage;
|
|
|
}
|
|
|
|
|
|
if ( this->RenderTableSize )
|
|
|
{
|
|
|
delete [] this->RenderTimeTable;
|
|
|
delete [] this->RenderVolumeTable;
|
|
|
delete [] this->RenderRendererTable;
|
|
|
}
|
|
|
|
|
|
this->SetRayIntegrator(NULL);
|
|
|
if (this->RealRayIntegrator)
|
|
|
{
|
|
|
this->RealRayIntegrator->UnRegister(this);
|
|
|
}
|
|
|
|
|
|
this->IntersectionLengths->Delete();
|
|
|
this->NearIntersections->Delete();
|
|
|
this->FarIntersections->Delete();
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
float vtkUnstructuredGridVolumeZSweepMapper::RetrieveRenderTime(
|
|
|
vtkRenderer *ren,
|
|
|
vtkVolume *vol )
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
for ( i = 0; i < this->RenderTableEntries; i++ )
|
|
|
{
|
|
|
if ( this->RenderVolumeTable[i] == vol &&
|
|
|
this->RenderRendererTable[i] == ren )
|
|
|
{
|
|
|
return this->RenderTimeTable[i];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return 0.0;
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::StoreRenderTime(
|
|
|
vtkRenderer *ren,
|
|
|
vtkVolume *vol,
|
|
|
float time )
|
|
|
{
|
|
|
int i;
|
|
|
for ( i = 0; i < this->RenderTableEntries; i++ )
|
|
|
{
|
|
|
if ( this->RenderVolumeTable[i] == vol &&
|
|
|
this->RenderRendererTable[i] == ren )
|
|
|
{
|
|
|
this->RenderTimeTable[i] = time;
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
// Need to increase size
|
|
|
if ( this->RenderTableEntries >= this->RenderTableSize )
|
|
|
{
|
|
|
if ( this->RenderTableSize == 0 )
|
|
|
{
|
|
|
this->RenderTableSize = 10;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->RenderTableSize *= 2;
|
|
|
}
|
|
|
|
|
|
float *oldTimePtr = this->RenderTimeTable;
|
|
|
vtkVolume **oldVolumePtr = this->RenderVolumeTable;
|
|
|
vtkRenderer **oldRendererPtr = this->RenderRendererTable;
|
|
|
|
|
|
this->RenderTimeTable = new float [this->RenderTableSize];
|
|
|
this->RenderVolumeTable = new vtkVolume *[this->RenderTableSize];
|
|
|
this->RenderRendererTable = new vtkRenderer *[this->RenderTableSize];
|
|
|
|
|
|
for (i = 0; i < this->RenderTableEntries; i++ )
|
|
|
{
|
|
|
this->RenderTimeTable[i] = oldTimePtr[i];
|
|
|
this->RenderVolumeTable[i] = oldVolumePtr[i];
|
|
|
this->RenderRendererTable[i] = oldRendererPtr[i];
|
|
|
}
|
|
|
|
|
|
delete [] oldTimePtr;
|
|
|
delete [] oldVolumePtr;
|
|
|
delete [] oldRendererPtr;
|
|
|
}
|
|
|
|
|
|
this->RenderTimeTable[this->RenderTableEntries] = time;
|
|
|
this->RenderVolumeTable[this->RenderTableEntries] = vol;
|
|
|
this->RenderRendererTable[this->RenderTableEntries] = ren;
|
|
|
|
|
|
this->RenderTableEntries++;
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::SelectScalarArray(int arrayNum)
|
|
|
{
|
|
|
if ( (this->ArrayId == arrayNum)
|
|
|
&& (this->ArrayAccessMode == VTK_GET_ARRAY_BY_ID) )
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
this->Modified();
|
|
|
|
|
|
this->ArrayId = arrayNum;
|
|
|
this->ArrayAccessMode = VTK_GET_ARRAY_BY_ID;
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::SelectScalarArray(
|
|
|
const char *arrayName)
|
|
|
{
|
|
|
if ( !arrayName
|
|
|
|| ( (strcmp(this->ArrayName, arrayName) == 0)
|
|
|
&& (this->ArrayAccessMode == VTK_GET_ARRAY_BY_ID) ) )
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
this->Modified();
|
|
|
|
|
|
delete[] this->ArrayName;
|
|
|
this->ArrayName = new char[strlen(arrayName) + 1];
|
|
|
strcpy(this->ArrayName, arrayName);
|
|
|
this->ArrayAccessMode = VTK_GET_ARRAY_BY_NAME;
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Return the method for obtaining scalar data.
|
|
|
const char *vtkUnstructuredGridVolumeZSweepMapper::GetScalarModeAsString(void)
|
|
|
{
|
|
|
if ( this->ScalarMode == VTK_SCALAR_MODE_USE_CELL_DATA )
|
|
|
{
|
|
|
return "UseCellData";
|
|
|
}
|
|
|
else if ( this->ScalarMode == VTK_SCALAR_MODE_USE_POINT_DATA )
|
|
|
{
|
|
|
return "UsePointData";
|
|
|
}
|
|
|
else if ( this->ScalarMode == VTK_SCALAR_MODE_USE_POINT_FIELD_DATA )
|
|
|
{
|
|
|
return "UsePointFieldData";
|
|
|
}
|
|
|
else if ( this->ScalarMode == VTK_SCALAR_MODE_USE_CELL_FIELD_DATA )
|
|
|
{
|
|
|
return "UseCellFieldData";
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return "Default";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::PrintSelf(ostream& os,
|
|
|
vtkIndent indent)
|
|
|
{
|
|
|
this->Superclass::PrintSelf(os,indent);
|
|
|
|
|
|
os << indent << "Max Pixel List Size: " << this->MaxPixelListSize << "\n";
|
|
|
|
|
|
os << indent << "ScalarMode: " << this->GetScalarModeAsString() << endl;
|
|
|
|
|
|
os << indent << "Image Sample Distance: "
|
|
|
<< this->ImageSampleDistance << "\n";
|
|
|
os << indent << "Minimum Image Sample Distance: "
|
|
|
<< this->MinimumImageSampleDistance << "\n";
|
|
|
os << indent << "Maximum Image Sample Distance: "
|
|
|
<< this->MaximumImageSampleDistance << "\n";
|
|
|
os << indent << "Auto Adjust Sample Distances: "
|
|
|
<< this->AutoAdjustSampleDistances << "\n";
|
|
|
os << indent << "Intermix Intersecting Geometry: "
|
|
|
<< (this->IntermixIntersectingGeometry ? "On\n" : "Off\n");
|
|
|
|
|
|
// The PrintSelf test just search for words in the PrintSelf function
|
|
|
// We add here the internal variable we don't want to display:
|
|
|
// this->ImageViewportSize this->ImageOrigin this->ImageInUseSize
|
|
|
|
|
|
os << indent << "ImageOrigin="<<ImageOrigin[0]<<", "<<ImageOrigin[1]<<"\n";
|
|
|
os << indent << "ImageInUseSize="<<ImageInUseSize[0]<<", "<<ImageInUseSize[1]<<"\n";
|
|
|
os << indent << "ImageMemorySize="<<ImageMemorySize[0]<<", "<<ImageMemorySize[1]<<"\n";
|
|
|
|
|
|
if (this->RayIntegrator)
|
|
|
{
|
|
|
os << indent << "RayIntegrator: "
|
|
|
<< this->RayIntegrator->GetClassName() << endl;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
os << indent << "RayIntegrator: (automatic)" << endl;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Description:
|
|
|
// Maximum size allowed for a pixel list. Default is 32.
|
|
|
// During the rendering, if a list of pixel is full, incremental compositing
|
|
|
// is performed. Even if it is a user setting, it is an advanced parameter.
|
|
|
// You have to understand how the algorithm works to change this value.
|
|
|
int vtkUnstructuredGridVolumeZSweepMapper::GetMaxPixelListSize()
|
|
|
{
|
|
|
return this->MaxPixelListSize;
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Description:
|
|
|
// Change the maximum size allowed for a pixel list. It is an advanced
|
|
|
// parameter.
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::SetMaxPixelListSize(int size)
|
|
|
{
|
|
|
assert("pre: positive_size" && size>0);
|
|
|
this->MaxPixelListSize=size;
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
#define ESTABLISH_INTEGRATOR(classname) \
|
|
|
if ( !this->RealRayIntegrator \
|
|
|
|| (!this->RealRayIntegrator->IsA(#classname)) ) \
|
|
|
{ \
|
|
|
if (this->RealRayIntegrator) this->RealRayIntegrator->UnRegister(this); \
|
|
|
this->RealRayIntegrator = classname::New(); \
|
|
|
this->RealRayIntegrator->Register(this); \
|
|
|
this->RealRayIntegrator->Delete(); \
|
|
|
} \
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Description:
|
|
|
// WARNING: INTERNAL METHOD - NOT INTENDED FOR GENERAL USE
|
|
|
// DO NOT USE THIS METHOD OUTSIDE OF THE RENDERING PROCESS
|
|
|
// Render the volume
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::Render(vtkRenderer *ren,
|
|
|
vtkVolume *vol)
|
|
|
{
|
|
|
vtkDebugMacro(<<"Render");
|
|
|
|
|
|
// Check for input
|
|
|
if(this->GetInput()==0)
|
|
|
{
|
|
|
vtkErrorMacro(<< "No Input!");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
this->Scalars = this->GetScalars(this->GetInput(), this->ScalarMode,
|
|
|
this->ArrayAccessMode,
|
|
|
this->ArrayId, this->ArrayName,
|
|
|
this->CellScalars);
|
|
|
|
|
|
if(this->Scalars==0)
|
|
|
{
|
|
|
vtkErrorMacro("Can't use the ZSweep mapper without scalars!");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
this->GetInput()->UpdateInformation();
|
|
|
this->GetInput()->SetUpdateExtentToWholeExtent();
|
|
|
this->GetInput()->Update();
|
|
|
|
|
|
// Check to make sure we have an appropriate integrator.
|
|
|
if (this->RayIntegrator)
|
|
|
{
|
|
|
if (this->RealRayIntegrator != this->RayIntegrator)
|
|
|
{
|
|
|
if (this->RealRayIntegrator)
|
|
|
{
|
|
|
this->RealRayIntegrator->UnRegister(this);
|
|
|
}
|
|
|
this->RealRayIntegrator = this->RayIntegrator;
|
|
|
this->RealRayIntegrator->Register(this);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (this->CellScalars)
|
|
|
{
|
|
|
ESTABLISH_INTEGRATOR(vtkUnstructuredGridHomogeneousRayIntegrator);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (vol->GetProperty()->GetIndependentComponents())
|
|
|
{
|
|
|
ESTABLISH_INTEGRATOR(vtkUnstructuredGridPreIntegration);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
ESTABLISH_INTEGRATOR(vtkUnstructuredGridPartialPreIntegration);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
// Start timing now. We didn't want to capture the update of the
|
|
|
// input data in the times
|
|
|
this->Timer->StartTimer();
|
|
|
|
|
|
int oldImageMemorySize[2];
|
|
|
oldImageMemorySize[0] = this->ImageMemorySize[0];
|
|
|
oldImageMemorySize[1] = this->ImageMemorySize[1];
|
|
|
|
|
|
// If we are automatically adjusting the size to achieve a desired frame
|
|
|
// rate, then do that adjustment here. Base the new image sample distance
|
|
|
// on the previous one and the previous render time. Don't let
|
|
|
// the adjusted image sample distance be less than the minimum image sample
|
|
|
// distance or more than the maximum image sample distance.
|
|
|
float oldImageSampleDistance = this->ImageSampleDistance;
|
|
|
if ( this->AutoAdjustSampleDistances )
|
|
|
{
|
|
|
float oldTime = this->RetrieveRenderTime( ren, vol );
|
|
|
float newTime = vol->GetAllocatedRenderTime();
|
|
|
this->ImageSampleDistance *= sqrt(oldTime / newTime);
|
|
|
this->ImageSampleDistance =
|
|
|
(this->ImageSampleDistance>this->MaximumImageSampleDistance)?
|
|
|
(this->MaximumImageSampleDistance):(this->ImageSampleDistance);
|
|
|
this->ImageSampleDistance =
|
|
|
(this->ImageSampleDistance<this->MinimumImageSampleDistance)?
|
|
|
(this->MinimumImageSampleDistance):(this->ImageSampleDistance);
|
|
|
}
|
|
|
|
|
|
// The full image fills the viewport. First, compute the actual viewport
|
|
|
// size, then divide by the ImageSampleDistance to find the full image
|
|
|
// size in pixels
|
|
|
int width, height;
|
|
|
ren->GetTiledSize(&width, &height);
|
|
|
this->ImageViewportSize[0] =
|
|
|
static_cast<int>(width/this->ImageSampleDistance);
|
|
|
this->ImageViewportSize[1] =
|
|
|
static_cast<int>(height/this->ImageSampleDistance);
|
|
|
|
|
|
this->ImageInUseSize[0] = this->ImageViewportSize[0];
|
|
|
this->ImageInUseSize[1] = this->ImageViewportSize[1];
|
|
|
this->ImageOrigin[0] = 0;
|
|
|
this->ImageOrigin[1] = 0;
|
|
|
|
|
|
// What is a power of 2 size big enough to fit this image?
|
|
|
this->ImageMemorySize[0] = 32;
|
|
|
this->ImageMemorySize[1] = 32;
|
|
|
while ( this->ImageMemorySize[0] < this->ImageInUseSize[0] )
|
|
|
{
|
|
|
this->ImageMemorySize[0] *= 2;
|
|
|
}
|
|
|
while ( this->ImageMemorySize[1] < this->ImageInUseSize[1] )
|
|
|
{
|
|
|
this->ImageMemorySize[1] *= 2;
|
|
|
}
|
|
|
|
|
|
// If the old image size is much too big (more than twice in
|
|
|
// either direction) then set the old width to 0 which will
|
|
|
// cause the image to be recreated
|
|
|
if ( oldImageMemorySize[0] > 2*this->ImageMemorySize[0] ||
|
|
|
oldImageMemorySize[1] > 2*this->ImageMemorySize[1] )
|
|
|
{
|
|
|
oldImageMemorySize[0] = 0;
|
|
|
}
|
|
|
|
|
|
// If the old image is big enough (but not too big - we handled
|
|
|
// that above) then we'll bump up our required size to the
|
|
|
// previous one. This will keep us from thrashing.
|
|
|
if ( oldImageMemorySize[0] >= this->ImageMemorySize[0] &&
|
|
|
oldImageMemorySize[1] >= this->ImageMemorySize[1] )
|
|
|
{
|
|
|
this->ImageMemorySize[0] = oldImageMemorySize[0];
|
|
|
this->ImageMemorySize[1] = oldImageMemorySize[1];
|
|
|
}
|
|
|
|
|
|
int bufferSize=this->ImageMemorySize[0] * this->ImageMemorySize[1] * 4;
|
|
|
|
|
|
// Do we already have a texture big enough? If not, create a new one and
|
|
|
// clear it.
|
|
|
if ( !this->Image ||
|
|
|
this->ImageMemorySize[0] > oldImageMemorySize[0] ||
|
|
|
this->ImageMemorySize[1] > oldImageMemorySize[1] )
|
|
|
{
|
|
|
// If there is an image there must be row bounds
|
|
|
if ( this->Image )
|
|
|
{
|
|
|
delete [] this->Image;
|
|
|
delete [] this->RealRGBAImage;
|
|
|
}
|
|
|
this->Image = new unsigned char[bufferSize];
|
|
|
this->RealRGBAImage=new float[bufferSize];
|
|
|
}
|
|
|
|
|
|
// We have to clear the image, each time:
|
|
|
memset(this->Image,0,bufferSize);
|
|
|
|
|
|
vtkIdType j=0;
|
|
|
while(j<bufferSize)
|
|
|
{
|
|
|
this->RealRGBAImage[j]=0;
|
|
|
this->RealRGBAImage[j+1]=0;
|
|
|
this->RealRGBAImage[j+2]=0;
|
|
|
this->RealRGBAImage[j+3]=0;
|
|
|
j+=4;
|
|
|
}
|
|
|
|
|
|
// Capture the zbuffer if necessary
|
|
|
if ( this->IntermixIntersectingGeometry &&
|
|
|
ren->GetNumberOfPropsRendered() )
|
|
|
{
|
|
|
int x1, x2, y1, y2;
|
|
|
double *viewport = ren->GetViewport();
|
|
|
int *renWinSize = ren->GetRenderWindow()->GetSize();
|
|
|
|
|
|
// turn this->ImageOrigin into (x1,y1) in window (not viewport!)
|
|
|
// coordinates.
|
|
|
x1 = static_cast<int> (
|
|
|
viewport[0] * static_cast<float>(renWinSize[0]) +
|
|
|
static_cast<float>(this->ImageOrigin[0]) * this->ImageSampleDistance );
|
|
|
y1 = static_cast<int> (
|
|
|
viewport[1] * static_cast<float>(renWinSize[1]) +
|
|
|
static_cast<float>(this->ImageOrigin[1]) * this->ImageSampleDistance);
|
|
|
|
|
|
// compute z buffer size
|
|
|
this->ZBufferSize[0] = static_cast<int>(
|
|
|
static_cast<float>(this->ImageInUseSize[0]) * this->ImageSampleDistance);
|
|
|
this->ZBufferSize[1] = static_cast<int>(
|
|
|
static_cast<float>(this->ImageInUseSize[1]) * this->ImageSampleDistance);
|
|
|
|
|
|
// Use the size to compute (x2,y2) in window coordinates
|
|
|
x2 = x1 + this->ZBufferSize[0] - 1;
|
|
|
y2 = y1 + this->ZBufferSize[1] - 1;
|
|
|
|
|
|
// This is the z buffer origin (in viewport coordinates)
|
|
|
this->ZBufferOrigin[0] = static_cast<int>(
|
|
|
static_cast<float>(this->ImageOrigin[0]) * this->ImageSampleDistance);
|
|
|
this->ZBufferOrigin[1] = static_cast<int>(
|
|
|
static_cast<float>(this->ImageOrigin[1]) * this->ImageSampleDistance);
|
|
|
|
|
|
// Capture the z buffer
|
|
|
this->ZBuffer = ren->GetRenderWindow()->GetZbufferData(x1,y1,x2,y2);
|
|
|
}
|
|
|
|
|
|
this->RealRayIntegrator->Initialize(vol, this->Scalars);
|
|
|
|
|
|
// Here is the Zsweep algorithm:
|
|
|
|
|
|
// 1. For each vertex, find the list of incident faces (the "use set") (3.1)
|
|
|
// In the original paper, it deals with incident cells but the chapter about
|
|
|
// the parallel version in the dissertation deals with faces, which makes
|
|
|
// more sense. Hence, there is no need for the sparsification step (3.5.1)
|
|
|
// It is view-independent, so it can be reused for the next call to Render()
|
|
|
// if the dataset did not change.
|
|
|
vtkDebugMacro(<<"BuildUseSets: start");
|
|
|
this->BuildUseSets();
|
|
|
vtkDebugMacro(<<"BuildUseSets: done");
|
|
|
|
|
|
// 2. Sort the vertices by z-coordinates (view-dependent) in view space.
|
|
|
// For each vertex, compute its camera coordinates and sort it
|
|
|
// by z in an heap. The heap is called the "event list".
|
|
|
// The heap stores the Id of the vertices.
|
|
|
// It is view-dependent.
|
|
|
vtkDebugMacro(<<"ProjectAndSortVertices: start");
|
|
|
this->ProjectAndSortVertices(ren,vol);
|
|
|
vtkDebugMacro(<<"ProjectAndSortVertices: done");
|
|
|
|
|
|
// 3. Create an empty "pixel list" (two way linked list) for each pixel of
|
|
|
// the screen.
|
|
|
vtkDebugMacro(<<"CreateAndCleanPixelList: start");
|
|
|
this->CreateAndCleanPixelList();
|
|
|
vtkDebugMacro(<<"CreateAndCleanPixelList: done");
|
|
|
|
|
|
// 4. Main loop
|
|
|
// (section 2 paragraph 11)
|
|
|
vtkDebugMacro(<<"MainLoop: start");
|
|
|
this->MainLoop(ren->GetRenderWindow());
|
|
|
vtkDebugMacro(<<"MainLoop: done");
|
|
|
|
|
|
// The algorithm is done: send to result to the final image.
|
|
|
if ( !ren->GetRenderWindow()->GetAbortRender() )
|
|
|
{
|
|
|
float depth;
|
|
|
if ( this->IntermixIntersectingGeometry )
|
|
|
{
|
|
|
depth = this->GetMinimumBoundsDepth( ren, vol );
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
depth = -1;
|
|
|
}
|
|
|
|
|
|
// copy the double image into the unsigned char image:
|
|
|
|
|
|
j=0;
|
|
|
while(j<bufferSize)
|
|
|
{
|
|
|
float alpha=this->RealRGBAImage[j+3];
|
|
|
if(alpha!=0)
|
|
|
{
|
|
|
this->Image[j]=this->ColorComponentRealToByte(this->RealRGBAImage[j]/alpha);
|
|
|
this->Image[j+1]=this->ColorComponentRealToByte(this->RealRGBAImage[j+1]/alpha);
|
|
|
this->Image[j+2]=this->ColorComponentRealToByte(this->RealRGBAImage[j+2]/alpha);
|
|
|
this->Image[j+3]=this->ColorComponentRealToByte(alpha);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->Image[j]=0;
|
|
|
this->Image[j+1]=0;
|
|
|
this->Image[j+2]=0;
|
|
|
this->Image[j+3]=0;
|
|
|
}
|
|
|
j+=4;
|
|
|
}
|
|
|
this->ImageDisplayHelper->
|
|
|
RenderTexture( vol, ren,
|
|
|
this->ImageMemorySize,
|
|
|
this->ImageViewportSize,
|
|
|
this->ImageInUseSize,
|
|
|
this->ImageOrigin,
|
|
|
depth,
|
|
|
this->Image );
|
|
|
|
|
|
this->Timer->StopTimer();
|
|
|
this->TimeToDraw = this->Timer->GetElapsedTime();
|
|
|
this->StoreRenderTime( ren, vol, this->TimeToDraw );
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->ImageSampleDistance = oldImageSampleDistance;
|
|
|
}
|
|
|
|
|
|
if ( this->ZBuffer )
|
|
|
{
|
|
|
delete [] this->ZBuffer;
|
|
|
this->ZBuffer = NULL;
|
|
|
}
|
|
|
|
|
|
this->UpdateProgress(1.0);
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::AllocateUseSet(vtkIdType size)
|
|
|
{
|
|
|
if(this->UseSet!=0)
|
|
|
{
|
|
|
if(size>static_cast<vtkIdType>(this->UseSet->Vector.size()))
|
|
|
{
|
|
|
delete this->UseSet;
|
|
|
this->UseSet=new vtkUseSet(size);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->UseSet->Clear();
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->UseSet=new vtkUseSet(size);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::AllocateVertices(vtkIdType size)
|
|
|
{
|
|
|
if(this->Vertices!=0)
|
|
|
{
|
|
|
if(size>static_cast<vtkIdType>(this->Vertices->Vector.size()))
|
|
|
{
|
|
|
delete this->Vertices;
|
|
|
this->Vertices=new vtkVertices(size);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->Vertices=new vtkVertices(size);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::BuildUseSets()
|
|
|
{
|
|
|
int needsUpdate = 0;
|
|
|
|
|
|
// If we have never created the list, we need updating
|
|
|
if (this->UseSet==0 )
|
|
|
{
|
|
|
needsUpdate = 1;
|
|
|
}
|
|
|
|
|
|
// If the data has changed in some way then we need to update
|
|
|
vtkUnstructuredGrid *input = this->GetInput();
|
|
|
if ( input->GetMTime() > this->SavedTriangleListMTime.GetMTime() )
|
|
|
{
|
|
|
needsUpdate = 1;
|
|
|
}
|
|
|
|
|
|
|
|
|
// If we don't need updating, return
|
|
|
if ( !needsUpdate )
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
vtkIdType numberOfCells=input->GetNumberOfCells();
|
|
|
vtkIdType numberOfPoints=input->GetNumberOfPoints();
|
|
|
|
|
|
// init the use set of each vertex
|
|
|
this->AllocateUseSet(numberOfPoints);
|
|
|
|
|
|
// for each cell
|
|
|
vtkIdType cellIdx=0;
|
|
|
while(cellIdx<numberOfCells)
|
|
|
{
|
|
|
input->GetCell(cellIdx,this->Cell);
|
|
|
|
|
|
vtkIdType faces=this->Cell->GetNumberOfFaces();
|
|
|
vtkIdType faceidx=0;
|
|
|
vtkCell *face;
|
|
|
vtkIdType faceIds[3];
|
|
|
vtkIdType orderedFaceIds[3];
|
|
|
// for each face
|
|
|
while(faceidx<faces)
|
|
|
{
|
|
|
face=this->Cell->GetFace(faceidx);
|
|
|
faceIds[0]=face->GetPointId(0);
|
|
|
faceIds[1]=face->GetPointId(1);
|
|
|
faceIds[2]=face->GetPointId(2);
|
|
|
this->ReorderTriangle(faceIds,orderedFaceIds);
|
|
|
|
|
|
// Add face only if it is not already in the useset.
|
|
|
this->UseSet->AddFace(orderedFaceIds);
|
|
|
++faceidx;
|
|
|
}
|
|
|
++cellIdx;
|
|
|
}
|
|
|
this->SavedTriangleListMTime.Modified();
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Description:
|
|
|
// Reorder vertices `v' in increasing order in `w'. Orientation does not
|
|
|
// matter for the algorithm.
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::ReorderTriangle(vtkIdType v[3],
|
|
|
vtkIdType w[3])
|
|
|
{
|
|
|
if(v[0]>v[1])
|
|
|
{
|
|
|
if(v[1]>v[2])
|
|
|
{
|
|
|
// v[2] is the min
|
|
|
w[0]=v[2];
|
|
|
w[1]=v[0];
|
|
|
w[2]=v[1];
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// v[1] is the min
|
|
|
w[0]=v[1];
|
|
|
w[1]=v[2];
|
|
|
w[2]=v[0];
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(v[0]>v[2])
|
|
|
{
|
|
|
// v[2] is the min
|
|
|
w[0]=v[2];
|
|
|
w[1]=v[0];
|
|
|
w[2]=v[1];
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// v[0] is the min
|
|
|
w[0]=v[0];
|
|
|
w[1]=v[1];
|
|
|
w[2]=v[2];
|
|
|
}
|
|
|
}
|
|
|
// At this point the triangle start with the min id and the
|
|
|
// order did not change
|
|
|
// Now, ensure that the two last id are in increasing order
|
|
|
if(w[1]>w[2])
|
|
|
{
|
|
|
vtkIdType tmp=w[1];
|
|
|
w[1]=w[2];
|
|
|
w[2]=tmp;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::ProjectAndSortVertices(
|
|
|
vtkRenderer *ren,
|
|
|
vtkVolume *vol)
|
|
|
{
|
|
|
assert("pre: empty list" && this->EventList->GetNumberOfItems()==0);
|
|
|
|
|
|
vtkUnstructuredGrid *input = this->GetInput();
|
|
|
vtkIdType numberOfPoints=input->GetNumberOfPoints();
|
|
|
|
|
|
vtkIdType pointId=0;
|
|
|
vtkVertexEntry *vertex=0;
|
|
|
// Pre-computation for the projection.
|
|
|
|
|
|
ren->ComputeAspect();
|
|
|
double *aspect = ren->GetAspect();
|
|
|
|
|
|
// Get the view matrix in two steps - there is a one step method in camera
|
|
|
// but it turns off stereo so we do not want to use that one
|
|
|
vtkCamera *cam = ren->GetActiveCamera();
|
|
|
this->PerspectiveTransform->Identity();
|
|
|
this->PerspectiveTransform->Concatenate(
|
|
|
cam->GetPerspectiveTransformMatrix(aspect[0]/aspect[1], 0.0, 1.0 ));
|
|
|
this->PerspectiveTransform->Concatenate(cam->GetViewTransformMatrix());
|
|
|
this->PerspectiveTransform->Concatenate(vol->GetMatrix());
|
|
|
this->PerspectiveMatrix->DeepCopy(this->PerspectiveTransform->GetMatrix());
|
|
|
|
|
|
this->AllocateVertices(numberOfPoints);
|
|
|
|
|
|
while(pointId<numberOfPoints)
|
|
|
{
|
|
|
vertex=&(this->Vertices->Vector[pointId]);
|
|
|
|
|
|
// Projection
|
|
|
//
|
|
|
double inPoint[4];
|
|
|
input->GetPoint(pointId,inPoint);
|
|
|
inPoint[3] = 1.0;
|
|
|
|
|
|
double outPoint[4];
|
|
|
this->PerspectiveMatrix->MultiplyPoint( inPoint, outPoint );
|
|
|
assert("outPoint[3]" && outPoint[3]!=0.0);
|
|
|
|
|
|
double invW=1/outPoint[3];
|
|
|
double zView = outPoint[2]*invW;
|
|
|
|
|
|
int xScreen=static_cast<int>((outPoint[0]*invW+1)*0.5*this->ImageViewportSize[0]-this->ImageOrigin[0]);
|
|
|
int yScreen=static_cast<int>((outPoint[1]*invW+1)*0.5*this->ImageViewportSize[1]-this->ImageOrigin[1]);
|
|
|
|
|
|
double outWorldPoint[4];
|
|
|
|
|
|
vol->GetMatrix()->MultiplyPoint( inPoint, outWorldPoint );
|
|
|
|
|
|
assert("check: vol no projection" && outWorldPoint[3]==1);
|
|
|
|
|
|
double scalar;
|
|
|
if(this->CellScalars) // cell attribute
|
|
|
{
|
|
|
assert(0);
|
|
|
// scalar=this->Scalars->GetComponent(cellIdx,0);
|
|
|
scalar=0;
|
|
|
}
|
|
|
else // point attribute
|
|
|
{
|
|
|
scalar=this->Scalars->GetComponent(pointId,0);
|
|
|
}
|
|
|
|
|
|
vertex->Set(xScreen,yScreen,outWorldPoint[0]/outWorldPoint[3],
|
|
|
outWorldPoint[1]/outWorldPoint[3],
|
|
|
outWorldPoint[2]/outWorldPoint[3],zView,scalar,invW);
|
|
|
|
|
|
// Sorting
|
|
|
//
|
|
|
// we store -z because the top of the priority list is the
|
|
|
// smallest value
|
|
|
#ifdef BACK_TO_FRONT
|
|
|
this->EventList->Insert(-zView,pointId);
|
|
|
#else
|
|
|
this->EventList->Insert(zView,pointId);
|
|
|
#endif
|
|
|
++pointId;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::CreateAndCleanPixelList()
|
|
|
{
|
|
|
// paper: a "pixel list" is a double linked list. We put that in a queue.
|
|
|
vtkIdType size=this->ImageInUseSize[0]*this->ImageInUseSize[1];
|
|
|
if(this->PixelListFrame!=0)
|
|
|
{
|
|
|
if(this->PixelListFrame->GetSize()<size)
|
|
|
{
|
|
|
delete this->PixelListFrame;
|
|
|
this->PixelListFrame=0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if(this->PixelListFrame==0)
|
|
|
{
|
|
|
this->PixelListFrame=new vtkPixelListFrame(size);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::MainLoop(vtkRenderWindow *renWin)
|
|
|
{
|
|
|
double previousZTarget=0.0;
|
|
|
double zTarget;
|
|
|
vtkIdType vertex;
|
|
|
|
|
|
// used to know if the next vertex is on the same plane
|
|
|
double currentZ; // than the previous one. If so, the z-target has to be
|
|
|
// updated (without calling the compositing function)
|
|
|
if(this->EventList->GetNumberOfItems()==0)
|
|
|
{
|
|
|
return; // we are done.
|
|
|
}
|
|
|
|
|
|
// initialize the "previous z-target" to the z-coordinate of the first
|
|
|
// vertex.
|
|
|
vertex=this->EventList->Peek(0,previousZTarget);
|
|
|
|
|
|
#ifdef BACK_TO_FRONT
|
|
|
previousZTarget=-previousZTarget; // because the EventList store -z
|
|
|
#endif
|
|
|
|
|
|
// (section 2 paragraph 11)
|
|
|
// initialize the "z-target" with the maximum z-coordinate of the adjacent
|
|
|
// vertices to the first vertex. The adjacent vertices can be found
|
|
|
// indirectly by using the "use set" of the first vertex (cells), and
|
|
|
// by taking the vertices of all those cells.
|
|
|
//
|
|
|
zTarget=previousZTarget;
|
|
|
vtkstd::list<vtkFace *>::iterator it;
|
|
|
vtkstd::list<vtkFace *>::iterator itEnd;
|
|
|
|
|
|
// this->MaxRecordedPixelListSize=0;
|
|
|
this->MaxPixelListSizeReached=0;
|
|
|
this->XBounds[0]=this->ImageInUseSize[0];
|
|
|
this->XBounds[1]=0;
|
|
|
this->YBounds[0]=this->ImageInUseSize[1];
|
|
|
this->YBounds[1]=0;
|
|
|
|
|
|
vtkIdType progressCount=0;
|
|
|
vtkIdType sum=this->EventList->GetNumberOfItems();
|
|
|
|
|
|
if(this->MemoryManager==0)
|
|
|
{
|
|
|
this->MemoryManager=new vtkPixelListEntryMemory;
|
|
|
}
|
|
|
|
|
|
this->UseSet->SetNotRendered();
|
|
|
|
|
|
int aborded=0;
|
|
|
// for each vertex of the "event list"
|
|
|
while(this->EventList->GetNumberOfItems()>0)
|
|
|
{
|
|
|
this->UpdateProgress(static_cast<double>(progressCount)/sum);
|
|
|
|
|
|
aborded=renWin->CheckAbortStatus();
|
|
|
if(aborded)
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
++progressCount;
|
|
|
// the z coordinate of the current vertex defines the "sweep plane".
|
|
|
vertex=this->EventList->Pop(0,currentZ);
|
|
|
|
|
|
if(this->UseSet->Vector[vertex]!=0)
|
|
|
{ // otherwise the vertex is not useful, basically this is the
|
|
|
// end we reached the last ztarget
|
|
|
|
|
|
#ifdef BACK_TO_FRONT
|
|
|
currentZ=-currentZ; // because the EventList store -z
|
|
|
#endif
|
|
|
|
|
|
if(previousZTarget==currentZ)
|
|
|
{
|
|
|
// the new vertex is on the same sweep plane than the previous vertex
|
|
|
// that defined a z target
|
|
|
// => the z target has to be updated accordingly
|
|
|
// This is also the case for the first vertex.
|
|
|
it=this->UseSet->Vector[vertex]->begin();
|
|
|
itEnd=this->UseSet->Vector[vertex]->end();
|
|
|
|
|
|
// for each face incident with the vertex
|
|
|
while(it!=itEnd)
|
|
|
{
|
|
|
vtkFace *face=(*it);
|
|
|
// for each point of the face, get the closest z
|
|
|
vtkIdType *vids=face->GetFaceIds();
|
|
|
vtkIdType i=0;
|
|
|
while(i<3)
|
|
|
{
|
|
|
double z=this->Vertices->Vector[vids[i]].GetZview();
|
|
|
#ifdef BACK_TO_FRONT
|
|
|
if(z<zTarget)
|
|
|
#else
|
|
|
if(z>zTarget)
|
|
|
#endif
|
|
|
{
|
|
|
zTarget=z;
|
|
|
}
|
|
|
++i;
|
|
|
}
|
|
|
++it;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Time to call the composite function?
|
|
|
#ifdef BACK_TO_FRONT
|
|
|
if(currentZ<zTarget)
|
|
|
#else
|
|
|
if(currentZ>zTarget)
|
|
|
#endif
|
|
|
{
|
|
|
this->CompositeFunction(zTarget);
|
|
|
|
|
|
// Update the zTarget
|
|
|
previousZTarget=zTarget;
|
|
|
|
|
|
it=this->UseSet->Vector[vertex]->begin();
|
|
|
itEnd=this->UseSet->Vector[vertex]->end();
|
|
|
// for each cell incident with the vertex
|
|
|
while(it!=itEnd)
|
|
|
{
|
|
|
vtkFace *face=(*it);
|
|
|
// for each point of the face, get the closest z
|
|
|
vtkIdType *vids=face->GetFaceIds();
|
|
|
vtkIdType i=0;
|
|
|
while(i<3)
|
|
|
{
|
|
|
double z=this->Vertices->Vector[vids[i]].GetZview();
|
|
|
#ifdef BACK_TO_FRONT
|
|
|
if(z<zTarget)
|
|
|
#else
|
|
|
if(z>zTarget)
|
|
|
#endif
|
|
|
{
|
|
|
zTarget=z;
|
|
|
}
|
|
|
++i;
|
|
|
}
|
|
|
++it;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(this->MaxPixelListSizeReached)
|
|
|
{
|
|
|
this->CompositeFunction(currentZ);
|
|
|
// We do not update the zTarget in this case.
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// use the "use set" (cells) of the vertex to get the cells that are
|
|
|
// incident on the vertex, and that have this vertex as
|
|
|
// minimal z-coordinate,
|
|
|
|
|
|
it=this->UseSet->Vector[vertex]->begin();
|
|
|
itEnd=this->UseSet->Vector[vertex]->end();
|
|
|
|
|
|
while(it!=itEnd)
|
|
|
{
|
|
|
vtkFace *face=(*it);
|
|
|
if(!face->GetRendered())
|
|
|
{
|
|
|
vtkIdType *vids=face->GetFaceIds();
|
|
|
this->RasterizeFace(vids);
|
|
|
face->SetRendered(1);
|
|
|
}
|
|
|
#if 0 // face search
|
|
|
// for each point of the face, get the closest z
|
|
|
vtkIdType *vids=face->GetFaceIds();
|
|
|
vtkIdType minVertex=vids[0];
|
|
|
double farestZ=this->Vertices->Vector[vids[0]].GetZview();
|
|
|
|
|
|
vtkIdType i=1;
|
|
|
while(i<3)
|
|
|
{
|
|
|
double z=this->Vertices->Vector[vids[i]].GetZview();
|
|
|
#ifdef BACK_TO_FRONT
|
|
|
if(z>farestZ)
|
|
|
#else
|
|
|
if(z<farestZ)
|
|
|
#endif
|
|
|
{
|
|
|
farestZ=z;
|
|
|
minVertex=vids[i];
|
|
|
}
|
|
|
++i;
|
|
|
}
|
|
|
if(minVertex==vertex)
|
|
|
{
|
|
|
// if(face->GetRendered())
|
|
|
// {
|
|
|
// cout<<"FACE ALREADY RENDERED!!!!"<<endl;
|
|
|
// }
|
|
|
this->RasterizeFace(vids);
|
|
|
// face->SetRendered(1);
|
|
|
}
|
|
|
#endif // face search
|
|
|
++it;
|
|
|
}
|
|
|
} // if useset of vertex is not null
|
|
|
} // while(eventList->GetNumberOfItems()>0)
|
|
|
|
|
|
if(!aborded)
|
|
|
{
|
|
|
// Here a final compositing
|
|
|
vtkDebugMacro(<<"Flush Compositing");
|
|
|
// this->SavePixelListFrame();
|
|
|
#ifdef BACK_TO_FRONT
|
|
|
this->CompositeFunction(-2);
|
|
|
#else
|
|
|
this->CompositeFunction(2);
|
|
|
#endif
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->EventList->Reset();
|
|
|
}
|
|
|
this->PixelListFrame->Clean(this->MemoryManager);
|
|
|
// vtkDebugMacro(<<"MaxRecordedPixelListSize="<<this->MaxRecordedPixelListSize);
|
|
|
|
|
|
assert("post: empty_list" && this->EventList->GetNumberOfItems()==0);
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::SavePixelListFrame()
|
|
|
{
|
|
|
vtkPolyData *dataset=vtkPolyData::New();
|
|
|
|
|
|
vtkIdType height=this->ImageInUseSize[1];
|
|
|
vtkIdType width=this->ImageInUseSize[0];
|
|
|
vtkPixelListEntry *current;
|
|
|
vtkIdType i;
|
|
|
|
|
|
vtkPoints *pts=vtkPoints::New();
|
|
|
pts->SetDataTypeToDouble();
|
|
|
|
|
|
vtkDoubleArray *dataArray=vtkDoubleArray::New();
|
|
|
vtkCellArray *vertices=vtkCellArray::New();
|
|
|
vtkIdType pointId=0;
|
|
|
|
|
|
// height=151;
|
|
|
// width=151;
|
|
|
|
|
|
vtkIdType y=0; //150;
|
|
|
while(y<height)
|
|
|
{
|
|
|
vtkIdType x=0; //150;
|
|
|
while(x<width)
|
|
|
{
|
|
|
i=y*this->ImageInUseSize[0]+x;
|
|
|
current=this->PixelListFrame->GetFirst(i);
|
|
|
while(current!=0)
|
|
|
{
|
|
|
double *values=current->GetValues();
|
|
|
|
|
|
double point[3];
|
|
|
point[0]=x;
|
|
|
point[1]=y;
|
|
|
point[2]=values[2]; // zWorld
|
|
|
|
|
|
pts->InsertNextPoint(point);
|
|
|
dataArray->InsertNextValue(values[3]);
|
|
|
vertices->InsertNextCell(1,&pointId);
|
|
|
current=current->GetNext();
|
|
|
++pointId;
|
|
|
}
|
|
|
++x;
|
|
|
}
|
|
|
++y;
|
|
|
}
|
|
|
dataset->SetPoints(pts);
|
|
|
pts->Delete();
|
|
|
dataset->SetVerts(vertices);
|
|
|
vertices->Delete();
|
|
|
dataset->GetPointData()->SetScalars(dataArray);
|
|
|
dataArray->Delete();
|
|
|
|
|
|
vtkXMLPolyDataWriter *writer=vtkXMLPolyDataWriter::New();
|
|
|
writer->SetFileName("pixellistframe.vtp");
|
|
|
writer->SetInput(dataset);
|
|
|
writer->SetIdTypeToInt32();
|
|
|
dataset->Delete();
|
|
|
writer->Write();
|
|
|
writer->Delete();
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Description:
|
|
|
// Perform a scan conversion of a triangle, interpolating z and the scalar.
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::RasterizeFace(vtkIdType faceIds[3])
|
|
|
{
|
|
|
// The triangle is splitted by an horizontal line passing through the
|
|
|
// second vertex v1 (y-order)
|
|
|
// Hence, on one side there one edge (v0v2), on the other side there are two
|
|
|
// edges (v0v1 and v1v2).
|
|
|
|
|
|
vtkVertexEntry *v0=&(this->Vertices->Vector[faceIds[0]]);
|
|
|
vtkVertexEntry *v1=&(this->Vertices->Vector[faceIds[1]]);
|
|
|
vtkVertexEntry *v2=&(this->Vertices->Vector[faceIds[2]]);
|
|
|
|
|
|
this->RasterizeTriangle(v0,v1,v2);
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Description:
|
|
|
// Perform a scan conversion of a triangle, interpolating z and the scalar.
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::RasterizeTriangle(
|
|
|
vtkVertexEntry *ve0,
|
|
|
vtkVertexEntry *ve1,
|
|
|
vtkVertexEntry *ve2
|
|
|
)
|
|
|
{
|
|
|
assert("pre: ve0_exists" && ve0!=0);
|
|
|
assert("pre: ve1_exists" && ve1!=0);
|
|
|
assert("pre: ve2_exists" && ve2!=0);
|
|
|
|
|
|
vtkVertexEntry *v0=ve0;
|
|
|
vtkVertexEntry *v1=ve1;
|
|
|
vtkVertexEntry *v2=ve2;
|
|
|
|
|
|
// The triangle is splitted by an horizontal line passing through the
|
|
|
// second vertex v1 (y-order)
|
|
|
// Hence, on one side there one edge (v0v2), on the other side there are two
|
|
|
// edges (v0v1 and v1v2).
|
|
|
|
|
|
// Order vertices by y screen.
|
|
|
|
|
|
vtkVertexEntry *tmp;
|
|
|
|
|
|
if(v0->GetScreenY()>v1->GetScreenY())
|
|
|
{
|
|
|
tmp=v0;
|
|
|
v0=v1;
|
|
|
v1=tmp;
|
|
|
}
|
|
|
if(v0->GetScreenY()>v2->GetScreenY())
|
|
|
{
|
|
|
tmp=v1;
|
|
|
v1=v0;
|
|
|
v0=v2;
|
|
|
v2=tmp;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(v1->GetScreenY()>v2->GetScreenY())
|
|
|
{
|
|
|
tmp=v1;
|
|
|
v1=v2;
|
|
|
v2=tmp;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if(v0->GetScreenY()<this->YBounds[0])
|
|
|
{
|
|
|
if(v0->GetScreenY()>=0)
|
|
|
{
|
|
|
this->YBounds[0]=v0->GetScreenY();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->YBounds[0]=0;
|
|
|
}
|
|
|
}
|
|
|
if(v2->GetScreenY()>this->YBounds[1])
|
|
|
{
|
|
|
if(v2->GetScreenY()<this->ImageInUseSize[1])
|
|
|
{
|
|
|
this->YBounds[1]=v2->GetScreenY();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->YBounds[1]=this->ImageInUseSize[1]-1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int x=v0->GetScreenX();
|
|
|
|
|
|
if(x<this->XBounds[0])
|
|
|
{
|
|
|
if(x>=0)
|
|
|
{
|
|
|
this->XBounds[0]=x;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->XBounds[0]=0;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(x>this->XBounds[1])
|
|
|
{
|
|
|
if(x<this->ImageInUseSize[0])
|
|
|
{
|
|
|
this->XBounds[1]=x;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->XBounds[1]=this->ImageInUseSize[0]-1;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
x=v1->GetScreenX();
|
|
|
|
|
|
if(x<this->XBounds[0])
|
|
|
{
|
|
|
if(x>=0)
|
|
|
{
|
|
|
this->XBounds[0]=x;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->XBounds[0]=0;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(x>this->XBounds[1])
|
|
|
{
|
|
|
if(x<this->ImageInUseSize[0])
|
|
|
{
|
|
|
this->XBounds[1]=x;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->XBounds[1]=this->ImageInUseSize[0]-1;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
x=v2->GetScreenX();
|
|
|
|
|
|
if(x<this->XBounds[0])
|
|
|
{
|
|
|
if(x>=0)
|
|
|
{
|
|
|
this->XBounds[0]=x;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->XBounds[0]=0;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(x>this->XBounds[1])
|
|
|
{
|
|
|
if(x<this->ImageInUseSize[0])
|
|
|
{
|
|
|
this->XBounds[1]=x;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this->XBounds[1]=this->ImageInUseSize[0]-1;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int dy20=v2->GetScreenY()-v0->GetScreenY();
|
|
|
int dx10=v1->GetScreenX()-v0->GetScreenX();
|
|
|
int dx20=v2->GetScreenX()-v0->GetScreenX();
|
|
|
int dy10=v1->GetScreenY()-v0->GetScreenY();
|
|
|
|
|
|
int det=dy20*dx10-dx20*dy10;
|
|
|
|
|
|
vtkScreenEdge *leftEdge=0;
|
|
|
vtkScreenEdge *rightEdge=0;
|
|
|
|
|
|
if(det==0) //v0v1v2 aligned or v0=v1=v2
|
|
|
{
|
|
|
// easy case: v0=v1=v2 render the 3 points
|
|
|
if(v0->GetScreenX()==v1->GetScreenX() && v0->GetScreenX()==v2->GetScreenX()
|
|
|
&& v0->GetScreenY()==v1->GetScreenY()
|
|
|
&& v0->GetScreenY()==v2->GetScreenY())
|
|
|
{
|
|
|
x=v0->GetScreenX();
|
|
|
int y=v0->GetScreenY();
|
|
|
if(x>=0 && x<this->ImageInUseSize[0] && y>=0 &&
|
|
|
y<this->ImageInUseSize[1])
|
|
|
{
|
|
|
vtkIdType i=y*this->ImageInUseSize[0]+x;
|
|
|
// Write the pixel
|
|
|
vtkPixelListEntry *p0=this->MemoryManager->AllocateEntry();
|
|
|
p0->Init(v0->GetValues(),v0->GetZview());
|
|
|
this->PixelListFrame->AddAndSort(i,p0);
|
|
|
|
|
|
vtkPixelListEntry *p1=this->MemoryManager->AllocateEntry();
|
|
|
p1->Init(v1->GetValues(),v1->GetZview());
|
|
|
this->PixelListFrame->AddAndSort(i,p1);
|
|
|
|
|
|
vtkPixelListEntry *p2=this->MemoryManager->AllocateEntry();
|
|
|
p2->Init(v2->GetValues(),v2->GetZview());
|
|
|
this->PixelListFrame->AddAndSort(i,p2);
|
|
|
|
|
|
// if(this->PixelListFrame->GetListSize(i)>this->MaxRecordedPixelListSize)
|
|
|
// {
|
|
|
// this->MaxRecordedPixelListSize=this->PixelListFrame->GetListSize(i);
|
|
|
// }
|
|
|
|
|
|
if(!this->MaxPixelListSizeReached)
|
|
|
{
|
|
|
this->MaxPixelListSizeReached=this->PixelListFrame->GetListSize(i)>
|
|
|
this->MaxPixelListSize;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else // line
|
|
|
{
|
|
|
this->RasterizeLine(v0,v1);
|
|
|
this->RasterizeLine(v1,v2);
|
|
|
this->RasterizeLine(v0,v2);
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(det>0) //v0v1 on right
|
|
|
{
|
|
|
this->DoubleEdge->Init(v0,v1,v2,dx10,dy10,1); // true=on right
|
|
|
rightEdge=this->DoubleEdge;
|
|
|
this->SimpleEdge->Init(v0,v2,dx20,dy20,0);
|
|
|
leftEdge=this->SimpleEdge;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// v0v1 on left
|
|
|
this->DoubleEdge->Init(v0,v1,v2,dx10,dy10,0); // true=on right
|
|
|
leftEdge=this->DoubleEdge;
|
|
|
this->SimpleEdge->Init(v0,v2,dx20,dy20,1);
|
|
|
rightEdge=this->SimpleEdge;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int y=v0->GetScreenY();
|
|
|
int y1=v1->GetScreenY();
|
|
|
int y2=v2->GetScreenY();
|
|
|
|
|
|
int skipped=0;
|
|
|
|
|
|
if(y1>=0) // clipping
|
|
|
{
|
|
|
|
|
|
if(y1>=this->ImageInUseSize[1]) // clipping
|
|
|
{
|
|
|
y1=this->ImageInUseSize[1]-1;
|
|
|
}
|
|
|
|
|
|
while(y<=y1)
|
|
|
{
|
|
|
if(y>=0 && y<this->ImageInUseSize[1]) // clipping
|
|
|
{
|
|
|
this->RasterizeSpan(y,leftEdge,rightEdge);
|
|
|
}
|
|
|
++y;
|
|
|
if(y<=y1)
|
|
|
{
|
|
|
leftEdge->NextLine(y);
|
|
|
rightEdge->NextLine(y);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
leftEdge->SkipLines(y1-y,y1);
|
|
|
rightEdge->SkipLines(y1-y,y1);
|
|
|
y=y1;
|
|
|
skipped=1;
|
|
|
}
|
|
|
|
|
|
if(y<this->ImageInUseSize[1]) // clipping
|
|
|
{
|
|
|
leftEdge->OnBottom(skipped,y);
|
|
|
rightEdge->OnBottom(skipped,y);
|
|
|
|
|
|
if(y2>=this->ImageInUseSize[1]) // clipping
|
|
|
{
|
|
|
y2=this->ImageInUseSize[1]-1;
|
|
|
}
|
|
|
|
|
|
while(y<=y2)
|
|
|
{
|
|
|
if(y>=0) // clipping, needed in case of no top
|
|
|
{
|
|
|
this->RasterizeSpan(y,leftEdge,rightEdge);
|
|
|
}
|
|
|
++y;
|
|
|
leftEdge->NextLine(y);
|
|
|
rightEdge->NextLine(y);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::RasterizeSpan(int y,
|
|
|
vtkScreenEdge *left,
|
|
|
vtkScreenEdge *right)
|
|
|
{
|
|
|
assert("pre: left_exists" && left!=0);
|
|
|
assert("pre: right_exists" && right!=0);
|
|
|
|
|
|
vtkIdType i=y*this->ImageInUseSize[0];
|
|
|
|
|
|
this->Span->Init(left->GetX(),
|
|
|
left->GetInvW(),
|
|
|
left->GetPValues(),
|
|
|
left->GetZview(),
|
|
|
right->GetX(),
|
|
|
right->GetInvW(),
|
|
|
right->GetPValues(),
|
|
|
right->GetZview());
|
|
|
|
|
|
while(!this->Span->IsAtEnd())
|
|
|
{
|
|
|
int x=this->Span->GetX();
|
|
|
if(x>=0 && x<this->ImageInUseSize[0]) // clipping
|
|
|
{
|
|
|
vtkIdType j=i+x;
|
|
|
// Write the pixel
|
|
|
vtkPixelListEntry *p=this->MemoryManager->AllocateEntry();
|
|
|
p->Init(this->Span->GetValues(),this->Span->GetZview());
|
|
|
this->PixelListFrame->AddAndSort(j,p);
|
|
|
|
|
|
|
|
|
// if(this->PixelListFrame->GetListSize(j)>this->MaxRecordedPixelListSize)
|
|
|
// {
|
|
|
// this->MaxRecordedPixelListSize=this->PixelListFrame->GetListSize(j);
|
|
|
// }
|
|
|
|
|
|
if(!this->MaxPixelListSizeReached)
|
|
|
{
|
|
|
this->MaxPixelListSizeReached=this->PixelListFrame->GetListSize(j)>
|
|
|
this->MaxPixelListSize;
|
|
|
}
|
|
|
}
|
|
|
this->Span->NextPixel();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
enum
|
|
|
{
|
|
|
VTK_LINE_CONSTANT=0,
|
|
|
VTK_LINE_BRESENHAM,
|
|
|
VTK_LINE_DIAGONAL
|
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::RasterizeLine(vtkVertexEntry *v0,
|
|
|
vtkVertexEntry *v1)
|
|
|
{
|
|
|
assert("pre: v0_exists" && v0!=0);
|
|
|
assert("pre: v1_exists" && v1!=0);
|
|
|
assert("pre: y_ordered" && v0->GetScreenY()<=v1->GetScreenY());
|
|
|
|
|
|
int lineCase;
|
|
|
int xIncrement; // if true increment x, if false increment y
|
|
|
int dx;
|
|
|
int dy;
|
|
|
int xSign;
|
|
|
|
|
|
// initialization is not useful, it is just to remove compiler warnings
|
|
|
int dx2=0;
|
|
|
int dy2=0;
|
|
|
int e=0;
|
|
|
|
|
|
double values[VTK_VALUES_SIZE];
|
|
|
double pValues[VTK_VALUES_SIZE];
|
|
|
|
|
|
double dPv[VTK_VALUES_SIZE];
|
|
|
double dInvW;
|
|
|
double dZ;
|
|
|
|
|
|
double zView;
|
|
|
double invW;
|
|
|
|
|
|
int i;
|
|
|
|
|
|
int x=v0->GetScreenX();
|
|
|
int y=v0->GetScreenY();
|
|
|
|
|
|
// 1. Find the case
|
|
|
dx=v1->GetScreenX()-v0->GetScreenX();
|
|
|
if(dx<0)
|
|
|
{
|
|
|
dx=-dx;
|
|
|
xSign=-1;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
xSign=1;
|
|
|
}
|
|
|
dy=v1->GetScreenY()-v0->GetScreenY();
|
|
|
xIncrement=dx>dy;
|
|
|
if(xIncrement)
|
|
|
{
|
|
|
if(dy==0)
|
|
|
{
|
|
|
lineCase=VTK_LINE_CONSTANT;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
lineCase=VTK_LINE_BRESENHAM;
|
|
|
dx2=dx<<1;
|
|
|
dy2=dy<<1;
|
|
|
e=dx;
|
|
|
}
|
|
|
|
|
|
double invDx=1.0/dx;
|
|
|
i=0;
|
|
|
invW=v0->GetInvW();
|
|
|
double invW1=v1->GetInvW();
|
|
|
double *val0=v0->GetValues();
|
|
|
double *val1=v1->GetValues();
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
values[i]=val0[i];
|
|
|
pValues[i]=values[i]*invW;
|
|
|
dPv[i]=(val1[i]*invW1-pValues[i])*invDx;
|
|
|
++i;
|
|
|
}
|
|
|
dInvW=(invW1-invW)*invDx;
|
|
|
zView=v0->GetZview();
|
|
|
dZ=(v1->GetZview()-zView)*invDx;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(dx==0)
|
|
|
{
|
|
|
if(dy==0)
|
|
|
{
|
|
|
// render both points and return
|
|
|
// write pixel
|
|
|
if(x>=0 && x<this->ImageInUseSize[0] && y>=0 &&
|
|
|
y<this->ImageInUseSize[1]) // clipping
|
|
|
{
|
|
|
vtkIdType j=y*this->ImageInUseSize[0]+x; // mult==bad!!
|
|
|
// Write the pixel
|
|
|
vtkPixelListEntry *p0=this->MemoryManager->AllocateEntry();
|
|
|
p0->Init(v0->GetValues(),v0->GetZview());
|
|
|
this->PixelListFrame->AddAndSort(j,p0);
|
|
|
|
|
|
// Write the pixel
|
|
|
vtkPixelListEntry *p1=this->MemoryManager->AllocateEntry();
|
|
|
p1->Init(v1->GetValues(),v1->GetZview());
|
|
|
this->PixelListFrame->AddAndSort(j,p1);
|
|
|
|
|
|
if(!this->MaxPixelListSizeReached)
|
|
|
{
|
|
|
this->MaxPixelListSizeReached=this->PixelListFrame->GetListSize(j)>
|
|
|
this->MaxPixelListSize;
|
|
|
}
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
lineCase=VTK_LINE_CONSTANT;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(dy==dx)
|
|
|
{
|
|
|
lineCase=VTK_LINE_DIAGONAL;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
lineCase=VTK_LINE_BRESENHAM;
|
|
|
dx2=dx<<1;
|
|
|
dy2=dy<<1;
|
|
|
e=dy;
|
|
|
}
|
|
|
}
|
|
|
double invDy=1.0/dy;
|
|
|
i=0;
|
|
|
invW=v0->GetInvW();
|
|
|
double invW1=v1->GetInvW();
|
|
|
double *val0=v0->GetValues();
|
|
|
double *val1=v1->GetValues();
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
values[i]=val0[i];
|
|
|
pValues[i]=values[i]*invW;
|
|
|
dPv[i]=(val1[i]*invW1-pValues[i])*invDy;
|
|
|
++i;
|
|
|
}
|
|
|
dInvW=(invW1-invW)*invDy;
|
|
|
zView=v0->GetZview();
|
|
|
dZ=(v1->GetZview()-zView)*invDy;
|
|
|
}
|
|
|
|
|
|
// 2. Iterate over each pixel of the straight line.
|
|
|
int done=0;
|
|
|
while(!done)
|
|
|
{
|
|
|
// write pixel
|
|
|
if(x>=0 && x<this->ImageInUseSize[0] && y>=0 &&
|
|
|
y<this->ImageInUseSize[1]) // clipping
|
|
|
{
|
|
|
vtkIdType j=y*this->ImageInUseSize[0]+x; // mult==bad!!
|
|
|
// Write the pixel
|
|
|
vtkPixelListEntry *p0=this->MemoryManager->AllocateEntry();
|
|
|
p0->Init(values,zView);
|
|
|
this->PixelListFrame->AddAndSort(j,p0);
|
|
|
if(!this->MaxPixelListSizeReached)
|
|
|
{
|
|
|
this->MaxPixelListSizeReached=this->PixelListFrame->GetListSize(j)>
|
|
|
this->MaxPixelListSize;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// next pixel
|
|
|
switch(lineCase)
|
|
|
{
|
|
|
case VTK_LINE_CONSTANT:
|
|
|
if(xIncrement)
|
|
|
{
|
|
|
x+=xSign;
|
|
|
if(xSign>0)
|
|
|
{
|
|
|
done=x>v1->GetScreenX();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
done=x<v1->GetScreenX();
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
++y;
|
|
|
done=y>v1->GetScreenY();
|
|
|
}
|
|
|
//<EFBFBD>values, invw, zview
|
|
|
break;
|
|
|
case VTK_LINE_DIAGONAL:
|
|
|
++y;
|
|
|
x+=xSign;
|
|
|
done=y>v1->GetScreenY();
|
|
|
//<EFBFBD>values, invw, zview
|
|
|
break;
|
|
|
case VTK_LINE_BRESENHAM:
|
|
|
if(xIncrement)
|
|
|
{
|
|
|
x+=xSign;
|
|
|
e+=dy2;
|
|
|
if(e>=dx2)
|
|
|
{
|
|
|
e-=dx2;
|
|
|
++y;
|
|
|
}
|
|
|
if(xSign>0)
|
|
|
{
|
|
|
done=x>v1->GetScreenX();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
done=x<v1->GetScreenX();
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
++y;
|
|
|
e+=dx2;
|
|
|
if(e>=dy2)
|
|
|
{
|
|
|
e-=dy2;
|
|
|
x+=xSign;
|
|
|
}
|
|
|
done=y>v1->GetScreenY();
|
|
|
}
|
|
|
// values, invw, zview
|
|
|
break;
|
|
|
}
|
|
|
if(!done)
|
|
|
{
|
|
|
invW+=dInvW;
|
|
|
i=0;
|
|
|
double w=1.0/invW;
|
|
|
while(i<VTK_VALUES_SIZE)
|
|
|
{
|
|
|
pValues[i]+=dPv[i];
|
|
|
values[i]=pValues[i]*w;
|
|
|
++i;
|
|
|
}
|
|
|
zView+=dZ;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void vtkUnstructuredGridVolumeZSweepMapper::CompositeFunction(double zTarget)
|
|
|
{
|
|
|
int y=this->YBounds[0];
|
|
|
vtkIdType i=y*this->ImageInUseSize[0]+this->XBounds[0];
|
|
|
|
|
|
vtkIdType index=(y*this->ImageMemorySize[0]+this->XBounds[0])<< 2; // *4
|
|
|
vtkIdType indexStep=this->ImageMemorySize[0]<<2; // *4
|
|
|
|
|
|
vtkPixelListEntry *current;
|
|
|
vtkPixelListEntry *next;
|
|
|
double zBuffer=0;
|
|
|
|
|
|
int newXBounds[2];
|
|
|
int newYBounds[2];
|
|
|
|
|
|
newXBounds[0]=this->ImageInUseSize[0];
|
|
|
newXBounds[1]=0;
|
|
|
newYBounds[0]=this->ImageInUseSize[1];
|
|
|
newYBounds[1]=0;
|
|
|
|
|
|
int xMin=this->XBounds[0];
|
|
|
int xMax=this->XBounds[1];
|
|
|
int yMax=this->YBounds[1];
|
|
|
|
|
|
vtkPixelList *pixel;
|
|
|
int x;
|
|
|
vtkIdType j;
|
|
|
vtkIdType index2;
|
|
|
int done;
|
|
|
int doIntegration;
|
|
|
double length;
|
|
|
float *color;
|
|
|
|
|
|
// for each pixel in the bounding box
|
|
|
while(y<=yMax)
|
|
|
{
|
|
|
x=xMin;
|
|
|
j=i;
|
|
|
index2=index;
|
|
|
while(x<=xMax)
|
|
|
{
|
|
|
pixel=this->PixelListFrame->GetList(j);
|
|
|
// we need at least two entries per pixel to perform compositing
|
|
|
if(pixel->GetSize()>=2)
|
|
|
{
|
|
|
current=pixel->GetFirst();
|
|
|
next=current->GetNext();
|
|
|
#ifdef BACK_TO_FRONT
|
|
|
done=current->GetZview()<=zTarget || next->GetZview()<=zTarget;
|
|
|
#else
|
|
|
done=current->GetZview()>=zTarget || next->GetZview()>=zTarget;
|
|
|
#endif
|
|
|
|
|
|
if(!done && this->ZBuffer!=0)
|
|
|
{
|
|
|
// value of the z buffer at the current pixel.
|
|
|
zBuffer=this->GetZBufferValue(x,y);
|
|
|
}
|
|
|
|
|
|
while(!done)
|
|
|
{
|
|
|
if(this->ZBuffer!=0)
|
|
|
{
|
|
|
// check that current and next are in front of the z-buffer value
|
|
|
doIntegration=current->GetZview()<zBuffer
|
|
|
&& next->GetZview()<zBuffer;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
doIntegration=1;
|
|
|
}
|
|
|
|
|
|
if(doIntegration)
|
|
|
{
|
|
|
if(current->GetZview()!=next->GetZview())
|
|
|
{
|
|
|
// length in world coordinates
|
|
|
length=sqrt(vtkMath::Distance2BetweenPoints(
|
|
|
current->GetValues(),next->GetValues()));
|
|
|
if(length!=0)
|
|
|
// if(length>=0.4)
|
|
|
{
|
|
|
color=this->RealRGBAImage+index2;
|
|
|
this->IntersectionLengths->SetValue(0,length);
|
|
|
this->NearIntersections->SetValue(0,current->GetValues()[VTK_VALUES_SCALAR_INDEX]);
|
|
|
this->FarIntersections->SetValue(0,next->GetValues()[VTK_VALUES_SCALAR_INDEX]);
|
|
|
#ifdef BACK_TO_FRONT
|
|
|
this->RealRayIntegrator->Integrate(this->IntersectionLengths,
|
|
|
this->FarIntersections,
|
|
|
this->NearIntersections,
|
|
|
color);
|
|
|
#else
|
|
|
this->RealRayIntegrator->Integrate(this->IntersectionLengths,
|
|
|
this->NearIntersections,
|
|
|
this->FarIntersections,
|
|
|
color);
|
|
|
#endif
|
|
|
} // length!=0
|
|
|
} // current->GetZview()!=next->GetZview()
|
|
|
} // doIntegration
|
|
|
|
|
|
// Next entry
|
|
|
pixel->RemoveFirst(this->MemoryManager); // remove current
|
|
|
|
|
|
done=pixel->GetSize()<2; // empty queue?
|
|
|
if(!done)
|
|
|
{
|
|
|
current=next;
|
|
|
next=current->GetNext();
|
|
|
#ifdef BACK_TO_FRONT
|
|
|
done=next->GetZview()<=zTarget;
|
|
|
#else
|
|
|
done=next->GetZview()>=zTarget;
|
|
|
#endif
|
|
|
}
|
|
|
} // while(!done)
|
|
|
}
|
|
|
if(pixel->GetSize()>=2)
|
|
|
{
|
|
|
if(x<newXBounds[0])
|
|
|
{
|
|
|
newXBounds[0]=x;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(x>newXBounds[1])
|
|
|
{
|
|
|
newXBounds[1]=x;
|
|
|
}
|
|
|
}
|
|
|
if(y<newYBounds[0])
|
|
|
{
|
|
|
newYBounds[0]=y;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(y>newYBounds[1])
|
|
|
{
|
|
|
newYBounds[1]=y;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// next abscissa
|
|
|
++j;
|
|
|
index2+=4;
|
|
|
++x;
|
|
|
}
|
|
|
// next ordinate
|
|
|
i=i+this->ImageInUseSize[0];
|
|
|
index+=indexStep;
|
|
|
++y;
|
|
|
}
|
|
|
|
|
|
// Update the bounding box. Useful for the delayed compositing
|
|
|
|
|
|
this->XBounds[0]=newXBounds[0];
|
|
|
this->XBounds[1]=newXBounds[1];
|
|
|
this->YBounds[0]=newYBounds[0];
|
|
|
this->YBounds[1]=newYBounds[1];
|
|
|
|
|
|
this->MaxPixelListSizeReached=0;
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Description:
|
|
|
// Convert and clamp a float color component into a unsigned char.
|
|
|
unsigned char vtkUnstructuredGridVolumeZSweepMapper::ColorComponentRealToByte(
|
|
|
float color)
|
|
|
{
|
|
|
int val=static_cast<int>(color*255.0);
|
|
|
if(val>255)
|
|
|
{
|
|
|
val=255;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(val<0)
|
|
|
{
|
|
|
val=0;
|
|
|
}
|
|
|
}
|
|
|
return static_cast<unsigned char>(val);
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
double vtkUnstructuredGridVolumeZSweepMapper::GetZBufferValue(int x,
|
|
|
int y)
|
|
|
{
|
|
|
int xPos, yPos;
|
|
|
|
|
|
xPos = static_cast<int>(static_cast<float>(x) * this->ImageSampleDistance);
|
|
|
yPos = static_cast<int>(static_cast<float>(y) * this->ImageSampleDistance);
|
|
|
|
|
|
xPos = (xPos >= this->ZBufferSize[0])?(this->ZBufferSize[0]-1):(xPos);
|
|
|
yPos = (yPos >= this->ZBufferSize[1])?(this->ZBufferSize[1]-1):(yPos);
|
|
|
|
|
|
return *(this->ZBuffer + yPos*this->ZBufferSize[0] + xPos);
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
double vtkUnstructuredGridVolumeZSweepMapper::GetMinimumBoundsDepth(
|
|
|
vtkRenderer *ren,
|
|
|
vtkVolume *vol )
|
|
|
{
|
|
|
double bounds[6];
|
|
|
vol->GetBounds( bounds );
|
|
|
|
|
|
ren->ComputeAspect();
|
|
|
double *aspect = ren->GetAspect();
|
|
|
|
|
|
// Get the view matrix in two steps - there is a one step method in camera
|
|
|
// but it turns off stereo so we do not want to use that one
|
|
|
vtkCamera *cam = ren->GetActiveCamera();
|
|
|
this->PerspectiveTransform->Identity();
|
|
|
this->PerspectiveTransform->Concatenate(
|
|
|
cam->GetPerspectiveTransformMatrix(aspect[0]/aspect[1], 0.0, 1.0 ));
|
|
|
this->PerspectiveTransform->Concatenate(cam->GetViewTransformMatrix());
|
|
|
this->PerspectiveMatrix->DeepCopy(this->PerspectiveTransform->GetMatrix());
|
|
|
|
|
|
double minZ = 1.0;
|
|
|
|
|
|
for ( int k = 0; k < 2; k++ )
|
|
|
{
|
|
|
for ( int j = 0; j < 2; j++ )
|
|
|
{
|
|
|
for ( int i = 0; i < 2; i++ )
|
|
|
{
|
|
|
double inPoint[4];
|
|
|
inPoint[0] = bounds[ i];
|
|
|
inPoint[1] = bounds[2+j];
|
|
|
inPoint[2] = bounds[4+k];
|
|
|
inPoint[3] = 1.0;
|
|
|
|
|
|
double outPoint[4];
|
|
|
this->PerspectiveMatrix->MultiplyPoint( inPoint, outPoint );
|
|
|
double testZ = outPoint[2] / outPoint[3];
|
|
|
minZ = ( testZ < minZ ) ? (testZ) : (minZ);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return minZ;
|
|
|
}
|
|
|
|