开发环境:
- Windows 11 家庭中文版
- Microsoft Visual Studio Community 2019
- VTK-9.3.0.rc0
- vtk-example
demo解决问题:创建一个带有盖的球体的可视化图形。程序通过将一个二维曲线沿着y轴旋转,形成三维曲面,从而实现这一目标。
关键点:
- 程序接受命令行参数来自定义可视化效果。这些参数包括弧的角度、旋转步长和弧的半径。另外,还可以选择使用可选参数来取消球体的盖子或者显示用于生成曲面的旋转曲线。
auto pts = GetLine(angle, step, radius, uncapped, start);
// Setup points and lines
vtkNew<vtkPoints> points;
vtkNew<vtkCellArray> lines;
for (auto pt : pts)
{
unsigned long pt_id = points->InsertNextPoint(pt.data());
if (pt_id < pts.size() - 1)
{
vtkNew<vtkLine> line;
line->GetPointIds()->SetId(0, pt_id);
line->GetPointIds()->SetId(1, pt_id + 1);
lines->InsertNextCell(line);
}
}
- 主要函数处理命令行参数,将角度转换为弧度,并利用VTK库设置可视化效果。它通过生成沿着弧的点来创建曲线,然后使用旋转成型来生成带有盖的球体。最终的可视化效果通过VTK的渲染功能在窗口中显示出来。
// Extrude the profile to make the capped sphere
vtkNew<vtkRotationalExtrusionFilter> extrude;
extrude->SetInputData(polydata);
extrude->SetResolution(60);
- 辅助函数:用于根据指定的角度、步长和半径构造曲线上的点。
std::vector<std::array<double, 3>>
GetLine(double const& angle, double const& step, double const& radius,
bool const& uncapped, double const& start)
prj name: CappedSphere
#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkCellArray.h>
#include <vtkLine.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkRotationalExtrusionFilter.h>
#include <array>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
#include <vector>
namespace {
/**
* Get the points for a line.
*
* @param angle: Length of the arc in degrees.
* @param step: Step size in degrees.
* @param radius: Radius of the arc.
* @param uncapped: True if uncapped.
* @param start: Starting angle.
* @return: A vector of points.
*/
std::vector<std::array<double, 3>>
GetLine(double const& angle, double const& step, double const& radius,
bool const& noCap, double const& start);
/**
* Show the command line parameters.
*
* @param fn: The program name.
*/
std::string ShowUsage(std::string fn);
} // namespace
int main(int argc, char* argv[])
{
// Our degree/radian conversions
constexpr auto pi = 3.141592653589793238462643383279502884L; /* pi */
auto d_r = [pi](long double d) { return pi * d / 180.0; };
// auto r_d = [pi](long double r) { return 180 * r / pi; };
auto isNumber = [](std::string const& token) {
return std::regex_match(
token, std::regex(("((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?")));
};
auto angle = 90.0;
auto step = 1.0;
auto radius = 1.0;
auto uncapped = false;
auto showLine = false;
// The command line arguments
std::vector<std::string> cmdVec;
for (auto i = 1; i < argc; ++i)
{
cmdVec.push_back(argv[i]);
}
if (!cmdVec.empty())
{
// Look for parameters
auto posCnt = 0;
for (auto const& token : cmdVec)
{
if (token == "-h" || token == "--help")
{
std::cout << ShowUsage(argv[0]) << std::endl;
return EXIT_SUCCESS;
}
if (token == "-u" || token == "--uncapped")
{
uncapped = true;
}
if (token == "-s" || token == "--show_line")
{
showLine = true;
}
if (isNumber(token) && posCnt < 3)
{
switch (posCnt)
{
case 0:
angle = std::stod(token);
break;
case 1:
step = std::stod(token);
break;
case 2:
radius = std::stod(token);
break;
default:
break;
}
posCnt++;
}
}
}
angle = d_r(std::abs(angle));
step = d_r(std::abs(step));
radius = std::abs(radius);
// With default settings set this to 45 and you get a bowl with a flat bottom.
auto start = d_r(90);
auto pts = GetLine(angle, step, radius, uncapped, start);
// Setup points and lines
vtkNew<vtkPoints> points;
vtkNew<vtkCellArray> lines;
for (auto pt : pts)
{
unsigned long pt_id = points->InsertNextPoint(pt.data());
if (pt_id < pts.size() - 1)
{
vtkNew<vtkLine> line;
line->GetPointIds()->SetId(0, pt_id);
line->GetPointIds()->SetId(1, pt_id + 1);
lines->InsertNextCell(line);
}
}
vtkNew<vtkPolyData> polydata;
polydata->SetPoints(points);
polydata->SetLines(lines);
// Extrude the profile to make the capped sphere
vtkNew<vtkRotationalExtrusionFilter> extrude;
extrude->SetInputData(polydata);
extrude->SetResolution(60);
// Visualize
vtkNew<vtkNamedColors> colors;
// To see the line
vtkNew<vtkPolyDataMapper> lineMapper;
lineMapper->SetInputData(polydata);
vtkNew<vtkActor> lineActor;
lineActor->SetMapper(lineMapper);
lineActor->GetProperty()->SetLineWidth(4);
lineActor->GetProperty()->SetColor(colors->GetColor3d("Red").GetData());
// To see the surface
vtkNew<vtkPolyDataMapper> surfaceMapper;
surfaceMapper->SetInputConnection(extrude->GetOutputPort());
vtkNew<vtkActor> surfaceActor;
surfaceActor->SetMapper(surfaceMapper);
surfaceActor->GetProperty()->SetColor(colors->GetColor3d("Khaki").GetData());
vtkNew<vtkRenderer> ren;
vtkNew<vtkRenderWindow> renWin;
renWin->AddRenderer(ren);
vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renWin);
ren->AddActor(surfaceActor);
if (showLine)
{
ren->AddActor(lineActor);
}
ren->SetBackground(colors->GetColor3d("LightSlateGray").GetData());
ren->ResetCamera();
ren->GetActiveCamera()->Azimuth(0);
ren->GetActiveCamera()->Elevation(60);
ren->ResetCameraClippingRange();
renWin->SetSize(600, 600);
renWin->Render();
renWin->SetWindowName("CappedSphere");
iren->Start();
return EXIT_SUCCESS;
}
namespace {
std::vector<std::array<double, 3>>
GetLine(double const& angle, double const& step, double const& radius,
bool const& uncapped, double const& start)
{
auto constexpr precision = 1.0e-06;
std::vector<std::array<double, 3>> pts;
// Do the curved line
auto theta = 0.0;
while (theta <= angle)
{
std::array<double, 3> p{{0.0, 0.0, 0.0}};
p[0] = radius * std::cos(start - theta);
p[2] = radius * std::sin(theta - start);
if (p[0] < 0)
{
p[0] = 0;
pts.push_back(p);
break;
}
if (std::abs(p[0]) < precision)
{
p[0] = 0;
}
if (std::abs(p[2]) < precision)
{
p[2] = 0;
}
pts.push_back(p);
theta += step;
}
if (!uncapped)
{
// Drop a perpendicular from the last point to the x-axis
if (pts.size() > 1)
{
std::array<double, 3> lastPoint = pts.back();
if (lastPoint[0] > 0)
{
auto numPts = 10;
auto interval = double(numPts) / radius;
auto i = 1;
while (i < numPts)
{
std::array<double, 3> p{{0.0, 0.0, lastPoint[2]}};
p[0] = lastPoint[0] - i / interval;
if (p[0] < 0)
{
p[0] = 0;
pts.push_back(p);
break;
}
if (std::abs(p[0]) < precision)
{
p[0] = 0;
}
if (std::abs(p[2]) < precision)
{
p[2] = 0;
}
pts.push_back(p);
++i;
}
}
lastPoint = pts.back();
if (lastPoint[0] > precision)
{
std::array<double, 3> p{{0.0, 0.0, lastPoint[2]}};
pts.push_back(p);
}
}
}
return pts;
}
std::string ShowUsage(std::string fn)
{
// Remove the folder (if present) then emove the extension in this order
// since the folder name may contain perionds.
auto last_slash_idx = fn.find_last_of("\\/");
if (std::string::npos != last_slash_idx)
{
fn.erase(0, last_slash_idx + 1);
}
// auto period_idx = fn.rfind('.');
// if (std::string::npos != period_idx)
//{
// fn.erase(period_idx);
//}
std::ostringstream os;
os << "\nusage: " << fn << " [-h] [-u] [-s] [angle] [step] [radius]\n";
os << "\n";
os << "Display a capped sphere.\n";
os << "\n";
os << "positional arguments:\n";
os << " angle The length of the arc in degrees from +z to -z in "
"the +x\n";
os << " direction in the x-z plane.\n";
os << " step Step size in degrees.\n";
os << " radius Radius of the arc.\n";
os << "\n";
os << "optional arguments:\n";
os << " -h, --help show this help message and exit\n";
os << " -u, --uncapped Uncap the sphere.\n";
os << " -s, --show_line Show the line that is rotationally extruded to "
"make the\n";
os << " surface.";
os << std::endl;
return os.str();
}
} // namespace