ParametricObjectsDemo
vtk-examples/Cxx/GeometricObjects/ParametricObjectsDemo
Description¶
Demonstrates the Parametric classes added by Andrew Maclean and additional classes added by Tim Meehan. The parametric spline is also included.
Options are provided to:
- Specify a single surface (-s SURFACE_NAME)
- Save the image (-w)
- Color the back-face (-b)
- Add normals (-n)
For example:
ParametricSurfaces -s RandomHills -w -b -n
Will write out a file called RandomHills.png
and produce an image with all the options enabled.
ParametricSurfaces -w
Will write out a file with no other options enabled called ParametricObjectsDemo.png
.
Note
To really appreciate the complexity of some of these surfaces, select a single surface, and use the options -b -n
. Also try specifying wireframe ( toggle "w" on the keyboard) and zooming in and out.
Tip
If you color the back face, the three-dimensional orientable surfaces will only show backface coloring inside the surface e.g ConicSpiral
or Torus
. For three dimensional non-orientable surfaces; backface coloring is visible because of the twisting used to generate these surfaces e.g Boy
or Figure8Klein
.
Cite
See: Parametric Equations for Surfaces, for more information. This paper gives a description of the first fifteen surfaces, including their parametric equations and derivatives. Also provided is an example of how to create your own surface, namely the Figure-8 Torus.
Question
If you have a question about this example, please use the VTK Discourse Forum
Code¶
ParametricObjectsDemo.cxx
#include <vtkActor.h>
#include <vtkActor2D.h>
#include <vtkCamera.h>
#include <vtkMath.h>
#include <vtkMinimalStandardRandomSequence.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkParametricFunctionSource.h>
#include <vtkPoints.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkTextMapper.h>
#include <vtkTextProperty.h>
#include <vtkParametricBoy.h>
#include <vtkParametricConicSpiral.h>
#include <vtkParametricCrossCap.h>
#include <vtkParametricDini.h>
#include <vtkParametricEllipsoid.h>
#include <vtkParametricEnneper.h>
#include <vtkParametricFigure8Klein.h>
#include <vtkParametricKlein.h>
#include <vtkParametricMobius.h>
#include <vtkParametricRandomHills.h>
#include <vtkParametricRoman.h>
#include <vtkParametricSpline.h>
#include <vtkParametricSuperEllipsoid.h>
#include <vtkParametricSuperToroid.h>
#include <vtkParametricTorus.h>
// Extra parametric surfaces.
#include <vtkParametricBohemianDome.h>
#include <vtkParametricBour.h>
#include <vtkParametricCatalanMinimal.h>
#include <vtkParametricHenneberg.h>
#include <vtkParametricKuen.h>
#include <vtkParametricPluckerConoid.h>
#include <vtkParametricPseudosphere.h>
// For glyphing
#include <vtkArrowSource.h>
#include <vtkDataSet.h>
#include <vtkGlyph3D.h>
#include <vtkMaskPoints.h>
// For writing out the image.
#include <vtkBMPWriter.h>
#include <vtkImageWriter.h>
#include <vtkJPEGWriter.h>
#include <vtkPNGWriter.h>
#include <vtkPNMWriter.h>
#include <vtkPostScriptWriter.h>
#include <vtkTIFFWriter.h>
#include <vtkWindowToImageFilter.h>
#include <algorithm>
#include <array>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <locale>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>
namespace {
// Holds the arguments from the command line as a map.
// e.g. key = name; value = (true|false,[value_0 ... value_n])
typedef std::map<std::string, std::pair<bool, std::vector<std::string>>>
TCmdArgs;
class CommandLineParser
{
public:
/**
* @param argvArgs: the command line arguments as a vector of strings.
* @param optArgs: the non-positional keys and their value.
* @param optArgsParam: the non-positional keys that have parameters and their
* value.
* @param posNum: the expected number of positional variables.
* @param posNumKName: the name of the positional values.
*/
CommandLineParser(std::vector<std::string>& argvArgs,
std::map<std::string, std::string>& optArgs,
std::map<std::string, std::string>& optArgsParam,
int posNum = 0, std::string const& posName = "_PKN");
~CommandLineParser();
public:
/**
* Parse the parameters from the command line.
*
* @return true if the parse was successful, false if unsuccessful.
*/
bool Parse();
/**
* Get the parsed command line arguments.
*
* @return The parsed arguments.
*/
TCmdArgs GetCommandArguments()
{
return this->cmdArgs;
}
/**
* @return The error message if Parse() failed.
*/
std::string GetParseError()
{
return this->parseError;
}
/**
* @return The key name used to identify positional arguments.
*/
std::string getPositionalKeyName()
{
return this->posName;
}
/**
* @return A string of the parsed command arguments.
*/
std::string DisplayCommandArguments();
private:
/**
* Separate the key from the value, e.g -kv -> k v.
*
* @return The vector with the keys separated from their value(s).
*/
std::vector<std::string> SeparateKV();
/**
* Find any unknown non-positional keys.
*
* @return If unknown keys were found, true is returned.
*/
bool HasUnknownKeys();
/**
* Find any duplicate non-positional keys.
*
* @return If duplicate keys were found, true is returned.
*/
bool HasDuplicateKeys();
/**
* Take a map and get the set of values in the map.
*
* @param m: The map.
* @param s: The set of values from the map.
*/
template <typename K, typename V>
void GetSetOfValues(std::map<K, V> const& m, std::set<V>& s);
/**
* Build a map of aliases for the keys from the key/value pairs.
*
* @param m: A map of key, value pairs.
* @param aliases: A map of aliases keyed by value.
*/
template <typename K, typename V>
void FindAliases(std::map<K, V> m,
std::map<V, std::pair<std::vector<K>, int>>& aliases);
private:
TCmdArgs cmdArgs;
std::map<std::string, std::pair<std::vector<std::string>, int>> aliases;
std::vector<std::string> cmdLineVec;
std::map<std::string, std::string>& optArgs;
std::map<std::string, std::string>& optArgsParam;
int posNum;
std::string posName;
std::string parseError;
std::vector<std::string> cl;
};
/**
* Show the command lime parameters.
*
* @param fn: The program name.
*/
std::string ShowUsage(std::string fn);
/**
* Create a map of the parametric functions and set some parameters.
* The first key groups the parametric functions and the
* second key is the name of the function.
*
* @return The map of functions.
*/
std::map<int, std::map<std::string, vtkSmartPointer<vtkParametricFunction>>>
GetParametricFunctions();
/**
* Write the render window view to an image file.
*
* Image types supported are:
* BMP, JPEG, PNM, PNG, PostScript, TIFF.
* The default parameters are used for all writers, change as needed.
*
* @param fileName The file name, if no extension then PNG is assumed.
* @param renWin The render window.
* @param rgba Used to set the buffer type.
*
*/
void WriteImage(std::string const& fileName, vtkRenderWindow* renWin,
bool rgba = true);
/**
* Get the centre of the object from the bounding box.
*/
std::vector<double> GetCentre(const std::vector<double>& bounds);
/**
* Calculate the maximum length of side of the bounding box.
*/
double GetMaximumLength(const std::vector<double>& bounds);
/**
* Display the dimensions of the bounding box, maximum diagonal length
* and coordinates of the centre.
*
* @param name: The name of the object.
* @param index: The index of the object.
* @param bounds: The bounding box of the object.
*
*/
void DisplayBoundingBoxAndCenter(std::string const& name, int const& index,
std::vector<double> const& bounds);
} // namespace
int main(int argc, char* argv[])
{
// These two maps need to be filled in by the user.
// key: Optional arguments such as -f or --foo with no parameters.
// value: A suitable name. For the keys -f or --foo, the name would be the
// same e.g f.
std::map<std::string, std::string> optArgs;
// key: Optional arguments requiring one or more paramters such as -s fn or
// --some_file fn. value: A suitable name. For the keys -s or --some_file, the
// name would be the same e.g s. To handle non-optional arguments we use a
// special key: _PKN (which can be user defined).
std::map<std::string, std::string> optArgsParam;
// Specify key/value pairs for the arguments we want.
optArgs["-b"] = "b";
optArgs["-n"] = "n";
optArgs["-w"] = "w";
// These are followed by one or more parameters on the command line.
optArgsParam["-s"] = "s";
// The command line arguments
std::vector<std::string> cmdVec;
for (auto i = 1; i < argc; ++i)
{
cmdVec.push_back(argv[i]);
}
CommandLineParser clp(cmdVec, optArgs, optArgsParam);
if (!cmdVec.empty())
{
// Usually -h and --help are reserved for help.
if (std::any_of(cmdVec.begin(), cmdVec.end(), [](const std::string& str) {
return str.size() > 0 && (str == "-h" || str == "--help");
}))
{
std::cout << ShowUsage(argv[0]) << std::endl;
return EXIT_SUCCESS;
}
if (!clp.Parse())
{
std::cerr << clp.GetParseError() << std::endl;
std::cerr << ShowUsage(argv[0]) << std::endl;
return EXIT_FAILURE;
}
}
TCmdArgs cmdArgs = clp.GetCommandArguments();
// std::cout << clp.DisplayCommandArguments() << std::endl;
std::pair<std::string, int> singleSurface;
if (cmdArgs["s"].first)
{
if (cmdArgs["s"].second.size() > 0)
{
singleSurface.first = cmdArgs["s"].second[0];
singleSurface.second = -1;
}
else
{
std::cerr << "Surface name is missing." << std::endl;
return EXIT_FAILURE;
}
}
vtkNew<vtkNamedColors> colors;
auto rendererSize = 200;
auto gridColumnDimensions = 5;
auto gridRowDimensions = 5;
if (cmdArgs["s"].first)
{
rendererSize = 800;
gridColumnDimensions = 1;
gridRowDimensions = 1;
}
// Create one text property for all
vtkNew<vtkTextProperty> textProperty;
textProperty->SetJustificationToCentered();
textProperty->SetFontSize(rendererSize / 12);
textProperty->SetColor(colors->GetColor3d("LavenderBlush").GetData());
// Create a parametric function source, renderer, mapper, and actor
// for each object
std::vector<vtkSmartPointer<vtkParametricFunctionSource>> pfnSrcs;
std::vector<vtkSmartPointer<vtkRenderer>> renderers;
std::vector<vtkSmartPointer<vtkPolyDataMapper>> mappers;
std::vector<vtkSmartPointer<vtkActor>> actors;
std::vector<vtkSmartPointer<vtkTextMapper>> textmappers;
std::vector<vtkSmartPointer<vtkActor2D>> textactors;
// Glyph the normals.
std::vector<vtkSmartPointer<vtkMaskPoints>> maskPts;
std::vector<vtkSmartPointer<vtkArrowSource>> arrow;
std::vector<vtkSmartPointer<vtkGlyph3D>> glyph;
std::vector<vtkSmartPointer<vtkPolyDataMapper>> glyphMapper;
std::vector<vtkSmartPointer<vtkActor>> glyphActor;
auto backProperty = vtkSmartPointer<vtkProperty>::New();
if (cmdArgs["b"].first)
{
backProperty->SetColor(colors->GetColor3d("Peru").GetData());
}
std::vector<std::vector<double>> boundingBox;
// Get the parametric functions and build the pipeline
auto pfn = GetParametricFunctions();
if (cmdArgs["s"].first)
{
// Is the surface name in the map?
std::vector<bool> surfaceExists;
for (auto& t : pfn)
{
if (t.second.find(cmdArgs["s"].second[0]) == t.second.end())
surfaceExists.push_back(false);
else
surfaceExists.push_back(true);
}
if (std::find(std::begin(surfaceExists), std::end(surfaceExists), true) ==
end(surfaceExists))
{
// All entries in surfaceExists are false
std::cout << "Nonexistent surface: " << cmdArgs["s"].second[0]
<< std::endl;
return EXIT_FAILURE;
}
}
// The count of parametric objects
auto objCount = 0;
std::vector<std::string> sortedNames;
for (auto t : pfn)
{
for (auto obj : t.second)
{
sortedNames.push_back(obj.first);
if (cmdArgs["s"].first)
{
if (obj.first == singleSurface.first)
{
singleSurface.second = objCount;
}
}
pfnSrcs.push_back(vtkSmartPointer<vtkParametricFunctionSource>::New());
pfnSrcs[objCount]->SetParametricFunction(pfn[t.first][obj.first]);
pfnSrcs[objCount]->SetUResolution(51);
pfnSrcs[objCount]->SetVResolution(51);
pfnSrcs[objCount]->SetWResolution(51);
pfnSrcs[objCount]->Update();
mappers.push_back(vtkSmartPointer<vtkPolyDataMapper>::New());
mappers[objCount]->SetInputConnection(pfnSrcs[objCount]->GetOutputPort());
actors.push_back(vtkSmartPointer<vtkActor>::New());
actors[objCount]->SetMapper(mappers[objCount]);
actors[objCount]->GetProperty()->SetColor(
colors->GetColor3d("NavajoWhite").GetData());
if (cmdArgs["b"].first)
{
actors[objCount]->SetBackfaceProperty(backProperty);
}
textmappers.push_back(vtkSmartPointer<vtkTextMapper>::New());
textmappers[objCount]->SetInput(obj.first.c_str());
textmappers[objCount]->SetTextProperty(textProperty);
textactors.push_back(vtkSmartPointer<vtkActor2D>::New());
textactors[objCount]->SetMapper(textmappers[objCount]);
textactors[objCount]->SetPosition(rendererSize / 2.0, 8);
renderers.push_back(vtkSmartPointer<vtkRenderer>::New());
double bounds[6];
pfnSrcs[objCount]->GetOutput()->GetBounds(bounds);
std::vector<double> v(std::begin(bounds), std::end(bounds));
boundingBox.push_back(v);
// DisplayBoundingBoxAndCenter(obj.first, objCount, v);
if (cmdArgs["n"].first)
{
// Glyphing
maskPts.push_back(vtkSmartPointer<vtkMaskPoints>::New());
maskPts[objCount]->RandomModeOn();
maskPts[objCount]->SetMaximumNumberOfPoints(150);
maskPts[objCount]->SetInputConnection(
pfnSrcs[objCount]->GetOutputPort());
arrow.push_back(vtkSmartPointer<vtkArrowSource>::New());
arrow[objCount]->SetTipResolution(16);
arrow[objCount]->SetTipLength(0.3);
arrow[objCount]->SetTipRadius(0.1);
double glyphScale = GetMaximumLength(boundingBox[objCount]);
glyph.push_back(vtkSmartPointer<vtkGlyph3D>::New());
glyph[objCount]->SetSourceConnection(arrow[objCount]->GetOutputPort());
glyph[objCount]->SetInputConnection(maskPts[objCount]->GetOutputPort());
glyph[objCount]->SetVectorModeToUseNormal();
glyph[objCount]->SetScaleFactor(glyphScale / 10.0);
glyph[objCount]->OrientOn();
glyph[objCount]->Update();
glyphMapper.push_back(vtkSmartPointer<vtkPolyDataMapper>::New());
glyphMapper[objCount]->SetInputConnection(
glyph[objCount]->GetOutputPort());
glyphActor.push_back(vtkSmartPointer<vtkActor>::New());
glyphActor[objCount]->SetMapper(glyphMapper[objCount]);
glyphActor[objCount]->GetProperty()->SetColor(
colors->GetColor3d("GreenYellow").GetData());
}
objCount++;
}
}
// Need a renderer even if there is no actor
for (auto i = objCount; i < gridColumnDimensions * gridRowDimensions; i++)
{
renderers.push_back(vtkSmartPointer<vtkRenderer>::New());
static_cast<vtkRenderer*>(renderers.back().GetPointer())
->SetBackground(colors->GetColor3d("MidnightBlue").GetData());
sortedNames.push_back("");
}
auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->SetSize(rendererSize * gridColumnDimensions,
rendererSize * gridRowDimensions);
for (auto row = 0; row < gridRowDimensions; row++)
{
for (auto col = 0; col < gridColumnDimensions; col++)
{
// (xmin, ymin, xmax, ymax)
double viewport[4] = {
static_cast<double>(col) * rendererSize /
(gridColumnDimensions * rendererSize),
static_cast<double>(gridRowDimensions - (row + 1)) * rendererSize /
(gridRowDimensions * rendererSize),
static_cast<double>(col + 1) * rendererSize /
(gridColumnDimensions * rendererSize),
static_cast<double>(gridRowDimensions - row) * rendererSize /
(gridRowDimensions * rendererSize)};
if (!cmdArgs["s"].first)
{
auto index = row * gridColumnDimensions + col;
renderWindow->AddRenderer(renderers[index]);
renderers[index]->SetViewport(viewport);
if (index > objCount - 1)
{
continue;
}
renderers[index]->AddActor(actors[index]);
// Normals can only be computed for polygons and triangle strips.
// The Spline is a line.
if (cmdArgs["n"].first && sortedNames[index] != "Spline")
{
renderers[index]->AddActor(glyphActor[index]);
}
renderers[index]->AddActor(textactors[index]);
renderers[index]->SetBackground(
colors->GetColor3d("MidnightBlue").GetData());
renderers[index]->ResetCamera();
renderers[index]->GetActiveCamera()->Azimuth(30);
renderers[index]->GetActiveCamera()->Elevation(-30);
renderers[index]->GetActiveCamera()->Zoom(0.9);
renderers[index]->ResetCameraClippingRange();
}
else
{
auto index = singleSurface.second;
if (index != -1)
{
renderWindow->AddRenderer(renderers[index]);
renderers[index]->SetViewport(viewport);
renderers[index]->AddActor(actors[index]);
// Normals can only be computed for polygons and triangle strips.
// The Spline is a line.
if (cmdArgs["n"].first && singleSurface.first != "Spline")
{
renderers[index]->AddActor(glyphActor[index]);
}
renderers[index]->AddActor(textactors[index]);
renderers[index]->SetBackground(
colors->GetColor3d("MidnightBlue").GetData());
renderers[index]->ResetCamera();
renderers[index]->GetActiveCamera()->Azimuth(30);
renderers[index]->GetActiveCamera()->Elevation(-30);
renderers[index]->GetActiveCamera()->Zoom(0.9);
renderers[index]->ResetCameraClippingRange();
}
}
}
}
vtkNew<vtkRenderWindowInteractor> interactor;
interactor->SetRenderWindow(renderWindow);
renderWindow->Render();
if (cmdArgs["s"].first)
{
renderWindow->SetWindowName(singleSurface.first.c_str());
}
else
{
renderWindow->SetWindowName("ParametricObjectsDemo");
}
renderWindow->Render();
if (cmdArgs["w"].first)
{
// -------------------------------
// Save the image
// -------------------------------
if (cmdArgs["s"].first)
{
WriteImage(singleSurface.first, renderWindow, false);
}
else
{
WriteImage("ParametricObjectsDemo", renderWindow, false);
}
}
interactor->Start();
return EXIT_SUCCESS;
}
namespace {
std::string ShowUsage(std::string fn)
{
// Remove the folder (if present) then remove 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][-s SURFACE_NAME][-w][-b][-n]\n\n"
<< "Display the parametric surfaces.\n\n"
<< "optional arguments:\n"
<< " -h show this help message and exit\n"
<< " -s SURFACE_NAME The name of the surface.\n"
<< " -w Write out the the image.\n"
<< " -b Color the back-face.\n"
<< " -n Display normals.\n"
<< std::endl;
return os.str();
}
std::map<int, std::map<std::string, vtkSmartPointer<vtkParametricFunction>>>
GetParametricFunctions()
{
std::map<int, std::map<std::string, vtkSmartPointer<vtkParametricFunction>>>
pfn;
pfn[0]["Boy"] = vtkSmartPointer<vtkParametricBoy>::New();
pfn[0]["ConicSpiral"] = vtkSmartPointer<vtkParametricConicSpiral>::New();
pfn[0]["CrossCap"] = vtkSmartPointer<vtkParametricCrossCap>::New();
pfn[0]["Dini"] = vtkSmartPointer<vtkParametricDini>::New();
pfn[0]["Ellipsoid"] = vtkSmartPointer<vtkParametricEllipsoid>::New();
pfn[0]["Enneper"] = vtkSmartPointer<vtkParametricEnneper>::New();
pfn[0]["Figure8Klein"] = vtkSmartPointer<vtkParametricFigure8Klein>::New();
pfn[0]["Klein"] = vtkSmartPointer<vtkParametricKlein>::New();
pfn[0]["Mobius"] = vtkSmartPointer<vtkParametricMobius>::New();
pfn[0]["RandomHills"] = vtkSmartPointer<vtkParametricRandomHills>::New();
pfn[0]["Roman"] = vtkSmartPointer<vtkParametricRoman>::New();
pfn[0]["SuperEllipsoid"] =
vtkSmartPointer<vtkParametricSuperEllipsoid>::New();
pfn[0]["SuperToroid"] = vtkSmartPointer<vtkParametricSuperToroid>::New();
pfn[0]["Torus"] = vtkSmartPointer<vtkParametricTorus>::New();
pfn[0]["Spline"] = vtkSmartPointer<vtkParametricSpline>::New();
// Extra parametric surfaces.
pfn[1]["BohemianDome"] = vtkSmartPointer<vtkParametricBohemianDome>::New();
pfn[1]["Bour"] = vtkSmartPointer<vtkParametricBour>::New();
pfn[1]["CatalanMinimal"] =
vtkSmartPointer<vtkParametricCatalanMinimal>::New();
pfn[1]["Henneberg"] = vtkSmartPointer<vtkParametricHenneberg>::New();
pfn[1]["Kuen"] = vtkSmartPointer<vtkParametricKuen>::New();
pfn[1]["PluckerConoid"] = vtkSmartPointer<vtkParametricPluckerConoid>::New();
pfn[1]["Pseudosphere"] = vtkSmartPointer<vtkParametricPseudosphere>::New();
// Now set some parameters.
static_cast<vtkParametricEllipsoid*>(pfn[0]["Ellipsoid"].GetPointer())
->SetXRadius(0.5);
static_cast<vtkParametricEllipsoid*>(pfn[0]["Ellipsoid"].GetPointer())
->SetYRadius(2.0);
static_cast<vtkParametricMobius*>(pfn[0]["Mobius"].GetPointer())
->SetRadius(2.0);
static_cast<vtkParametricMobius*>(pfn[0]["Mobius"].GetPointer())
->SetMinimumV(-0.5);
static_cast<vtkParametricMobius*>(pfn[0]["Mobius"].GetPointer())
->SetMaximumV(0.5);
static_cast<vtkParametricRandomHills*>(pfn[0]["RandomHills"].GetPointer())
->AllowRandomGenerationOn();
static_cast<vtkParametricRandomHills*>(pfn[0]["RandomHills"].GetPointer())
->SetRandomSeed(1);
static_cast<vtkParametricRandomHills*>(pfn[0]["RandomHills"].GetPointer())
->SetNumberOfHills(30);
static_cast<vtkParametricSuperEllipsoid*>(
pfn[0]["SuperEllipsoid"].GetPointer())
->SetN1(0.5);
static_cast<vtkParametricSuperEllipsoid*>(
pfn[0]["SuperEllipsoid"].GetPointer())
->SetN2(0.4);
static_cast<vtkParametricSuperToroid*>(pfn[0]["SuperToroid"].GetPointer())
->SetN1(0.5);
static_cast<vtkParametricSuperToroid*>(pfn[0]["SuperToroid"].GetPointer())
->SetN2(3.0);
// The spline needs points
auto inputPoints = vtkSmartPointer<vtkPoints>::New();
auto rng = vtkSmartPointer<vtkMinimalStandardRandomSequence>::New();
rng->SetSeed(8775070);
for (auto p = 0; p < 10; p++)
{
std::array<double, 3> xyz;
for (auto& idx : xyz)
{
idx = rng->GetRangeValue(-1.0, 1.0);
rng->Next();
}
inputPoints->InsertNextPoint(xyz.data());
}
static_cast<vtkParametricSpline*>(pfn[0]["Spline"].GetPointer())
->SetPoints(inputPoints);
// Extra parametric surfaces.
static_cast<vtkParametricBohemianDome*>(pfn[1]["BohemianDome"].GetPointer())
->SetA(5.0);
static_cast<vtkParametricBohemianDome*>(pfn[1]["BohemianDome"].GetPointer())
->SetB(1.0);
static_cast<vtkParametricBohemianDome*>(pfn[1]["BohemianDome"].GetPointer())
->SetC(2.0);
static_cast<vtkParametricKuen*>(pfn[1]["Kuen"].GetPointer())
->SetDeltaV0(0.001);
return pfn;
}
std::vector<double> GetCentre(const std::vector<double>& bounds)
{
std::vector<double> centre;
if (bounds.size() != 6)
{
return centre;
}
for (unsigned int i = 1; i < bounds.size(); i += 2)
{
centre.push_back(bounds[i] - (bounds[i] - bounds[i - 1]) / 2.0);
}
return centre;
}
double GetMaximumLength(const std::vector<double>& bounds)
{
auto maxLen = -1.0;
if (bounds.size() != 6)
{
return maxLen;
}
for (auto i = 0; i < int(bounds.size()); i += 2)
{
maxLen = std::max(maxLen, std::abs(bounds[i + 1] - bounds[i]));
}
return maxLen;
}
void DisplayBoundingBoxAndCenter(std::string const& name, int const& index,
std::vector<double> const& bounds)
{
if (bounds.size() != 6)
{
return;
}
auto maxLength = GetMaximumLength(bounds);
auto centre = GetCentre(bounds);
std::cout << std::left << std::setw(21) << name << std::right << ": "
<< std::setw(2) << index << "\n"
<< std::left << std::setw(21) << " Bounds (min, max)"
<< ": x:(" << std::right << std::fixed << std::setw(6)
<< std::setprecision(2) << bounds[0] << ", " << std::setw(6)
<< bounds[1] << ") y:(" << std::setw(6) << bounds[2] << ", "
<< std::setw(6) << bounds[3] << ") z:(" << std::setw(6) << bounds[4]
<< ", " << std::setw(6) << bounds[5] << ")\n"
<< std::left << std::setw(21) << " Maximum side length"
<< ": " << std::right << std::setw(6) << maxLength << "\n"
<< std::left << std::setw(21) << " Centre (x, y, z)"
<< ": (" << std::right << std::setw(6) << centre[0] << ", "
<< std::setw(6) << centre[1] << ", " << std::setw(6) << centre[2]
<< ")\n"
<< std::endl;
}
void WriteImage(std::string const& fileName, vtkRenderWindow* renWin, bool rgba)
{
if (!fileName.empty())
{
std::string fn = fileName;
std::string ext;
auto found = fn.find_last_of(".");
if (found == std::string::npos)
{
ext = ".png";
fn += ext;
}
else
{
ext = fileName.substr(found, fileName.size());
}
std::locale loc;
std::transform(ext.begin(), ext.end(), ext.begin(),
[=](char const& c) { return std::tolower(c, loc); });
auto writer = vtkSmartPointer<vtkImageWriter>::New();
if (ext == ".bmp")
{
writer = vtkSmartPointer<vtkBMPWriter>::New();
}
else if (ext == ".jpg")
{
writer = vtkSmartPointer<vtkJPEGWriter>::New();
}
else if (ext == ".pnm")
{
writer = vtkSmartPointer<vtkPNMWriter>::New();
}
else if (ext == ".ps")
{
if (rgba)
{
rgba = false;
}
writer = vtkSmartPointer<vtkPostScriptWriter>::New();
}
else if (ext == ".tiff")
{
writer = vtkSmartPointer<vtkTIFFWriter>::New();
}
else
{
writer = vtkSmartPointer<vtkPNGWriter>::New();
}
auto window_to_image_filter =
vtkSmartPointer<vtkWindowToImageFilter>::New();
window_to_image_filter->SetInput(renWin);
window_to_image_filter->SetScale(1); // image quality
if (rgba)
{
window_to_image_filter->SetInputBufferTypeToRGBA();
}
else
{
window_to_image_filter->SetInputBufferTypeToRGB();
}
// Read from the front buffer.
window_to_image_filter->ReadFrontBufferOff();
window_to_image_filter->Update();
writer->SetFileName(fn.c_str());
writer->SetInputConnection(window_to_image_filter->GetOutputPort());
writer->Write();
}
else
{
std::cerr << "No filename provided." << std::endl;
}
return;
}
CommandLineParser::CommandLineParser(
std::vector<std::string>& cmdLineVec,
std::map<std::string, std::string>& optArgs,
std::map<std::string, std::string>& optArgsParam, int posNum,
std::string const& posName)
: cmdLineVec(cmdLineVec),
optArgs(optArgs),
optArgsParam(optArgsParam),
posNum(posNum),
posName(posName)
{
// Make a set of all the values.
std::set<std::string> pVals;
this->GetSetOfValues(this->optArgs, pVals);
this->GetSetOfValues(this->optArgsParam, pVals);
// Set the default arguments
for (auto p : pVals)
{
this->cmdArgs[p].first = false;
}
// Get the aliases
this->FindAliases(this->optArgsParam, this->aliases);
this->FindAliases(this->optArgs, this->aliases);
cl = this->SeparateKV();
}
CommandLineParser::~CommandLineParser() = default;
std::vector<std::string> CommandLineParser::SeparateKV()
{
// For keys with parameters, short commands like -xy need to be split into
// -x y.
std::vector<std::string> cl;
if (!this->optArgsParam.empty())
{
for (auto v : this->cmdLineVec)
{
auto a = v.substr(0, 2);
if (this->optArgsParam.count(a) > 0 && v.size() > 2)
{
cl.push_back(a);
cl.push_back(v.substr(2));
}
else
{
cl.push_back(v);
}
}
}
return cl;
}
bool CommandLineParser::HasUnknownKeys()
{
auto hasKV = !this->optArgsParam.empty();
auto hasNKV = !this->optArgs.empty();
std::vector<std::string> unknownKeys;
for (auto v : this->cl)
{
if (v[0] == '-' || v.substr(0, 2) == "--")
{
if ((hasKV && this->optArgsParam.count(v) > 0) ||
(hasNKV && this->optArgs.count(v) > 0))
{
continue;
}
else
{
unknownKeys.push_back(v);
}
}
}
if (unknownKeys.size() > 0)
{
std::ostringstream os;
os << "Unknown parameters found: ";
std::copy(std::begin(unknownKeys), std::prev(std::end(unknownKeys)),
std::ostream_iterator<std::string>(os, ", "));
os << unknownKeys.back();
this->parseError = os.str();
return true;
}
return false;
}
bool CommandLineParser::HasDuplicateKeys()
{
// Look for duplicate non-positional parameters.
for (auto k : this->optArgsParam)
{
auto c = std::count(this->cl.begin(), this->cl.end(), k.first);
this->aliases[k.second].second += int(c);
}
for (auto k : this->optArgs)
{
auto c = std::count(this->cl.begin(), this->cl.end(), k.first);
this->aliases[k.second].second += int(c);
}
std::map<std::string, std::vector<std::string>> duplicates;
for (auto v : this->aliases)
{
if (v.second.second > 1)
{
duplicates[v.first] = v.second.first;
}
}
if (duplicates.size() > 0)
{
std::ostringstream os;
os << "Duplicated parameters found: ";
for (auto d : duplicates)
{
os << "(";
std::copy(std::begin(d.second), std::prev(std::end(d.second)),
std::ostream_iterator<std::string>(os, ", "));
os << d.second.back();
os << ") ";
}
// Get rid of trailing spaces
auto last = os.str().find_last_not_of(' ');
this->parseError = os.str().substr(0, last + 1);
return true;
}
return false;
}
bool CommandLineParser::Parse()
{
this->parseError.clear();
if (!this->cl.empty())
{
if (HasUnknownKeys() || HasDuplicateKeys())
{
return false;
}
std::vector<std::string> positionalArguments;
for (auto it = this->cl.begin(); it != this->cl.end(); ++it)
{
if (!this->optArgs.empty())
{
if (this->optArgs.count(*it) > 0)
{
this->cmdArgs[this->optArgs[*it]].first = true;
continue;
}
}
if (!this->optArgsParam.empty())
{
if (this->optArgsParam.count(*it) > 0)
{
if (std::next(it) != this->cl.end())
{
auto key = this->optArgsParam[*it];
while (std::next(it) != this->cl.end())
{
if ((*std::next(it))[0] == '-')
{
break;
}
++it;
std::string fn = *it;
if (fn[0] != '-')
{
if (!this->cmdArgs[key].first)
{
this->cmdArgs[key].first = true;
}
this->cmdArgs[key].second.push_back(fn);
}
else
{
if (this->cmdArgs[key].second.empty())
{
std::ostringstream os;
os << key << " must be followed by a parameter.";
this->parseError = os.str();
return false;
}
else
{
break;
}
}
}
}
else
{
std::ostringstream os;
os << *it
<< " must be followed by a parameter, reached the end of the "
"commands.";
this->parseError = os.str();
return false;
}
continue;
}
}
positionalArguments.push_back(*it);
}
if (this->posNum != int(positionalArguments.size()))
{
std::ostringstream os;
os << "Expected " << this->posNum << " positional arguments, got "
<< positionalArguments.size() << " instead.";
this->parseError = os.str();
return false;
}
if (!positionalArguments.empty())
{
this->cmdArgs[this->posName].first = true;
this->cmdArgs[this->posName].second = positionalArguments;
}
else
{
this->cmdArgs[this->posName].first = false;
}
}
return true;
}
std::string CommandLineParser::DisplayCommandArguments()
{
std::vector<std::string> setParameters;
std::vector<std::string> unSetParameters;
for (auto k : this->cmdArgs)
{
if (this->cmdArgs[k.first].first)
{
setParameters.push_back(k.first);
}
else
{
unSetParameters.push_back(k.first);
}
}
// Sort them
std::sort(setParameters.begin(), setParameters.end());
std::sort(unSetParameters.begin(), unSetParameters.end());
std::ostringstream os;
if (setParameters.size() > 0)
{
os << "Set parameters:" << std::endl;
for (auto k : setParameters)
{
if (this->cmdArgs[k].first)
{
os << " " << k;
if (!this->cmdArgs[k].second.empty())
{
os << ": ";
std::copy(std::begin(cmdArgs[k].second),
std::prev(std::end(cmdArgs[k].second)),
std::ostream_iterator<std::string>(os, ", "));
os << this->cmdArgs[k].second.back();
}
os << std::endl;
}
}
}
if (unSetParameters.size() > 0)
{
os << "Unset parameters:" << std::endl;
for (auto k : unSetParameters)
{
if (!this->cmdArgs[k].first)
{
os << " " << k;
if (!this->cmdArgs[k].second.empty())
{
os << ": ";
std::copy(std::begin(this->cmdArgs[k].second),
std::prev(std::end(this->cmdArgs[k].second)),
std::ostream_iterator<std::string>(os, ", "));
os << this->cmdArgs[k].second.back();
}
os << std::endl;
}
}
}
return os.str();
}
template <typename K, typename V>
void CommandLineParser::GetSetOfValues(std::map<K, V> const& m, std::set<V>& s)
{
std::for_each(m.begin(), m.end(), [&](const std::pair<const K, V>& element) {
s.insert(element.second);
});
}
template <typename K, typename V>
void CommandLineParser::FindAliases(
std::map<K, V> m, std::map<V, std::pair<std::vector<K>, int>>& aliases)
{
std::set<V> values;
this->GetSetOfValues(m, values);
for (auto v : values)
{
std::vector<K> a;
for (auto it : m)
{
if (it.second == v)
{
a.push_back(it.first);
}
}
if (a.size() > 0)
{
aliases[v] = std::pair<std::vector<K>, int>(a, 0);
}
}
}
} // namespace
CMakeLists.txt¶
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(ParametricObjectsDemo)
find_package(VTK COMPONENTS
vtkCommonColor
vtkCommonComputationalGeometry
vtkCommonCore
vtkCommonDataModel
vtkFiltersCore
vtkFiltersSources
vtkIOImage
vtkInteractionStyle
vtkRenderingContextOpenGL2
vtkRenderingCore
vtkRenderingFreeType
vtkRenderingGL2PSOpenGL2
vtkRenderingOpenGL2
QUIET
)
if (NOT VTK_FOUND)
message("Skipping ParametricObjectsDemo: ${VTK_NOT_FOUND_MESSAGE}")
return()
endif()
message (STATUS "VTK_VERSION: ${VTK_VERSION}")
if (VTK_VERSION VERSION_LESS "8.90.0")
# old system
include(${VTK_USE_FILE})
add_executable(ParametricObjectsDemo MACOSX_BUNDLE ParametricObjectsDemo.cxx )
target_link_libraries(ParametricObjectsDemo PRIVATE ${VTK_LIBRARIES})
else()
# 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(ParametricObjectsDemo MACOSX_BUNDLE ParametricObjectsDemo.cxx )
target_link_libraries(ParametricObjectsDemo PRIVATE ${VTK_LIBRARIES})
# vtk_module_autoinit is needed
vtk_module_autoinit(
TARGETS ParametricObjectsDemo
MODULES ${VTK_LIBRARIES}
)
endif()
Download and Build ParametricObjectsDemo¶
Click here to download ParametricObjectsDemo and its CMakeLists.txt file. Once the tarball ParametricObjectsDemo.tar has been downloaded and extracted,
cd ParametricObjectsDemo/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:
./ParametricObjectsDemo
WINDOWS USERS
Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.