I am trying draw a primitive scene with multiple polyData objects, write them into .ply file and view it using paraview. So far I am able to display an ellipsoid source, two sets of arrow sources showing axes of cameras placed on the ellipsoid. I display points as spheres using a glyph filter. I want to draw lines representing camera FoV originating from the sets of arrows (see attached images). I create different objects as separate polyData objects and combine them into one polyData using append filter before writing to .ply file. I am struggling with drawing lines that can be visualized in paraview. I must have tried all the VTK examples related to lines. When using vtkLineSource or vtkLine as cells, I can see some geometry at the ends of the lines when viewed on paraview as ‘points gaussian’ (second attached image). But in ‘surface’, I do not see the lines at all. How to draw lines so they can be seen in paraview with the rest of my polyData objects?
edit:
vtk - 9.2
Qt 6.3, Qt creator 8.0
C++
Paraview 5.10.1
//----------------------------- Ellipsoid -----------------------------------------------------------
vtkSmartPointer<vtkParametricEllipsoid> ellipsoid = vtkSmartPointer<vtkParametricEllipsoid>::New();
// set ellipsoid parameters
ellipsoid->SetXRadius(ellipseParams[0]);
ellipsoid->SetYRadius(ellipseParams[1]);
ellipsoid->SetZRadius(ellipseParams[2]);
ellipsoid->SetMaximumV(M_PI/2);
// set ellipsoid center
vtkSmartPointer<vtkParametricFunctionSource> ellipsoidSource = vtkSmartPointer<vtkParametricFunctionSource>::New();
ellipsoidSource->SetParametricFunction(ellipsoid);
ellipsoidSource->SetUResolution(50);
ellipsoidSource->SetVResolution(50);
ellipsoidSource->SetWResolution(50);
ellipsoidSource->Update();
vtkSmartPointer<vtkPolyData> scene = vtkSmartPointer<vtkPolyData>::New();
scene->ShallowCopy(ellipsoidSource->GetOutput());
// -----------------------------------Create another polydata with control points-----------------------------------
auto pointSet = vtkSmartPointer<vtkPoints>::New();
int numControl = controlp.size();
pointSet->SetNumberOfPoints(numControl);
for(vtkIdType i = 0; i < numControl; i++)
{
double point[3];
point[0] = controlp[i][0];
point[1] = controlp[i][1];
point[2] = controlp[i][2];
pointSet->SetPoint(i,point);
}
auto sceneControl = vtkSmartPointer<vtkPolyData>::New();
sceneControl->SetPoints(pointSet);
// pass through glyph3d filter to render points as small spheres and save into new polydata
auto glyphFilter = vtkSmartPointer<vtkGlyph3D>::New();
auto sphereSource = vtkSmartPointer<vtkSphereSource>::New();
sphereSource->SetRadius(0.08);
//sphereSource->SetThetaResolution(21);
//sphereSource->SetPhiResolution(21);
glyphFilter->SetSourceConnection(sphereSource->GetOutputPort());
glyphFilter->SetInputData(sceneControl);
glyphFilter->Update();
auto scenePoints = vtkSmartPointer<vtkPolyData>::New();
scenePoints->ShallowCopy(glyphFilter->GetOutput());
// -------------------------------------Display camera axes----------------------------------------------------------
std::vector<std::vector<vtkSmartPointer<vtkPolyData>>> arrowDatVec(nCam);
std::vector<vtkSmartPointer<vtkPolyData>> linesDatVec(nCam);
for(int camCount = 0; camCount < nCam; camCount++)
{
std::vector<double> optSol = optSolVecs[camCount];
double x = ellipseParams[0] * cos(optSol[0]) * sin(optSol[1]);
double y = ellipseParams[1] * sin(optSol[0]) * sin(optSol[1]);
double z = ellipseParams[2] * cos(optSol[1]);
std::vector<double> camPos({x,y,z});
std::vector<std::vector<double>> camDirs(3);
// camera direction vector from the optimized solution
std::vector<double> camDir = normVec(std::vector<double>({optSol[2],optSol[3],optSol[4]}));
camDirs[2] = camDir;
// camera right vector as a cross product of direction and world up vectors
std::vector<double> worldUp({0.0,0.0,1.0});
std::vector<double> camRight = normVec(crossVec(camDir,worldUp));
camDirs[0] = camRight;
std::vector<double> camUp = normVec(crossVec(camRight,camDir));
camDirs[1] = camUp;
std::vector<vtkSmartPointer<vtkPolyData>> arrowDat(3);
for(int i = 0 ; i < 3; i++)
{
// Create an arrow.
vtkNew<vtkArrowSource> arrowSource;
arrowSource->SetShaftRadius(.01);
arrowSource->SetShaftResolution(20);
arrowSource->SetTipLength(.1);
arrowSource->SetTipRadius(.05);
arrowSource->SetTipResolution(20);
//vtkMath::Normalize(camDirs[i].data());
auto rng = vtkSmartPointer<vtkMinimalStandardRandomSequence>::New();
rng->SetSeed(8775070);
std::vector<double> arbitrary(3);
for(int j = 0; j < 3; j++)
{
rng->Next();
arbitrary[i] = rng->GetRangeValue(-10, 10);
}
std::vector<double> zTemp(3), yTemp(3);
vtkMath::Cross(camDirs[i].data(), arbitrary.data(), zTemp.data());
vtkMath::Normalize(zTemp.data());
vtkMath::Cross(zTemp.data(), camDirs[i].data(), yTemp.data());
vtkNew<vtkMatrix4x4> matrix;
// Create the direction cosine matrix
matrix->Identity();
for (auto j = 0; j < 3; j++)
{
matrix->SetElement(j, 0, camDirs[i][j]);
matrix->SetElement(j, 1, yTemp[j]);
matrix->SetElement(j, 2, zTemp[j]);
}
// Apply the transforms
auto transform = vtkSmartPointer<vtkTransform>::New();
transform->Translate(camPos.data());
transform->Concatenate(matrix);
double length = vtkMath::Norm(camDirs[i].data());
transform->Scale(length, length, length);
// Transform the polydata
auto transformPD = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
transformPD->SetTransform(transform);
transformPD->SetInputConnection(arrowSource->GetOutputPort());
transformPD->Update();
arrowDat[i] = transformPD->GetOutput();
}
arrowDatVec[camCount] = arrowDat;
// --------------------Draw camera FoV----------------------------------
intrinsics intParams;
double hFov = 2 * atan(intParams.h/(2*intParams.f_x));
double vFov = 2 * atan(intParams.v/(2*intParams.f_y));
double halfH = intParams.depth * tan(hFov/2);
double halfV = intParams.depth * tan(vFov/2);
double fovCent[3] = {x+(intParams.depth * camDir[0]), y+(intParams.depth * camDir[1]), z+(intParams.depth * camDir[2])};
double fovTL[3] = {fovCent[0]+(camUp[0]*halfV)+(-camRight[0]*halfH), fovCent[1]+(camUp[1]*halfV)+(-camRight[1]*halfH), fovCent[2]+(camUp[2]*halfV)+(-camRight[2]*halfH)};
double fovBL[3] = {fovTL[0]+(-camUp[0]*2*halfV), fovTL[1]+(-camUp[1]*2*halfV), fovCent[2]+(-camUp[2]*2*halfV)};
double fovBR[3] = {fovBL[0]+(camRight[0]*2*halfH), fovCent[1]+(camRight[1]*2*halfH), fovCent[2]+(camRight[2]*2*halfH)};
double fovTR[3] = {fovBR[0]+(camUp[0]*2*halfV), fovBR[1]+(camUp[1]*2*halfV), fovBR[2]+(camUp[2]*2*halfV)};
// Insert the 5 points defining fov
vtkSmartPointer<vtkPoints> fovPoints = vtkSmartPointer<vtkPoints>::New();
fovPoints->InsertNextPoint(x,y,z);
fovPoints->InsertNextPoint(fovTL);
fovPoints->InsertNextPoint(fovBL);
fovPoints->InsertNextPoint(fovBR);
fovPoints->InsertNextPoint(fovTR);
vtkSmartPointer<vtkPolyData> linesDat = vtkSmartPointer<vtkPolyData>::New();
linesDat->SetPoints(fovPoints);
std::vector<vtkSmartPointer<vtkLine>> lineVec(8);
for(int i = 0; i < 8; i++)
lineVec[i] = vtkSmartPointer<vtkLine>::New();
// Draw 8 lines defining camera fov
lineVec[0]->GetPointIds()->SetId(0,0); // origin to TL
lineVec[0]->GetPointIds()->SetId(1,1);
lineVec[1]->GetPointIds()->SetId(0,0); // origin to BL
lineVec[1]->GetPointIds()->SetId(1,2);
lineVec[2]->GetPointIds()->SetId(0,0); // origin to BR
lineVec[2]->GetPointIds()->SetId(1,3);
lineVec[3]->GetPointIds()->SetId(0,0); // origin to TR
lineVec[3]->GetPointIds()->SetId(1,4);
lineVec[4]->GetPointIds()->SetId(0,1); // TL to BL
lineVec[4]->GetPointIds()->SetId(1,2);
lineVec[5]->GetPointIds()->SetId(0,2); // BL to BR
lineVec[5]->GetPointIds()->SetId(1,3);
lineVec[6]->GetPointIds()->SetId(0,3); // BR to TR
lineVec[6]->GetPointIds()->SetId(1,4);
lineVec[7]->GetPointIds()->SetId(0,4); // TR to TL
lineVec[7]->GetPointIds()->SetId(1,1);
// create cell array and set it to polydata
auto lineCell = vtkSmartPointer<vtkCellArray>::New();
for(int i = 0; i < 8; i++)
lineCell->InsertNextCell(lineVec[i]);
linesDat->SetLines(lineCell);
// Draw lines as tubes
vtkSmartPointer<vtkTubeFilter> tubeFilt = vtkSmartPointer<vtkTubeFilter>::New();
tubeFilt->SetInputData(linesDat);
tubeFilt->SetRadius(5);
tubeFilt->SetNumberOfSides(6);
tubeFilt->Update();
// add poly data into array
linesDatVec[camCount] = tubeFilt->GetOutput();
}
//-------------------------------- Combine all polyData-----------------------------------------------------------------
vtkSmartPointer<vtkAppendPolyData> appendFilter = vtkSmartPointer<vtkAppendPolyData>::New();
appendFilter->AddInputData(scene);
appendFilter->AddInputData(scenePoints);
for(int i = 0; i < nCam; i++)
{
appendFilter->AddInputData(linesDatVec[i]);
for(int j = 0; j < 3; j++)
appendFilter->AddInputData(arrowDatVec[i][j]);
}
//appendFilter->Update();
auto cleanFilter = vtkSmartPointer<vtkCleanPolyData>::New();
cleanFilter->SetInputConnection(appendFilter->GetOutputPort());
cleanFilter->Update();
// --------------------------------write to file-------------------------------------------------------------------------
vtkSmartPointer<vtkPLYWriter> outWrite = vtkSmartPointer<vtkPLYWriter>::New();
std::string fileName = folderPath+"ellipse_test.ply";
outWrite->SetFileName(fileName.c_str());
//outWrite->SetInputData(scenePoints);
outWrite->SetInputConnection(cleanFilter->GetOutputPort());
outWrite->Write();