Files
create/src/Mod/Fem/App/PropertyPostDataObject.cpp
2023-08-15 01:09:53 +02:00

429 lines
15 KiB
C++

/***************************************************************************
* Copyright (c) 2015 Stefan Tröger <stefantroeger@gmx.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <Python.h>
# include <vtkCompositeDataSet.h>
# include <vtkMultiBlockDataSet.h>
# include <vtkMultiPieceDataSet.h>
# include <vtkPolyData.h>
# include <vtkRectilinearGrid.h>
# include <vtkStructuredGrid.h>
# include <vtkUnstructuredGrid.h>
# include <vtkUniformGrid.h>
# include <vtkXMLDataSetWriter.h>
# include <vtkXMLImageDataReader.h>
# include <vtkXMLPolyDataReader.h>
# include <vtkXMLRectilinearGridReader.h>
# include <vtkXMLStructuredGridReader.h>
# include <vtkXMLUnstructuredGridReader.h>
#endif
#include <App/Application.h>
#include <App/DocumentObject.h>
#include <Base/Console.h>
#include <Base/FileInfo.h>
#include <Base/Reader.h>
#include <Base/Stream.h>
#include <Base/Writer.h>
#include <CXX/Objects.hxx>
#include "PropertyPostDataObject.h"
using namespace Fem;
TYPESYSTEM_SOURCE(Fem::PropertyPostDataObject , App::Property)
PropertyPostDataObject::PropertyPostDataObject()
{
}
PropertyPostDataObject::~PropertyPostDataObject()
{
}
void PropertyPostDataObject::scaleDataObject(vtkDataObject *dataObject, double s)
{
auto scalePoints = [](vtkPoints *points, double s) {
for (vtkIdType i = 0; i < points->GetNumberOfPoints(); i++) {
double xyz[3];
points->GetPoint(i, xyz);
for (double & j : xyz)
j *= s;
points->SetPoint(i, xyz);
}
};
if (dataObject->GetDataObjectType() == VTK_POLY_DATA) {
vtkPolyData *dataSet = vtkPolyData::SafeDownCast(dataObject);
scalePoints(dataSet->GetPoints(), s);
}
else if (dataObject->GetDataObjectType() == VTK_STRUCTURED_GRID) {
vtkStructuredGrid *dataSet = vtkStructuredGrid::SafeDownCast(dataObject);
scalePoints(dataSet->GetPoints(), s);
}
else if (dataObject->GetDataObjectType() == VTK_UNSTRUCTURED_GRID) {
vtkUnstructuredGrid *dataSet = vtkUnstructuredGrid::SafeDownCast(dataObject);
scalePoints(dataSet->GetPoints(), s);
}
else if (dataObject->GetDataObjectType() == VTK_MULTIBLOCK_DATA_SET) {
vtkMultiBlockDataSet *dataSet = vtkMultiBlockDataSet::SafeDownCast(dataObject);
for (unsigned int i = 0; i < dataSet->GetNumberOfBlocks(); i++)
scaleDataObject(dataSet->GetBlock(i), s);
}
else if (dataObject->GetDataObjectType() == VTK_MULTIPIECE_DATA_SET) {
vtkMultiPieceDataSet *dataSet = vtkMultiPieceDataSet::SafeDownCast(dataObject);
for (unsigned int i = 0; i < dataSet->GetNumberOfPieces(); i++)
scaleDataObject(dataSet->GetPiece(i), s);
}
}
void PropertyPostDataObject::scale(double s)
{
if (m_dataObject) {
aboutToSetValue();
scaleDataObject(m_dataObject, s);
hasSetValue();
}
}
void PropertyPostDataObject::setValue(const vtkSmartPointer<vtkDataObject> &ds)
{
aboutToSetValue();
if (ds) {
createDataObjectByExternalType(ds);
m_dataObject->DeepCopy(ds);
}
else {
m_dataObject = nullptr;
}
hasSetValue();
}
const vtkSmartPointer<vtkDataObject> &PropertyPostDataObject::getValue() const
{
return m_dataObject;
}
bool PropertyPostDataObject::isComposite()
{
return m_dataObject && !m_dataObject->IsA("vtkDataSet");
}
bool PropertyPostDataObject::isDataSet()
{
return m_dataObject && m_dataObject->IsA("vtkDataSet");
}
int PropertyPostDataObject::getDataType()
{
if (!m_dataObject)
return -1;
return m_dataObject->GetDataObjectType();
}
PyObject *PropertyPostDataObject::getPyObject()
{
//TODO: fetch the vtk python object from the data set and return it
return Py::new_reference_to(Py::None());
}
void PropertyPostDataObject::setPyObject(PyObject * /*value*/)
{
}
App::Property *PropertyPostDataObject::Copy() const
{
PropertyPostDataObject *prop = new PropertyPostDataObject();
if (m_dataObject) {
prop->createDataObjectByExternalType(m_dataObject);
prop->m_dataObject->DeepCopy(m_dataObject);
}
return prop;
}
void PropertyPostDataObject::createDataObjectByExternalType(vtkSmartPointer<vtkDataObject> ex)
{
switch (ex->GetDataObjectType()) {
case VTK_POLY_DATA:
m_dataObject = vtkSmartPointer<vtkPolyData>::New();
break;
case VTK_STRUCTURED_GRID:
m_dataObject = vtkSmartPointer<vtkStructuredGrid>::New();
break;
case VTK_RECTILINEAR_GRID:
m_dataObject = vtkSmartPointer<vtkRectilinearGrid>::New();
break;
case VTK_UNSTRUCTURED_GRID:
m_dataObject = vtkSmartPointer<vtkUnstructuredGrid>::New();
break;
case VTK_UNIFORM_GRID:
m_dataObject = vtkSmartPointer<vtkUniformGrid>::New();
break;
case VTK_COMPOSITE_DATA_SET:
m_dataObject = vtkCompositeDataSet::New();
break;
case VTK_MULTIBLOCK_DATA_SET:
m_dataObject = vtkSmartPointer<vtkMultiBlockDataSet>::New();
break;
case VTK_MULTIPIECE_DATA_SET:
m_dataObject = vtkSmartPointer<vtkMultiPieceDataSet>::New();
break;
default:
break;
};
}
void PropertyPostDataObject::Paste(const App::Property &from)
{
aboutToSetValue();
m_dataObject = dynamic_cast<const PropertyPostDataObject &>(from).m_dataObject;
hasSetValue();
}
unsigned int PropertyPostDataObject::getMemSize() const
{
return m_dataObject ? m_dataObject->GetActualMemorySize() : 0;
}
void PropertyPostDataObject::getPaths(std::vector<App::ObjectIdentifier>& /*paths*/) const
{
/* paths.push_back(App::ObjectIdentifier(getContainer())
<< App::ObjectIdentifier::Component::SimpleComponent(getName())
<< App::ObjectIdentifier::Component::SimpleComponent(
App::ObjectIdentifier::String("ShapeType")));
paths.push_back(App::ObjectIdentifier(getContainer())
<< App::ObjectIdentifier::Component::SimpleComponent(getName())
<< App::ObjectIdentifier::Component::SimpleComponent(
App::ObjectIdentifier::String("Orientation")));
paths.push_back(App::ObjectIdentifier(getContainer())
<< App::ObjectIdentifier::Component::SimpleComponent(getName())
<< App::ObjectIdentifier::Component::SimpleComponent(
App::ObjectIdentifier::String("Length")));
paths.push_back(App::ObjectIdentifier(getContainer())
<< App::ObjectIdentifier::Component::SimpleComponent(getName())
<< App::ObjectIdentifier::Component::SimpleComponent(
App::ObjectIdentifier::String("Area")));
paths.push_back(App::ObjectIdentifier(getContainer())
<< App::ObjectIdentifier::Component::SimpleComponent(getName())
<< App::ObjectIdentifier::Component::SimpleComponent(
App::ObjectIdentifier::String("Volume")));
*/
}
void PropertyPostDataObject::Save(Base::Writer &writer) const
{
std::string extension;
if (!m_dataObject)
return;
switch (m_dataObject->GetDataObjectType()) {
case VTK_POLY_DATA:
extension = "vtp";
break;
case VTK_STRUCTURED_GRID:
extension = "vts";
break;
case VTK_RECTILINEAR_GRID:
extension = "vtr";
break;
case VTK_UNSTRUCTURED_GRID:
extension = "vtu";
break;
case VTK_UNIFORM_GRID:
extension = "vti";//image data
break;
//TODO:multi-datasets use multiple files, this needs to be implemented specially
// case VTK_COMPOSITE_DATA_SET:
// prop->m_dataObject = vtkCompositeDataSet::New();
// break;
// case VTK_MULTIBLOCK_DATA_SET:
// prop->m_dataObject = vtkMultiBlockDataSet::New();
// break;
// case VTK_MULTIPIECE_DATA_SET:
// prop->m_dataObject = vtkMultiPieceDataSet::New();
// break;
default:
break;
};
if (!writer.isForceXML()) {
std::string file = "Data." + extension;
writer.Stream() << writer.ind() << "<Data file=\""
<< writer.addFile(file.c_str(), this)
<< "\"/>" << std::endl;
}
}
void PropertyPostDataObject::Restore(Base::XMLReader &reader)
{
reader.readElement("Data");
if (!reader.hasAttribute("file"))
return;
std::string file(reader.getAttribute("file"));
if (!file.empty()) {
// initiate a file read
reader.addFile(file.c_str(), this);
}
}
void PropertyPostDataObject::SaveDocFile(Base::Writer &writer) const
{
// If the shape is empty we simply store nothing. The file size will be 0 which
// can be checked when reading in the data.
if (!m_dataObject)
return;
// create a temporary file and copy the content to the zip stream
// once the tmp. filename is known use always the same because otherwise
// we may run into some problems on the Linux platform
static Base::FileInfo fi(App::Application::getTempFileName());
vtkSmartPointer<vtkXMLDataSetWriter> xmlWriter = vtkSmartPointer<vtkXMLDataSetWriter>::New();
xmlWriter->SetInputDataObject(m_dataObject);
xmlWriter->SetFileName(fi.filePath().c_str());
xmlWriter->SetDataModeToBinary();
#ifdef VTK_CELL_ARRAY_V2
// Looks like an invalid data object that causes a crash with vtk9
vtkUnstructuredGrid *dataGrid = vtkUnstructuredGrid::SafeDownCast(m_dataObject);
if (dataGrid && (dataGrid->GetPiece() < 0 || dataGrid->GetNumberOfPoints() <= 0)) {
std::cerr << "PropertyPostDataObject::SaveDocFile: ignore empty vtkUnstructuredGrid\n";
return;
}
#endif
if (xmlWriter->Write() != 1) {
// Note: Do NOT throw an exception here because if the tmp. file could
// not be created we should not abort.
// We only print an error message but continue writing the next files to the
// stream...
App::PropertyContainer *father = this->getContainer();
if (father && father->isDerivedFrom(App::DocumentObject::getClassTypeId())) {
App::DocumentObject *obj = static_cast<App::DocumentObject *>(father);
Base::Console().Error("Dataset of '%s' cannot be written to vtk file '%s'\n",
obj->Label.getValue(), fi.filePath().c_str());
}
else {
Base::Console().Error("Cannot save vtk file '%s'\n", fi.filePath().c_str());
}
std::stringstream ss;
ss << "Cannot save vtk file '" << fi.filePath() << "'";
writer.addError(ss.str());
}
Base::ifstream file(fi, std::ios::in | std::ios::binary);
if (file) {
std::streambuf *buf = file.rdbuf();
writer.Stream() << buf;
}
file.close();
// remove temp file
fi.deleteFile();
}
void PropertyPostDataObject::RestoreDocFile(Base::Reader &reader)
{
Base::FileInfo xml(reader.getFileName());
// create a temporary file and copy the content from the zip stream
Base::FileInfo fi(App::Application::getTempFileName());
// read in the ASCII file and write back to the file stream
Base::ofstream file(fi, std::ios::out | std::ios::binary);
unsigned long ulSize = 0;
if (reader) {
std::streambuf *buf = file.rdbuf();
reader >> buf;
file.flush();
ulSize = buf->pubseekoff(0, std::ios::cur, std::ios::in);
}
file.close();
// Read the data from the temp file
if (ulSize > 0) {
std::string extension = xml.extension();
// TODO: read in of composite data structures need to be coded,
// including replace of "GetOutputAsDataSet()"
vtkSmartPointer<vtkXMLReader> xmlReader;
if (extension == "vtp")
xmlReader = vtkSmartPointer<vtkXMLPolyDataReader>::New();
else if (extension == "vts")
xmlReader = vtkSmartPointer<vtkXMLStructuredGridReader>::New();
else if (extension == "vtr")
xmlReader = vtkSmartPointer<vtkXMLRectilinearGridReader>::New();
else if (extension == "vtu")
xmlReader = vtkSmartPointer<vtkXMLUnstructuredGridReader>::New();
else if (extension == "vti")
xmlReader = vtkSmartPointer<vtkXMLImageDataReader>::New();
xmlReader->SetFileName(fi.filePath().c_str());
xmlReader->Update();
if (!xmlReader->GetOutputAsDataSet()) {
// Note: Do NOT throw an exception here because if the tmp. created file could
// not be read it's NOT an indication for an invalid input stream 'reader'.
// We only print an error message but continue reading the next files from the
// stream...
App::PropertyContainer *father = this->getContainer();
if (father && father->isDerivedFrom(App::DocumentObject::getClassTypeId())) {
App::DocumentObject *obj = static_cast<App::DocumentObject *>(father);
Base::Console().Error("Dataset file '%s' with data of '%s' seems to be empty\n",
fi.filePath().c_str(), obj->Label.getValue());
}
else {
Base::Console().Warning("Loaded Dataset file '%s' seems to be empty\n",
fi.filePath().c_str());
}
}
else {
aboutToSetValue();
createDataObjectByExternalType(xmlReader->GetOutputAsDataSet());
m_dataObject->DeepCopy(xmlReader->GetOutputAsDataSet());
hasSetValue();
}
}
// delete the temp file
fi.deleteFile();
}