Cloned library of VTK-5.0.0 with extra build files for internal package management.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4217 lines
112 KiB

/*=========================================================================
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;
}