CappedSphere
Repository source: CappedSphere
Description¶
Demonstrates how to create a capped sphere.
Firstly a line is created in the x-z plane corresponding to an arc from +z to -z in the +x direction in the x-z plane, the length of the arc is specified in degrees.
Then the line is extended by dropping a perpendicular to the x-axis.
The points generated are then converted to a line and passed through to the vtkRotationalExtrusionFilter to generate the resultant 3D surface.
The parameters are:
- angle - the arc length in degrees default 90° (a hemisphere)
- step -the step size of the arc in degrees, default 1°
- radius - the radius of the arc default 1
Options are provided to:
- Uncap the sphere (-u, --uncapped)
- Display the line that was rotationally extruded (-s, --show_line)
Note
The coordinate system for specifying the arc is left-handed with 0° aligned with the positive z-axis, 90° aligned with the positive x-axis.
Note
You can substitute different parametric equations for x and z in the line generating function to get other shapes.
Other languages
See (Python), (PythonicAPI)
Question
If you have a question about this example, please use the VTK Discourse Forum
Code¶
CappedSphere.cxx
#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
CMakeLists.txt¶
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(CappedSphere)
find_package(VTK COMPONENTS
CommonColor
CommonCore
CommonDataModel
FiltersModeling
InteractionStyle
RenderingContextOpenGL2
RenderingCore
RenderingFreeType
RenderingGL2PSOpenGL2
RenderingOpenGL2
)
if (NOT VTK_FOUND)
message(FATAL_ERROR "CappedSphere: Unable to find the VTK build folder.")
endif()
# Prevent a "command line is too long" failure in Windows.
set(CMAKE_NINJA_FORCE_RESPONSE_FILE "ON" CACHE BOOL "Force Ninja to use response files.")
add_executable(CappedSphere MACOSX_BUNDLE CappedSphere.cxx )
target_link_libraries(CappedSphere PRIVATE ${VTK_LIBRARIES}
)
# vtk_module_autoinit is needed
vtk_module_autoinit(
TARGETS CappedSphere
MODULES ${VTK_LIBRARIES}
)
Download and Build CappedSphere¶
Click here to download CappedSphere and its CMakeLists.txt file. Once the tarball CappedSphere.tar has been downloaded and extracted,
cd CappedSphere/build
If VTK is installed:
cmake ..
If VTK is not installed but compiled on your system, you will need to specify the path to your VTK build:
cmake -DVTK_DIR:PATH=/home/me/vtk_build ..
Build the project:
make
and run it:
./CappedSphere
WINDOWS USERS
Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.