I have a vtk file which maps temperature in 3 dimensions. I would like to determine the temperature at a given x, y, z point. I will use the following code in order to load the vtk file (Reading .vtk file):
int main(int argc, char *argv[])
{
// simply set filename here (oh static joy)
std::string inputFilename = "setYourPathToVtkFileHere";
// Get all data from the file
vtkSmartPointer<vtkGenericDataObjectReader> reader =
vtkSmartPointer<vtkGenericDataObjectReader>::New();
reader->SetFileName(inputFilename.c_str());
reader->Update();
// All of the standard data types can be checked and obtained like this:
if (reader->IsFilePolyData())
{
std::cout << "output is a polydata" << std::endl;
vtkPolyData* output = reader->GetPolyDataOutput();
std::cout << "output has " << output->GetNumberOfPoints() << " points." << std::endl;
}
return EXIT_SUCCESS;
}
However, when searching through the extensive list of methods in the vtk library I cant find the appropriate function to extract a value at a specific location. Any suggestions?
The proper way to retrieve a scalar value at a given position depends on two questions:
How is your data laid out and
From which position do you want to retrieve an attribute
Concerning the data layout there are two main layouts:
Structured: The data resides in a uniform grid
Unstructured: The point samples are arbitrary
Concerning the position you can have two situations:
Query at a sample position: You ask for a point that is directly a sample in your dataset
Query at an arbitrary position: You ask for a point that is somewhere in your domain but does not neccessarily coincide with a sample of your data.
Independent of the data layout, to retrieve the data at a sample position (i.e. a sample of your original data set) you can use the vtkPointLocator class. Use the class as follows (untested):
// Build locator object
vtkSmartPointer<vtkPointLocator> locator = vtkPointLocator::New();
locator->SetDataSet(polyData);
locator->BuildLocator();
// Define query position
double pt[3] = {0.1, 0.2, 0.3};
// Get the ID of the point that is closest to the query position
vtkIdType id = locator->FindClosestPoint(pt);
// Retrieve the first attribute value from this point
double value = polyData->GetPointData()->GetScalars()->GetTuple(id, 0);
This will give you the point value of the closest sample of your data.
Note that this will not give you the explicit position of the point in the data set as this is implicitly encoded in the variable id. To retrieve the actual position of the closest point, you can write:
double *dataPt = polyData->GetPoint(id);
If you want to retrieve data at an arbitrary position of your domain you will need some way of interpolation.
Here, the data layout is important.
For structured data you can first convert your data to a vtkImage and then perform queries on it. If you want to retrieve an interpolated attribute using linear or cubic schemes you can add a vtkImageInterpolator in your filter chain and then retrieve a point using the GetScalarComponentAsDouble method.
For unstructured data you should first decide on an interpolation scheme. vtk has various filters to reconstruct continuous data from data samples. Options include Delaunay triangulation/tetrahedrization (vtkDelaunay2D, vtkDelaunay3D) , as well as the Shepard method (vtkShepardMethod). Either method will give you a new data set with possibilities to query arbitrary points. If you want to retrieve the scalar attributes for a (batch of) points without actually reconstructing a complete data set you can also look at the vtkProbeFilter.
You need to first extract the polyData from the reader. Then, store the points via vtkPolyData::getPoints into a vtksmartPointer<vtkPoints>.
To finish, create a std::vector of a custom struct and store them while iterating over your vtkPoints.
Here's some code to illustrate :
#include <vtkDataArray.h>
#include <vtkDataSet.h>
#include <vtkGenericDataObjectReader.h>
#include <vtkPointLocator.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkSmartPointer.h>
#include <vtkStructuredGrid.h>
#include <string>
struct Pnt {
double x_, y_, z_;
Pnt(double x, double y, double z) : x_(x), y_(y), z_(z) {}
};
int main ( int argc, char *argv[] )
{
// Ensure a filename was specified
if(argc != 2)
{
std::cerr << "Usage: " << argv[0] << " InputFilename" << endl;
return EXIT_FAILURE;
}
// simply set filename here (oh static joy)
std::string inputFilename = "setYourPathToVtkFileHere";
// Get all data from the file
vtkSmartPointer<vtkGenericDataObjectReader> reader = vtkSmartPointer<vtkGenericDataObjectReader>::New();
reader->SetFileName(inputFilename.c_str());
reader->Update();
vtkSmartPointer<vtkPolyData> polydata = reader->GetPolyDataOutput();
vtkSmartPointer<vtkPoints> vtk_points = polydata->GetPoints();
std::vector<Pnt> my_points;
for (int i = 0; i < vtk_points->GetNumberOfPoints(); i++){
const auto pnt = vtk_points->GetPoint(i);
my_points.emplace_back(pnt[0], pnt[1], pnt[2]);
}
return EXIT_SUCCESS;
}
And here is the version with the vtkPointLocator mentionned in QnD's answer :
int main(int argc, char *argv[])
{
// Ensure a filename was specified
if (argc != 2)
{
std::cerr << "Usage: " << argv[0] << " InputFilename" << endl;
return EXIT_FAILURE;
}
// simply set filename here (oh static joy)
std::string inputFilename = "setYourPathToVtkFileHere";
// Get all data from the file
vtkSmartPointer<vtkGenericDataObjectReader> reader = vtkSmartPointer<vtkGenericDataObjectReader>::New();
reader->SetFileName(inputFilename.c_str());
reader->Update();
vtkSmartPointer<vtkPolyData> polydata = reader->GetPolyDataOutput();
//Building locator
vtkSmartPointer<vtkPointLocator> locator = vtkPointLocator::New();
locator->SetDataSet(polydata);
locator->BuildLocator();
//Finding point
const double pt[3] = { 0.1, 0.2, 0.3 };
vtkIdType id = locator->FindClosestPoint(pt);
double pnt_found[3];
polydata->GetPointData()->GetScalars()->GetTuple(id, pnt_found);
return EXIT_SUCCESS;
}
And the CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
PROJECT(GenericDataObjectReader)
find_package(VTK REQUIRED)
include(${VTK_USE_FILE})
add_executable(GenericDataObjectReader MACOSX_BUNDLE GenericDataObjectReader)
if(VTK_LIBRARIES)
target_link_libraries(GenericDataObjectReader ${VTK_LIBRARIES})
else()
target_link_libraries(GenericDataObjectReader vtkHybrid vtkWidgets)
endif()
Please let me know if I need to do any specific action to correctly credit the other answer. I'm new and don't know all the specifics yet.
Related
I want to load point cloud data using PCL. I could load sample data on tutorial correctly, but when I try with my data, the value of pcd file was changed to very small value.
Terminal output image
The actual value is like 3603538.71629 but when PCL reads this file it becomes very small values.
This is my pcd file.
# .PCD v.7 - Point Cloud Data file format
VERSION .7
FIELDS x y z cluster
SIZE 4 4 4 4
TYPE F F F F
COUNT 1 1 1 1
WIDTH 14937
HEIGHT 1
VIEWPOINT 0 0 0 1 0 0 0
POINTS 14937
DATA binary
# POINT_X;POINT_Y;Count;Cluster
3603538.71629;5794698.05946;1;4
3605159.73611;5792213.47052;1;20
3605158.44424;5792230.86339;1;20
3605158.97718;5792221.85844;1;20
3605152.30217;5792232.17992;1;20
3604558.82308;5793345.02318;1;55
3604944.90684;5794341.30959;1;56
This is my pcd_read.cpp
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
int
main (int argc, char** argv)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
if (pcl::io::loadPCDFile<pcl::PointXYZ> ("xyzc-Cloud.pcd", *cloud) == -1) //* load the file
{
PCL_ERROR ("Couldn't read file xyzc-Cloud.pcd \n");
return (-1);
}
std::cout << "Loaded "
<< cloud->width * cloud->height
<< " data points from xyzc-Cloud.pcd with the following fields: "
<< std::endl;
for (size_t i = 0; i < 10; ++i)
std::cout << " " << cloud->points[i].x
<< " " << cloud->points[i].y
<< " " << cloud->points[i].z << std::endl;
return (0);
}
'''
Environment:
macOS ver 10.14.16
I think you are seeing corruption because of your coordinate data being improperly formatted. The fields are supposed to be space or tab delimited, whereas you have a character ;.
Quote from the docs:
Storing point cloud data in both a simple ascii form with each point on a line, space or tab separated, without any other characters on it, as well as in a binary dump format, allows us to have the best of both worlds: simplicity and speed, depending on the underlying application.
This is confirmed by looking at the reader source where the lines are split using the following. Your semi-colon will lead to invalid splitting and subsequent float extraction. The function does not return an error for improper formatting, which is why your code still works, albeit incorrectly.
boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on);
Therefore, answer is to format your .pcd body data with space delimiters.
Also as #UlrichtEckhardt comments you're mixing and matching intended output formats:
Replace
DATA binary
with
DATA ascii
I am using HDFql to create an HDF5 file. I am creating a group and putting some data into it and that works fine. I then add an attribute to the file (which also seems to work, since it shows up when I inspect the HDF5 file with an HDF editor), but I can't figure out how to read the value of the attribute. Here is a minimal example:
#include <iostream.h>
#include <HDFql.hpp>
int main (int argc, const char * argv[]) {
char script[1024];
//Create the HDF5 file and the group "test"
HDFql::execute("CREATE TRUNCATE FILE /tmp/test.h5");
HDFql::execute("USE FILE /tmp/test.h5");
HDFql::execute("CREATE GROUP test");
//Generate some arbitrary data and place it in test/data
int data_length = 1000;
int data[data_length];
for(int i=0; i<data_length; i++) {data[i] = i;}
sprintf(script, "CREATE DATASET test/data AS INT(%d) VALUES FROM MEMORY %d",
data_length, HDFql::variableTransientRegister(data));
HDFql::execute(script);
//Create an attribute called "channels" and give it an arbitrary value of 11
HDFql::execute("CREATE ATTRIBUTE test/data/channels AS INT VALUES(11)");
//Show the attribute
HDFql::execute("SHOW ATTRIBUTE test/data/channels");
//Try to move the cursor to the attribute
HDFql::cursorLast();
//If that worked, print the attribute contents
if(HDFql::cursorGetInt())
{
std::cout << "channels = " << *HDFql::cursorGetInt() << std::endl;
} else
{
std::cout << "Couldn't find attribute" << std::endl;
}
HDFql::execute("CLOSE FILE");
}
I would expect the output to the console to be channels = 11, instead I am getting channels = 1953719668. Curiously, if I call cursorGetChar instead, the return value is "t", and if I say
std::cout << "channels = " << HDFql::cursorGetChar() << std::endl;
the output becomes channels = test/data/channels.
So I think I have misunderstood how exactly HDFql cursors work. So my question is: what is wrong with my code? And why is my code wrong?
Many thanks!
When you do SHOW ATTRIBUTE test/data/channels you are basically testing for the existence of an attribute named channels stored in test/data. Since this attribute exists, function HDFql::execute returns HDFql::Success and the cursor is populated with the string test/data/channels. On the other hand, if the attribute didn't exist, function HDFql::execute would have returned HDFql::ErrorNotFound and the cursor would be empty.
To read the value stored in attribute test/data/channels do the following instead:
HDFql::execute("SELECT FROM test/data/channels");
HDFql::cursorFirst();
std::cout << "channels = " << *HDFql::cursorGetInt() << std::endl;
I need to read simple step files (turning parts) with C++. Therefore I try to use Open Cascade. I already can open my step file and read the number of shapes in my file. Now I need to get the geometrical data (length, width, diameters..), but I dont know how it works, although I read all of the documentations.
Is there anyone who already worked with Open Cascade and can help me with my Problem? I would be very happy, thank alot !!
That's my Code since then
#include <iostream>
#include <STEPControl_Reader.hxx>
#include <string>
using namespace std;
int main() {
STEPControl_Reader reader;
IFSelect_ReturnStatus stat = reader.ReadFile("C:\\Users\\Kelevradesktop.Kelevra-desktop\\Desktop\\Studienarbeit\\steptest.step");
IFSelect_PrintCount mode = IFSelect_ListByItem;
reader.PrintCheckLoad(false, mode);
Standard_Integer NbRoots = reader.NbRootsForTransfer(); //Transfer whole file
Standard_Integer num = reader.TransferRoots();
Standard_Integer NbTrans = reader.TransferRoots();
TopoDS_Shape result = reader.OneShape();
TopoDS_Shape shape = reader.Shape();
cout << NbRoots << endl;
cout << NbTrans << endl;
cout << num << endl;
system("pause");
return 0;
}
Check the FreeCad source code. They use OpenCascade and can import step and iges. It should get you started. The function ImportStepParts(...) in https://github.com/FreeCAD/FreeCAD/blob/master/src/Mod/Part/App/ImportStep.cpp is what you search for.
Use TopExpExplorer class to iterate through the objects (vertexes, edges, faces..) of a shape. An iterating example you can find in this tutorial.
Use GProp_GProps class to get properties of a shape. Example:
GProp_GProps propertiesSystemFace;
BRepGProp::VolumeProperties(shape, propertiesSystemFace);
double shapeVolume = propertiesSystemFace.Mass();
gp_Pnt centerOfMass = propertiesSystemFace.CentreOfMass();
Also you can convert TopoDS_Edge to curve object to get some other parameters according to the edge type:
BRepAdaptor_Curve adaptCrv = BRepAdaptor_Curve(edge);
what I'm trying to do is performing CGAL boolean operator (union operator) on 2 models that has RGB color for each vertex. but the result doesn't retain the color info.
Maybe you have an idea how to solve this issue.
Here are the models for doing boolean operation (coff format):
model1
model2
image of input models
Here is the code I'm using:
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/Polygon_mesh_processing/corefinement.h>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Polyhedron_3<K, CGAL::Polyhedron_items_with_id_3> Mesh;
namespace PMP = CGAL::Polygon_mesh_processing;
int main(int argc, char* argv[])
{
const char* filename1 = (argc > 1) ? argv[1] : "data/b1.off";
const char* filename2 = (argc > 2) ? argv[2] : "data/b2.off";
std::ifstream input(filename1);
Mesh mesh1, mesh2;
if (!input || !(input >> mesh1))
{
std::cerr << "First mesh is not a valid off file." << std::endl;
return 1;
}
input.close();
input.open(filename2);
if (!input || !(input >> mesh2))
{
std::cerr << "Second mesh is not a valid off file." << std::endl;
return 1;
}
Mesh out;
bool valid_union = PMP::corefine_and_compute_union(mesh1, mesh2, out);
if (valid_union)
{
std::cout << "Union was successfully computed\n";
std::ofstream output("union.off");
output << out;
return 0;
}
std::cout << "Union could not be computed\n";
return 1;
}
What I get at the end has correct mesh but doesn't retain the color info.
Is there any chance to fix the color info?
union model as result
It is not yet possible. There is one hidden parameter in corefine to preserve those attributes but the output builder for the Boolean operation is missing a visitor (that I hope to have time to add in CGAL 4.13).
There a few workaround but none of them will handle all the possible cases.
EDIT:
In CGAL 4.13 this will be possible by passing in the named parameter a visitor. This PR is the one adding the support and is already merged into the master branch. The following example shows how to do it.
I found that there is a command in cgal library to load "coff" format
which contains color RGB. But I don't know how to use it. My first
attempt is how to define data types.
As far as I know, only the Surface_mesh data structure is able to work with a COFF file. You can look at the code of the Surface_mesh_io_plugin in Polyhedron/demo/Polyhedron.
The COFF format is a standard that is quite well documented.
I'm trying to do something like this,
enum Data_Property
{
NAME,
DATETIME,
TYPE,
};
struct Item_properties
{
char* Name;
char* DateTime;
int Type;
};
int main() {
std::string data = "/Name Item_Name /DATETIME [TimeStamp] /TYPE [Integer value]";
std::list <std::string> known_properties;
known_properties.push_back("/Name");
known_properties.push_back("/DATETIME");
known_properties.push_back("/TYPE");
Item_properties item_p = extract_properties(data); //I use a function to get properties
//in a structure.
//here I want to use a switch-case statement to get the property by enum value
}
I need to know is there any way i can make it any simple? or how would i combine property keys like (/NAME /DATETIME /TYPE) with an enum and avoid using a std::list i.e known_properties?
As far as I know, there is nothing in C++ enums that is like C# enum attributes. I could be wrong though.
Given that, I'd suggest using a map to map the data property to item property. You should only have to build this map once. You can also associate the string representations to the item and data properties. This should make it easier to get the item properties objects built. I don't think a list would be an ideal data structure for this task.
First, let me say that there is always another alternative, one of my philosophies.
Let's look at the Big Picture. You are trying read an Item from string. The Item is a container of datums. In Object Oriented terms, the Item would like each datum to load its own data from a string. The Item doesn't need to know how to load a datum, as it can request this from each datum.
Your datums are actually more complex and intelligent than the C++ simple POD types. (Simple POD types don't have field names.) So, you need to create classes to represent these (or to encapsulate the extra complexity). Trust me, this will be better in the long run.
Here is a simple example of Refactoring the Name member:
struct Name_Field
{
std::string value; // Change your habits to prefer std:string to char *
void load_from(const std::string& text,
const std::string::size_type& starting_position = 0)
{
value.clear();
std::string::size_type position = 0;
position = text.find("/Name", starting_position);
if (position != std::string::npos) // the field name was found.
{
// Skip the next space
++position;
// Check for end of text
if (position < text.length())
{
std::string::size_t end_position;
end_position = text.find_first_not_of(" ", position);
if (end_position != std::string::npos)
{
value = text.substr(position, end_position - position);
}
}
}
};
You can now add a load_from to the Item class:
struct Item
{
Name_Field Name;
char * DateTime;
char * Type;
void load_from(const std::string& text)
{
std::string::size_t position = 0;
// Notice, the Name member is responsible for load its data,
// relieving Item class from knowing how to do it.
Name.load_from(text, position);
}
};
To load the item:
Item new_item;
new_item.load_from(data);
As you refactor, be aware of common methods. For example, you may want to put common methods between Name_Field and DateTime_Field into a base (parent) class, Field_Base. This will simplify your code and design as well as support re-usability.
Have you looked at Boost::program_options? Here's some example code:
#include "boost\program_options\parsers.hpp"
#include "boost\program_options\variables_map.hpp"
#include <iostream>
using std::cout;
using std::endl;
namespace po = boost::program_options;
int _tmain(int argc, _TCHAR* argv[])
{
po::options_description desc("Allowed options");
string taskName;
desc.add_options()
("help", "produce help message")
("TaskName", po::value<string>(&taskName), "The project's prefix")
;
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help")) {
cout << desc << endl;
return 1;
}
if (!vm.count("TaskName"))
{
cout << "Use \"DOTaskTests.exe --help\" for command line options" << endl;
return 1;
}
}