Files
create/src/App/PropertyGeo.cpp
Markus Reitböck 73c97bc90f App: use CMake to generate precompiled headers on all platforms
"Professional CMake" book suggest the following:

"Targets should build successfully with or without compiler support for precompiled headers. It
should be considered an optimization, not a requirement. In particular, do not explicitly include a
precompile header (e.g. stdafx.h) in the source code, let CMake force-include an automatically
generated precompile header on the compiler command line instead. This is more portable across
the major compilers and is likely to be easier to maintain. It will also avoid warnings being
generated from certain code checking tools like iwyu (include what you use)."

Therefore, removed the "#include <PreCompiled.h>" from sources, also
there is no need for the "#ifdef _PreComp_" anymore
2025-09-14 09:47:02 +02:00

1348 lines
43 KiB
C++

/***************************************************************************
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
* *
* 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 <Base/MatrixPy.h>
#include <Base/PlacementPy.h>
#include <Base/Reader.h>
#include <Base/Quantity.h>
#include <Base/QuantityPy.h>
#include <Base/Rotation.h>
#include <Base/RotationPy.h>
#include <Base/Stream.h>
#include <Base/Tools.h>
#include <Base/VectorPy.h>
#include <Base/Writer.h>
#include "ComplexGeoData.h"
#include "Document.h"
#include "PropertyGeo.h"
#include "Placement.h"
#include "ObjectIdentifier.h"
using namespace App;
using namespace Base;
using namespace std;
//**************************************************************************
//**************************************************************************
// PropertyVector
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TYPESYSTEM_SOURCE(App::PropertyVector, App::Property)
//**************************************************************************
// Construction/Destruction
PropertyVector::PropertyVector() = default;
PropertyVector::~PropertyVector() = default;
//**************************************************************************
// Base class implementer
void PropertyVector::setValue(const Base::Vector3d& vec)
{
aboutToSetValue();
_cVec = vec;
hasSetValue();
}
void PropertyVector::setValue(double x, double y, double z)
{
aboutToSetValue();
_cVec.Set(x, y, z);
hasSetValue();
}
const Base::Vector3d& PropertyVector::getValue() const
{
return _cVec;
}
PyObject* PropertyVector::getPyObject()
{
return new Base::VectorPy(_cVec);
}
void PropertyVector::setPyObject(PyObject* value)
{
if (PyObject_TypeCheck(value, &(Base::VectorPy::Type))) {
Base::VectorPy* pcObject = static_cast<Base::VectorPy*>(value);
Base::Vector3d* val = pcObject->getVectorPtr();
setValue(*val);
}
else if (PyTuple_Check(value) && PyTuple_Size(value) == 3) {
PyObject* item {};
Base::Vector3d cVec;
// x
item = PyTuple_GetItem(value, 0);
if (PyFloat_Check(item)) {
cVec.x = PyFloat_AsDouble(item);
}
else if (PyLong_Check(item)) {
cVec.x = (double)PyLong_AsLong(item);
}
else {
throw Base::TypeError("Not allowed type used in tuple (float expected)...");
}
// y
item = PyTuple_GetItem(value, 1);
if (PyFloat_Check(item)) {
cVec.y = PyFloat_AsDouble(item);
}
else if (PyLong_Check(item)) {
cVec.y = (double)PyLong_AsLong(item);
}
else {
throw Base::TypeError("Not allowed type used in tuple (float expected)...");
}
// z
item = PyTuple_GetItem(value, 2);
if (PyFloat_Check(item)) {
cVec.z = PyFloat_AsDouble(item);
}
else if (PyLong_Check(item)) {
cVec.z = (double)PyLong_AsLong(item);
}
else {
throw Base::TypeError("Not allowed type used in tuple (float expected)...");
}
setValue(cVec);
}
else {
std::string error = std::string("type must be 'Vector' or tuple of three floats, not ");
error += value->ob_type->tp_name;
throw Base::TypeError(error);
}
}
void PropertyVector::Save(Base::Writer& writer) const
{
// clang-format off
writer.Stream() << writer.ind()
<< "<PropertyVector"
<< " valueX=\"" << _cVec.x << "\""
<< " valueY=\"" << _cVec.y << "\""
<< " valueZ=\"" << _cVec.z << "\""
<< "/>\n";
// clang-format on
}
void PropertyVector::Restore(Base::XMLReader& reader)
{
// read my Element
reader.readElement("PropertyVector");
// get the value of my Attribute
aboutToSetValue();
_cVec.x = reader.getAttribute<double>("valueX");
_cVec.y = reader.getAttribute<double>("valueY");
_cVec.z = reader.getAttribute<double>("valueZ");
hasSetValue();
}
Property* PropertyVector::Copy() const
{
PropertyVector* p = new PropertyVector();
p->_cVec = _cVec;
return p;
}
void PropertyVector::Paste(const Property& from)
{
aboutToSetValue();
_cVec = dynamic_cast<const PropertyVector&>(from)._cVec;
hasSetValue();
}
void PropertyVector::getPaths(std::vector<ObjectIdentifier>& paths) const
{
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("x")));
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("y")));
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("z")));
}
const boost::any PropertyVector::getPathValue(const ObjectIdentifier& path) const
{
Base::Unit unit = getUnit();
if (unit != Unit::One) {
std::string p = path.getSubPathStr();
if (p == ".x" || p == ".y" || p == ".z") {
// Convert double to quantity
return Base::Quantity(boost::any_cast<double>(Property::getPathValue(path)), unit);
}
}
return Property::getPathValue(path);
}
bool PropertyVector::getPyPathValue(const ObjectIdentifier& path, Py::Object& res) const
{
Base::Unit unit = getUnit();
if (unit == Unit::One) {
return false;
}
std::string p = path.getSubPathStr();
if (p == ".x") {
res = Py::asObject(new QuantityPy(new Quantity(getValue().x, unit)));
}
else if (p == ".y") {
res = Py::asObject(new QuantityPy(new Quantity(getValue().y, unit)));
}
else if (p == ".z") {
res = Py::asObject(new QuantityPy(new Quantity(getValue().z, unit)));
}
else {
return false;
}
return true;
}
//**************************************************************************
// PropertyVectorDistance
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TYPESYSTEM_SOURCE(App::PropertyVectorDistance, App::PropertyVector)
//**************************************************************************
// Construction/Destruction
PropertyVectorDistance::PropertyVectorDistance() = default;
PropertyVectorDistance::~PropertyVectorDistance() = default;
//**************************************************************************
// PropertyPosition
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TYPESYSTEM_SOURCE(App::PropertyPosition, App::PropertyVector)
//**************************************************************************
// Construction/Destruction
PropertyPosition::PropertyPosition() = default;
PropertyPosition::~PropertyPosition() = default;
//**************************************************************************
// PropertyPosition
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TYPESYSTEM_SOURCE(App::PropertyDirection, App::PropertyVector)
//**************************************************************************
// Construction/Destruction
PropertyDirection::PropertyDirection() = default;
PropertyDirection::~PropertyDirection() = default;
//**************************************************************************
// PropertyVectorList
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TYPESYSTEM_SOURCE(App::PropertyVectorList, App::PropertyLists)
//**************************************************************************
// Construction/Destruction
PropertyVectorList::PropertyVectorList() = default;
PropertyVectorList::~PropertyVectorList() = default;
//**************************************************************************
// Base class implementer
void PropertyVectorList::setValue(double x, double y, double z)
{
setValue(Base::Vector3d(x, y, z));
}
PyObject* PropertyVectorList::getPyObject()
{
PyObject* list = PyList_New(getSize());
for (int i = 0; i < getSize(); i++) {
PyList_SetItem(list, i, new VectorPy(_lValueList[i]));
}
return list;
}
Base::Vector3d PropertyVectorList::getPyValue(PyObject* item) const
{
PropertyVector val;
val.setPyObject(item);
return val.getValue();
}
void PropertyVectorList::Save(Base::Writer& writer) const
{
if (!writer.isForceXML()) {
writer.Stream() << writer.ind() << "<VectorList file=\"" << writer.addFile(getName(), this)
<< "\"/>" << std::endl;
}
}
void PropertyVectorList::Restore(Base::XMLReader& reader)
{
reader.readElement("VectorList");
std::string file(reader.getAttribute<const char*>("file"));
if (!file.empty()) {
// initiate a file read
reader.addFile(file.c_str(), this);
}
}
void PropertyVectorList::SaveDocFile(Base::Writer& writer) const
{
Base::OutputStream str(writer.Stream());
uint32_t uCt = (uint32_t)getSize();
str << uCt;
if (!isSinglePrecision()) {
for (const auto& it : _lValueList) {
str << it.x << it.y << it.z;
}
}
else {
for (const auto& it : _lValueList) {
float x = (float)it.x;
float y = (float)it.y;
float z = (float)it.z;
str << x << y << z;
}
}
}
void PropertyVectorList::RestoreDocFile(Base::Reader& reader)
{
Base::InputStream str(reader);
uint32_t uCt = 0;
str >> uCt;
std::vector<Base::Vector3d> values(uCt);
if (!isSinglePrecision()) {
for (auto& it : values) {
str >> it.x >> it.y >> it.z;
}
}
else {
Base::Vector3f vec;
for (auto& it : values) {
str >> vec.x >> vec.y >> vec.z;
it.Set(vec.x, vec.y, vec.z);
}
}
setValues(values);
}
Property* PropertyVectorList::Copy() const
{
PropertyVectorList* p = new PropertyVectorList();
p->_lValueList = _lValueList;
return p;
}
void PropertyVectorList::Paste(const Property& from)
{
setValues(dynamic_cast<const PropertyVectorList&>(from)._lValueList);
}
unsigned int PropertyVectorList::getMemSize() const
{
return static_cast<unsigned int>(_lValueList.size() * sizeof(Base::Vector3d));
}
//**************************************************************************
//**************************************************************************
// PropertyMatrix
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TYPESYSTEM_SOURCE(App::PropertyMatrix, App::Property)
//**************************************************************************
// Construction/Destruction
PropertyMatrix::PropertyMatrix() = default;
PropertyMatrix::~PropertyMatrix() = default;
//**************************************************************************
// Base class implementer
void PropertyMatrix::setValue(const Base::Matrix4D& mat)
{
aboutToSetValue();
_cMat = mat;
hasSetValue();
}
const Base::Matrix4D& PropertyMatrix::getValue() const
{
return _cMat;
}
PyObject* PropertyMatrix::getPyObject()
{
return new Base::MatrixPy(_cMat);
}
void PropertyMatrix::setPyObject(PyObject* value)
{
if (PyObject_TypeCheck(value, &(Base::MatrixPy::Type))) {
Base::MatrixPy* pcObject = static_cast<Base::MatrixPy*>(value);
setValue(pcObject->value());
}
else if (PyTuple_Check(value) && PyTuple_Size(value) == 16) {
PyObject* item;
Base::Matrix4D cMatrix;
const int dim = 4;
for (int x = 0; x < dim; x++) {
for (int y = 0; y < dim; y++) {
item = PyTuple_GetItem(value, x + y * dim);
if (PyFloat_Check(item)) {
cMatrix[x][y] = PyFloat_AsDouble(item);
}
else if (PyLong_Check(item)) {
cMatrix[x][y] = (double)PyLong_AsLong(item);
}
else {
throw Base::TypeError(
"Not allowed type used in matrix tuple (a number expected)...");
}
}
}
setValue(cMatrix);
}
else {
std::string error = std::string("type must be 'Matrix' or tuple of 16 float or int, not ");
error += value->ob_type->tp_name;
throw Base::TypeError(error);
}
}
void PropertyMatrix::Save(Base::Writer& writer) const
{
// clang-format off
writer.Stream() << writer.ind() << "<PropertyMatrix";
writer.Stream() << " a11=\"" << _cMat[0][0] << "\" a12=\"" << _cMat[0][1] << "\" a13=\"" << _cMat[0][2] << "\" a14=\"" << _cMat[0][3] << "\"";
writer.Stream() << " a21=\"" << _cMat[1][0] << "\" a22=\"" << _cMat[1][1] << "\" a23=\"" << _cMat[1][2] << "\" a24=\"" << _cMat[1][3] << "\"";
writer.Stream() << " a31=\"" << _cMat[2][0] << "\" a32=\"" << _cMat[2][1] << "\" a33=\"" << _cMat[2][2] << "\" a34=\"" << _cMat[2][3] << "\"";
writer.Stream() << " a41=\"" << _cMat[3][0] << "\" a42=\"" << _cMat[3][1] << "\" a43=\"" << _cMat[3][2] << "\" a44=\"" << _cMat[3][3] << "\"";
writer.Stream() <<"/>" << endl;
// clang-format on
}
void PropertyMatrix::Restore(Base::XMLReader& reader)
{
// read my Element
reader.readElement("PropertyMatrix");
// get the value of my Attribute
aboutToSetValue();
_cMat[0][0] = reader.getAttribute<double>("a11");
_cMat[0][1] = reader.getAttribute<double>("a12");
_cMat[0][2] = reader.getAttribute<double>("a13");
_cMat[0][3] = reader.getAttribute<double>("a14");
_cMat[1][0] = reader.getAttribute<double>("a21");
_cMat[1][1] = reader.getAttribute<double>("a22");
_cMat[1][2] = reader.getAttribute<double>("a23");
_cMat[1][3] = reader.getAttribute<double>("a24");
_cMat[2][0] = reader.getAttribute<double>("a31");
_cMat[2][1] = reader.getAttribute<double>("a32");
_cMat[2][2] = reader.getAttribute<double>("a33");
_cMat[2][3] = reader.getAttribute<double>("a34");
_cMat[3][0] = reader.getAttribute<double>("a41");
_cMat[3][1] = reader.getAttribute<double>("a42");
_cMat[3][2] = reader.getAttribute<double>("a43");
_cMat[3][3] = reader.getAttribute<double>("a44");
hasSetValue();
}
Property* PropertyMatrix::Copy() const
{
PropertyMatrix* p = new PropertyMatrix();
p->_cMat = _cMat;
return p;
}
void PropertyMatrix::Paste(const Property& from)
{
aboutToSetValue();
_cMat = dynamic_cast<const PropertyMatrix&>(from)._cMat;
hasSetValue();
}
//**************************************************************************
//**************************************************************************
// PropertyPlacement
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TYPESYSTEM_SOURCE(App::PropertyPlacement, App::Property)
//**************************************************************************
// Construction/Destruction
PropertyPlacement::PropertyPlacement() = default;
PropertyPlacement::~PropertyPlacement() = default;
//**************************************************************************
// Base class implementer
void PropertyPlacement::setValue(const Base::Placement& pos)
{
aboutToSetValue();
_cPos = pos;
hasSetValue();
}
bool PropertyPlacement::setValueIfChanged(const Base::Placement& pos, double tol, double atol)
{
if (_cPos.getPosition().IsEqual(pos.getPosition(), tol)
&& _cPos.getRotation().isSame(pos.getRotation(), atol)) {
return false;
}
setValue(pos);
return true;
}
const Base::Placement& PropertyPlacement::getValue() const
{
return _cPos;
}
void PropertyPlacement::getPaths(std::vector<ObjectIdentifier>& paths) const
{
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Base"))
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("x")));
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Base"))
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("y")));
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Base"))
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("z")));
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Rotation"))
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Angle")));
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Rotation"))
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Axis"))
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("x")));
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Rotation"))
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Axis"))
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("y")));
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Rotation"))
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Axis"))
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("z")));
}
namespace
{
double toDouble(const boost::any& value)
{
double avalue {};
if (value.type() == typeid(Base::Quantity)) {
avalue = boost::any_cast<Base::Quantity>(value).getValue();
}
else if (value.type() == typeid(double)) {
avalue = boost::any_cast<double>(value);
}
else if (value.type() == typeid(int)) {
avalue = boost::any_cast<int>(value);
}
else if (value.type() == typeid(unsigned int)) {
avalue = boost::any_cast<unsigned int>(value);
}
else if (value.type() == typeid(short)) {
avalue = boost::any_cast<short>(value);
}
else if (value.type() == typeid(unsigned short)) {
avalue = boost::any_cast<unsigned short>(value);
}
else if (value.type() == typeid(long)) {
avalue = boost::any_cast<long>(value);
}
else if (value.type() == typeid(unsigned long)) {
avalue = boost::any_cast<unsigned long>(value);
}
else {
throw std::bad_cast();
}
return avalue;
}
} // namespace
void PropertyPlacement::setPathValue(const ObjectIdentifier& path, const boost::any& value)
{
auto updateAxis = [this](int index, double coord) {
Base::Vector3d axis;
double angle;
Base::Vector3d base = _cPos.getPosition();
Base::Rotation rot = _cPos.getRotation();
rot.getRawValue(axis, angle);
axis[index] = coord;
rot.setValue(axis, angle);
Base::Placement plm(base, rot);
setValue(plm);
};
auto updateYawPitchRoll = [this](int index, double angle) {
Base::Vector3d base = _cPos.getPosition();
Base::Rotation rot = _cPos.getRotation();
double yaw, pitch, roll;
rot.getYawPitchRoll(yaw, pitch, roll);
if (index == 0) {
if (angle < -180.0 || angle > 180.0) {
throw Base::ValueError("Yaw angle is out of range [-180, +180]");
}
yaw = angle;
}
else if (index == 1) {
if (angle < -90.0 || angle > 90.0) {
throw Base::ValueError("Pitch angle is out of range [-90, +90]");
}
pitch = angle;
}
else if (index == 2) {
if (angle < -180.0 || angle > 180.0) {
throw Base::ValueError("Roll angle is out of range [-180, +180]");
}
roll = angle;
}
rot.setYawPitchRoll(yaw, pitch, roll);
Base::Placement plm(base, rot);
setValue(plm);
};
std::string subpath = path.getSubPathStr();
if (subpath == ".Rotation.Angle") {
double avalue = toDouble(value);
Property::setPathValue(path, Base::toRadians(avalue));
}
else if (subpath == ".Rotation.Axis.x") {
updateAxis(0, toDouble(value));
}
else if (subpath == ".Rotation.Axis.y") {
updateAxis(1, toDouble(value));
}
else if (subpath == ".Rotation.Axis.z") {
updateAxis(2, toDouble(value));
}
else if (subpath == ".Rotation.Yaw") {
updateYawPitchRoll(0, toDouble(value));
}
else if (subpath == ".Rotation.Pitch") {
updateYawPitchRoll(1, toDouble(value));
}
else if (subpath == ".Rotation.Roll") {
updateYawPitchRoll(2, toDouble(value));
}
else {
Property::setPathValue(path, value);
}
}
const boost::any PropertyPlacement::getPathValue(const ObjectIdentifier& path) const
{
auto getAxis = [](const Base::Placement& plm) {
Base::Vector3d axis;
double angle;
const Base::Rotation& rot = plm.getRotation();
rot.getRawValue(axis, angle);
return axis;
};
auto getYawPitchRoll = [](const Base::Placement& plm) {
Base::Vector3d ypr;
const Base::Rotation& rot = plm.getRotation();
rot.getYawPitchRoll(ypr.x, ypr.y, ypr.z);
return ypr;
};
std::string p = path.getSubPathStr();
if (p == ".Rotation.Angle") {
// Convert angle to degrees
return Base::Quantity(
Base::toDegrees(boost::any_cast<double>(Property::getPathValue(path))),
Unit::Angle);
}
else if (p == ".Base.x" || p == ".Base.y" || p == ".Base.z") {
// Convert double to quantity
return Base::Quantity(boost::any_cast<double>(Property::getPathValue(path)), Unit::Length);
}
else if (p == ".Rotation.Axis.x") {
return getAxis(_cPos).x;
}
else if (p == ".Rotation.Axis.y") {
return getAxis(_cPos).y;
}
else if (p == ".Rotation.Axis.z") {
return getAxis(_cPos).z;
}
else if (p == ".Rotation.Yaw") {
return getYawPitchRoll(_cPos).x;
}
else if (p == ".Rotation.Pitch") {
return getYawPitchRoll(_cPos).y;
}
else if (p == ".Rotation.Roll") {
return getYawPitchRoll(_cPos).z;
}
else {
return Property::getPathValue(path);
}
}
bool PropertyPlacement::getPyPathValue(const ObjectIdentifier& path, Py::Object& res) const
{
auto getAxis = [](const Base::Placement& plm) {
Base::Vector3d axis;
double angle;
const Base::Rotation& rot = plm.getRotation();
rot.getRawValue(axis, angle);
return axis;
};
auto getYawPitchRoll = [](const Base::Placement& plm) {
Base::Vector3d ypr;
const Base::Rotation& rot = plm.getRotation();
rot.getYawPitchRoll(ypr.x, ypr.y, ypr.z);
return ypr;
};
std::string p = path.getSubPathStr();
if (p == ".Rotation.Angle") {
Base::Vector3d axis;
double angle;
_cPos.getRotation().getValue(axis, angle);
res = Py::asObject(new QuantityPy(new Quantity(Base::toDegrees(angle), Unit::Angle)));
return true;
}
else if (p == ".Base.x") {
res = Py::asObject(new QuantityPy(new Quantity(_cPos.getPosition().x, Unit::Length)));
return true;
}
else if (p == ".Base.y") {
res = Py::asObject(new QuantityPy(new Quantity(_cPos.getPosition().y, Unit::Length)));
return true;
}
else if (p == ".Base.z") {
res = Py::asObject(new QuantityPy(new Quantity(_cPos.getPosition().z, Unit::Length)));
return true;
}
else if (p == ".Rotation.Axis.x") {
res = Py::Float(getAxis(_cPos).x);
return true;
}
else if (p == ".Rotation.Axis.y") {
res = Py::Float(getAxis(_cPos).y);
return true;
}
else if (p == ".Rotation.Axis.z") {
res = Py::Float(getAxis(_cPos).z);
return true;
}
else if (p == ".Rotation.Yaw") {
res = Py::Float(getYawPitchRoll(_cPos).x);
return true;
}
else if (p == ".Rotation.Pitch") {
res = Py::Float(getYawPitchRoll(_cPos).y);
return true;
}
else if (p == ".Rotation.Roll") {
res = Py::Float(getYawPitchRoll(_cPos).z);
return true;
}
return false;
}
PyObject* PropertyPlacement::getPyObject()
{
return new Base::PlacementPy(new Base::Placement(_cPos));
}
void PropertyPlacement::setPyObject(PyObject* value)
{
if (PyObject_TypeCheck(value, &(Base::MatrixPy::Type))) {
Base::MatrixPy* pcObject = static_cast<Base::MatrixPy*>(value);
Base::Matrix4D mat = pcObject->value();
Base::Placement p;
p.fromMatrix(mat);
setValue(p);
}
else if (PyObject_TypeCheck(value, &(Base::PlacementPy::Type))) {
setValue(*static_cast<Base::PlacementPy*>(value)->getPlacementPtr());
}
else {
std::string error = std::string("type must be 'Matrix' or 'Placement', not ");
error += value->ob_type->tp_name;
throw Base::TypeError(error);
}
}
void PropertyPlacement::Save(Base::Writer& writer) const
{
// clang-format off
writer.Stream() << writer.ind() << "<PropertyPlacement";
writer.Stream() << " Px=\"" << _cPos.getPosition().x << "\""
<< " Py=\"" << _cPos.getPosition().y << "\""
<< " Pz=\"" << _cPos.getPosition().z << "\"";
writer.Stream() << " Q0=\"" << _cPos.getRotation()[0] << "\""
<< " Q1=\"" << _cPos.getRotation()[1] << "\""
<< " Q2=\"" << _cPos.getRotation()[2] << "\""
<< " Q3=\"" << _cPos.getRotation()[3] << "\"";
Vector3d axis;
double rfAngle {};
_cPos.getRotation().getRawValue(axis, rfAngle);
writer.Stream() << " A=\"" << rfAngle << "\""
<< " Ox=\"" << axis.x << "\""
<< " Oy=\"" << axis.y << "\""
<< " Oz=\"" << axis.z << "\"";
writer.Stream() << "/>" << std::endl;
// clang-format on
}
void PropertyPlacement::Restore(Base::XMLReader& reader)
{
// read my Element
reader.readElement("PropertyPlacement");
// get the value of my Attribute
aboutToSetValue();
if (reader.hasAttribute("A")) {
_cPos = Base::Placement(Vector3d(reader.getAttribute<double>("Px"),
reader.getAttribute<double>("Py"),
reader.getAttribute<double>("Pz")),
Rotation(Vector3d(reader.getAttribute<double>("Ox"),
reader.getAttribute<double>("Oy"),
reader.getAttribute<double>("Oz")),
reader.getAttribute<double>("A")));
}
else {
_cPos = Base::Placement(Vector3d(reader.getAttribute<double>("Px"),
reader.getAttribute<double>("Py"),
reader.getAttribute<double>("Pz")),
Rotation(reader.getAttribute<double>("Q0"),
reader.getAttribute<double>("Q1"),
reader.getAttribute<double>("Q2"),
reader.getAttribute<double>("Q3")));
}
hasSetValue();
}
Property* PropertyPlacement::Copy() const
{
PropertyPlacement* p = new PropertyPlacement();
p->_cPos = _cPos;
return p;
}
void PropertyPlacement::Paste(const Property& from)
{
aboutToSetValue();
_cPos = dynamic_cast<const PropertyPlacement&>(from)._cPos;
hasSetValue();
}
//**************************************************************************
// PropertyPlacementList
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TYPESYSTEM_SOURCE(App::PropertyPlacementList, App::PropertyLists)
//**************************************************************************
// Construction/Destruction
PropertyPlacementList::PropertyPlacementList() = default;
PropertyPlacementList::~PropertyPlacementList() = default;
//**************************************************************************
// Base class implementer
PyObject* PropertyPlacementList::getPyObject()
{
PyObject* list = PyList_New(getSize());
for (int i = 0; i < getSize(); i++) {
PyList_SetItem(list, i, new Base::PlacementPy(new Base::Placement(_lValueList[i])));
}
return list;
}
Base::Placement PropertyPlacementList::getPyValue(PyObject* item) const
{
PropertyPlacement val;
val.setPyObject(item);
return val.getValue();
}
void PropertyPlacementList::Save(Base::Writer& writer) const
{
if (!writer.isForceXML()) {
writer.Stream() << writer.ind() << "<PlacementList file=\""
<< writer.addFile(getName(), this) << "\"/>" << std::endl;
}
}
void PropertyPlacementList::Restore(Base::XMLReader& reader)
{
reader.readElement("PlacementList");
std::string file(reader.getAttribute<const char*>("file"));
if (!file.empty()) {
// initiate a file read
reader.addFile(file.c_str(), this);
}
}
void PropertyPlacementList::SaveDocFile(Base::Writer& writer) const
{
Base::OutputStream str(writer.Stream());
uint32_t uCt = (uint32_t)getSize();
str << uCt;
if (!isSinglePrecision()) {
for (const auto& it : _lValueList) {
str << it.getPosition().x << it.getPosition().y << it.getPosition().z
<< it.getRotation()[0] << it.getRotation()[1] << it.getRotation()[2]
<< it.getRotation()[3];
}
}
else {
for (const auto& it : _lValueList) {
float x = (float)it.getPosition().x;
float y = (float)it.getPosition().y;
float z = (float)it.getPosition().z;
float q0 = (float)it.getRotation()[0];
float q1 = (float)it.getRotation()[1];
float q2 = (float)it.getRotation()[2];
float q3 = (float)it.getRotation()[3];
str << x << y << z << q0 << q1 << q2 << q3;
}
}
}
void PropertyPlacementList::RestoreDocFile(Base::Reader& reader)
{
Base::InputStream str(reader);
uint32_t uCt = 0;
str >> uCt;
std::vector<Base::Placement> values(uCt);
if (!isSinglePrecision()) {
for (auto& it : values) {
Base::Vector3d pos;
double q0, q1, q2, q3;
str >> pos.x >> pos.y >> pos.z >> q0 >> q1 >> q2 >> q3;
Base::Rotation rot(q0, q1, q2, q3);
it.setPosition(pos);
it.setRotation(rot);
}
}
else {
float x {}, y {}, z {};
float q0 {}, q1 {}, q2 {}, q3 {};
for (auto& it : values) {
str >> x >> y >> z >> q0 >> q1 >> q2 >> q3;
Base::Vector3d pos(x, y, z);
Base::Rotation rot(q0, q1, q2, q3);
it.setPosition(pos);
it.setRotation(rot);
}
}
setValues(values);
}
Property* PropertyPlacementList::Copy() const
{
PropertyPlacementList* p = new PropertyPlacementList();
p->_lValueList = _lValueList;
return p;
}
void PropertyPlacementList::Paste(const Property& from)
{
setValues(dynamic_cast<const PropertyPlacementList&>(from)._lValueList);
}
unsigned int PropertyPlacementList::getMemSize() const
{
return static_cast<unsigned int>(_lValueList.size() * sizeof(Base::Vector3d));
}
//**************************************************************************
//**************************************************************************
// PropertyPlacement
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TYPESYSTEM_SOURCE(App::PropertyPlacementLink, App::PropertyLink)
//**************************************************************************
// Construction/Destruction
PropertyPlacementLink::PropertyPlacementLink() = default;
PropertyPlacementLink::~PropertyPlacementLink() = default;
App::Placement* PropertyPlacementLink::getPlacementObject() const
{
if (_pcLink->isDerivedFrom<App::Placement>()) {
return dynamic_cast<App::Placement*>(_pcLink);
}
else {
return nullptr;
}
}
//**************************************************************************
// Base class implementer
Property* PropertyPlacementLink::Copy() const
{
PropertyPlacementLink* p = new PropertyPlacementLink();
p->_pcLink = _pcLink;
return p;
}
void PropertyPlacementLink::Paste(const Property& from)
{
aboutToSetValue();
_pcLink = dynamic_cast<const PropertyPlacementLink&>(from)._pcLink;
hasSetValue();
}
//**************************************************************************
//**************************************************************************
// PropertyRotation
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TYPESYSTEM_SOURCE(App::PropertyRotation, App::Property)
PropertyRotation::PropertyRotation() = default;
PropertyRotation::~PropertyRotation() = default;
void PropertyRotation::setValue(const Base::Rotation& rot)
{
aboutToSetValue();
_rot = rot;
hasSetValue();
}
bool PropertyRotation::setValueIfChanged(const Base::Rotation& rot, double atol)
{
if (_rot.isSame(rot, atol)) {
return false;
}
setValue(rot);
return true;
}
const Base::Rotation& PropertyRotation::getValue() const
{
return _rot;
}
void PropertyRotation::getPaths(std::vector<ObjectIdentifier>& paths) const
{
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Angle")));
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Axis"))
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("x")));
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Axis"))
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("y")));
paths.push_back(ObjectIdentifier(*this)
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Axis"))
<< ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("z")));
}
void PropertyRotation::setPathValue(const ObjectIdentifier& path, const boost::any& value)
{
auto updateAxis = [this](int index, double coord) {
Base::Vector3d axis;
double angle;
_rot.getRawValue(axis, angle);
axis[index] = coord;
setValue(Base::Rotation {axis, angle});
};
std::string subpath = path.getSubPathStr();
if (subpath == ".Angle") {
double avalue = toDouble(value);
Property::setPathValue(path, Base::toRadians(avalue));
}
else if (subpath == ".Axis.x") {
updateAxis(0, toDouble(value));
}
else if (subpath == ".Axis.y") {
updateAxis(1, toDouble(value));
}
else if (subpath == ".Axis.z") {
updateAxis(2, toDouble(value));
}
else {
Property::setPathValue(path, value);
}
}
const boost::any PropertyRotation::getPathValue(const ObjectIdentifier& path) const
{
auto getAxis = [](const Base::Rotation& rot) {
Base::Vector3d axis;
double angle;
rot.getRawValue(axis, angle);
return axis;
};
std::string p = path.getSubPathStr();
if (p == ".Angle") {
// Convert angle to degrees
return Base::Quantity(
Base::toDegrees(boost::any_cast<double>(Property::getPathValue(path))),
Unit::Angle);
}
else if (p == ".Axis.x") {
return getAxis(_rot).x;
}
else if (p == ".Axis.y") {
return getAxis(_rot).y;
}
else if (p == ".Axis.z") {
return getAxis(_rot).z;
}
else {
return Property::getPathValue(path);
}
}
bool PropertyRotation::getPyPathValue(const ObjectIdentifier& path, Py::Object& res) const
{
auto getAxis = [](const Base::Rotation& rot) {
Base::Vector3d axis;
double angle;
rot.getRawValue(axis, angle);
return axis;
};
std::string p = path.getSubPathStr();
if (p == ".Angle") {
Base::Vector3d axis;
double angle;
_rot.getValue(axis, angle);
res = Py::asObject(new QuantityPy(new Quantity(Base::toDegrees(angle), Unit::Angle)));
return true;
}
else if (p == ".Axis.x") {
res = Py::Float(getAxis(_rot).x);
return true;
}
else if (p == ".Axis.y") {
res = Py::Float(getAxis(_rot).y);
return true;
}
else if (p == ".Axis.z") {
res = Py::Float(getAxis(_rot).z);
return true;
}
return false;
}
PyObject* PropertyRotation::getPyObject()
{
return new Base::RotationPy(new Base::Rotation(_rot));
}
void PropertyRotation::setPyObject(PyObject* value)
{
if (PyObject_TypeCheck(value, &(Base::MatrixPy::Type))) {
Base::MatrixPy* object = static_cast<Base::MatrixPy*>(value);
Base::Matrix4D mat = object->value();
Base::Rotation p;
p.setValue(mat);
setValue(p);
}
else if (PyObject_TypeCheck(value, &(Base::RotationPy::Type))) {
setValue(*static_cast<Base::RotationPy*>(value)->getRotationPtr());
}
else {
std::string error = std::string("type must be 'Matrix' or 'Rotation', not ");
error += value->ob_type->tp_name;
throw Base::TypeError(error);
}
}
void PropertyRotation::Save(Base::Writer& writer) const
{
Vector3d axis;
double rfAngle {};
_rot.getRawValue(axis, rfAngle);
writer.Stream() << writer.ind() << "<PropertyRotation";
writer.Stream() << " A=\"" << rfAngle << "\""
<< " Ox=\"" << axis.x << "\""
<< " Oy=\"" << axis.y << "\""
<< " Oz=\"" << axis.z << "\""
<< "/>\n";
}
void PropertyRotation::Restore(Base::XMLReader& reader)
{
reader.readElement("PropertyRotation");
aboutToSetValue();
_rot = Rotation(Vector3d(reader.getAttribute<double>("Ox"),
reader.getAttribute<double>("Oy"),
reader.getAttribute<double>("Oz")),
reader.getAttribute<double>("A"));
hasSetValue();
}
Property* PropertyRotation::Copy() const
{
PropertyRotation* p = new PropertyRotation();
p->_rot = _rot;
return p;
}
void PropertyRotation::Paste(const Property& from)
{
aboutToSetValue();
_rot = dynamic_cast<const PropertyRotation&>(from)._rot;
hasSetValue();
}
// ------------------------------------------------------------
TYPESYSTEM_SOURCE_ABSTRACT(App::PropertyGeometry, App::Property)
PropertyGeometry::PropertyGeometry() = default;
PropertyGeometry::~PropertyGeometry() = default;
// ------------------------------------------------------------
TYPESYSTEM_SOURCE_ABSTRACT(App::PropertyComplexGeoData, App::PropertyGeometry)
PropertyComplexGeoData::PropertyComplexGeoData() = default;
PropertyComplexGeoData::~PropertyComplexGeoData() = default;
std::string PropertyComplexGeoData::getElementMapVersion(bool) const
{
auto data = getComplexData();
if (!data) {
return std::string();
}
auto owner = freecad_cast<DocumentObject*>(getContainer());
std::ostringstream ss;
if (owner && owner->getDocument() && owner->getDocument()->getStringHasher() == data->Hasher) {
ss << "1.";
}
else {
ss << "0.";
}
ss << data->getElementMapVersion();
return ss.str();
}
bool PropertyComplexGeoData::checkElementMapVersion(const char* ver) const
{
auto data = getComplexData();
if (!data) {
return false;
}
auto owner = freecad_cast<DocumentObject*>(getContainer());
std::ostringstream ss;
const char* prefix;
if (owner && owner->getDocument() && owner->getDocument()->getStringHasher() == data->Hasher) {
prefix = "1.";
}
else {
prefix = "0.";
}
if (!boost::starts_with(ver, prefix)) {
return true;
}
return data->checkElementMapVersion(ver + 2);
}
void PropertyComplexGeoData::afterRestore()
{
auto data = getComplexData();
if (data && data->isRestoreFailed()) {
data->resetRestoreFailure();
auto owner = freecad_cast<DocumentObject*>(getContainer());
if (owner && owner->getDocument()
&& !owner->getDocument()->testStatus(App::Document::PartialDoc)) {
owner->getDocument()->addRecomputeObject(owner);
}
}
PropertyGeometry::afterRestore();
}