From 18a5ff83b126667a0467703c9cc0e3efa802c3de Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 26 Oct 2016 10:28:23 +0200 Subject: [PATCH] add support of Hermite curves --- src/Mod/Part/App/AppPart.cpp | 3 + src/Mod/Part/App/CMakeLists.txt | 3 + src/Mod/Part/App/Geometry.cpp | 147 +++++++++++++++++++++++ src/Mod/Part/App/Geometry.h | 54 +++++++++ src/Mod/Part/App/HermiteCurvePy.xml | 30 +++++ src/Mod/Part/App/HermiteCurvePyImp.cpp | 155 +++++++++++++++++++++++++ 6 files changed, 392 insertions(+) create mode 100644 src/Mod/Part/App/HermiteCurvePy.xml create mode 100644 src/Mod/Part/App/HermiteCurvePyImp.cpp diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index cc614c9c1..4e623335a 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -75,6 +75,7 @@ #include "ArcOfHyperbolaPy.h" #include "BezierCurvePy.h" #include "BSplineCurvePy.h" +#include "HermiteCurvePy.h" #include "HyperbolaPy.h" #include "OffsetCurvePy.h" #include "ParabolaPy.h" @@ -200,6 +201,7 @@ PyMODINIT_FUNC initPart() Base::Interpreter().addType(&Part::ArcOfHyperbolaPy ::Type,partModule,"ArcOfHyperbola"); Base::Interpreter().addType(&Part::BezierCurvePy ::Type,partModule,"BezierCurve"); Base::Interpreter().addType(&Part::BSplineCurvePy ::Type,partModule,"BSplineCurve"); + Base::Interpreter().addType(&Part::HermiteCurvePy ::Type,partModule,"HermiteCurve"); Base::Interpreter().addType(&Part::OffsetCurvePy ::Type,partModule,"OffsetCurve"); Base::Interpreter().addType(&Part::PlanePy ::Type,partModule,"Plane"); @@ -320,6 +322,7 @@ PyMODINIT_FUNC initPart() Part::GeomCurve ::init(); Part::GeomBezierCurve ::init(); Part::GeomBSplineCurve ::init(); + Part::GeomHermiteCurve ::init(); Part::GeomCircle ::init(); Part::GeomArcOfCircle ::init(); Part::GeomArcOfEllipse ::init(); diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index bee0cdcee..dba514687 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -56,6 +56,7 @@ generate_from_xml(LinePy) generate_from_xml(PointPy) generate_from_xml(BezierCurvePy) generate_from_xml(BSplineCurvePy) +generate_from_xml(HermiteCurvePy) generate_from_xml(PlanePy) generate_from_xml(ConePy) generate_from_xml(CylinderPy) @@ -194,6 +195,8 @@ SET(Python_SRCS BezierCurvePyImp.cpp BSplineCurvePy.xml BSplineCurvePyImp.cpp + HermiteCurvePy.xml + HermiteCurvePyImp.cpp PlanePy.xml PlanePyImp.cpp ConePy.xml diff --git a/src/Mod/Part/App/Geometry.cpp b/src/Mod/Part/App/Geometry.cpp index 4e9e980ac..0c2f22dc6 100644 --- a/src/Mod/Part/App/Geometry.cpp +++ b/src/Mod/Part/App/Geometry.cpp @@ -50,6 +50,7 @@ # include # include # include +# include # include # include # include @@ -70,8 +71,11 @@ # include # include # include +# include # include # include +# include +# include # include # include # include @@ -103,6 +107,7 @@ #include "ArcOfParabolaPy.h" #include "BezierCurvePy.h" #include "BSplineCurvePy.h" +#include "HermiteCurvePy.h" #include "HyperbolaPy.h" #include "ArcOfHyperbolaPy.h" #include "OffsetCurvePy.h" @@ -467,6 +472,148 @@ PyObject *GeomBezierCurve::getPyObject(void) // ------------------------------------------------- +TYPESYSTEM_SOURCE(Part::GeomHermiteCurve,Part::GeomCurve); + +GeomHermiteCurve::GeomHermiteCurve() +{ + std::vector p; + p.push_back(gp_Pnt(0.0,0.0,0.0)); + p.push_back(gp_Pnt(1.0,0.0,0.0)); + + std::vector t; + t.push_back(gp_Vec(1,0,0)); + t.push_back(gp_Vec(1,0,0)); + + compute(p, t); +} + +GeomHermiteCurve::GeomHermiteCurve(const std::vector& p, + const std::vector& t) +{ + compute(p, t); +} + +GeomHermiteCurve::~GeomHermiteCurve() +{ +} + +void GeomHermiteCurve::interpolate(const std::vector& p, + const std::vector& t) +{ + if (p.size() < 2) + Standard_ConstructionError::Raise(); + if (p.size() != t.size()) + Standard_ConstructionError::Raise(); + + compute(p, t); +} + +void GeomHermiteCurve::getCardinalSplineTangents(const std::vector& p, + const std::vector& c, + std::vector& t) const +{ + // https://de.wikipedia.org/wiki/Kubisch_Hermitescher_Spline#Cardinal_Spline + if (p.size() < 2) + Standard_ConstructionError::Raise(); + if (p.size() != c.size()) + Standard_ConstructionError::Raise(); + + t.resize(p.size()); + if (p.size() == 2) { + t[0] = gp_Vec(p[0], p[1]); + t[1] = gp_Vec(p[0], p[1]); + } + else { + std::size_t e = p.size() - 1; + + for (std::size_t i = 1; i < e; i++) { + gp_Vec v = gp_Vec(p[i-1], p[i+1]); + double f = 0.5 * (1-c[i]); + v.Scale(f); + t[i] = v; + } + + t[0] = t[1]; + t[t.size()-1] = t[t.size()-2]; + } +} + +void GeomHermiteCurve::getCardinalSplineTangents(const std::vector& p, double c, + std::vector& t) const +{ + // https://de.wikipedia.org/wiki/Kubisch_Hermitescher_Spline#Cardinal_Spline + if (p.size() < 2) + Standard_ConstructionError::Raise(); + + t.resize(p.size()); + if (p.size() == 2) { + t[0] = gp_Vec(p[0], p[1]); + t[1] = gp_Vec(p[0], p[1]); + } + else { + std::size_t e = p.size() - 1; + double f = 0.5 * (1-c); + + for (std::size_t i = 1; i < e; i++) { + gp_Vec v = gp_Vec(p[i-1], p[i+1]); + v.Scale(f); + t[i] = v; + } + + t[0] = t[1]; + t[t.size()-1] = t[t.size()-2]; + } +} + +const Handle_Geom_Geometry& GeomHermiteCurve::handle() const +{ + return myCurve; +} + +void GeomHermiteCurve::compute(const std::vector& p, + const std::vector& t) +{ + double tol3d = Precision::Approximation(); + Handle_TColgp_HArray1OfPnt pts = new TColgp_HArray1OfPnt(1, p.size()); + for (std::size_t i=0; iSetValue(i+1, p[i]); + } + + TColgp_Array1OfVec tgs(1, t.size()); + Handle_TColStd_HArray1OfBoolean fgs = new TColStd_HArray1OfBoolean(1, t.size()); + for (std::size_t i=0; iSetValue(i+1, Standard_True); + } + + GeomAPI_Interpolate interpolate(pts, Standard_False, tol3d); + interpolate.Load(tgs, fgs); + interpolate.Perform(); + this->myCurve = interpolate.Curve(); + + this->poles = p; + this->tangents = t; +} + +Geometry *GeomHermiteCurve::clone(void) const +{ + GeomHermiteCurve *newCurve = new GeomHermiteCurve(poles, tangents); + newCurve->Construction = this->Construction; + return newCurve; +} + +// Persistence implementer +unsigned int GeomHermiteCurve::getMemSize (void) const {assert(0); return 0;/* not implemented yet */} +void GeomHermiteCurve::Save (Base::Writer &/*writer*/) const {assert(0); /* not implemented yet */} +void GeomHermiteCurve::Restore (Base::XMLReader &/*reader*/) {assert(0); /* not implemented yet */} + +PyObject *GeomHermiteCurve::getPyObject(void) +{ + return new HermiteCurvePy(static_cast(this->clone())); +} + +// ------------------------------------------------- + TYPESYSTEM_SOURCE(Part::GeomBSplineCurve,Part::GeomCurve); GeomBSplineCurve::GeomBSplineCurve() diff --git a/src/Mod/Part/App/Geometry.h b/src/Mod/Part/App/Geometry.h index 1caf633b0..464cfa41e 100644 --- a/src/Mod/Part/App/Geometry.h +++ b/src/Mod/Part/App/Geometry.h @@ -52,7 +52,10 @@ #include #include #include +#include +#include #include +#include #include #include @@ -150,6 +153,57 @@ private: Handle_Geom_BezierCurve myCurve; }; +/*! + * \brief The GeomHermiteCurve class + * The GeomHermiteCurve describes a cubic Hermite spline. + * @note Since OpenCascade doesn't directly support Hermite splines + * the returned curve will be a B-Spline curve. + */ +class PartExport GeomHermiteCurve : public GeomCurve +{ + TYPESYSTEM_HEADER(); +public: + GeomHermiteCurve(); + GeomHermiteCurve(const std::vector&, const std::vector&); + virtual ~GeomHermiteCurve(); + virtual Geometry *clone(void) const; + /*! + * Set the poles and tangents for the cubic Hermite spline + */ + void interpolate(const std::vector&, const std::vector&); + /*! + * Compute the tangents for a Cardinal spline using the + * the cubic Hermite spline. It uses the method for Cardinal splines. + */ + void getCardinalSplineTangents(const std::vector&, + const std::vector&, + std::vector&) const; + /*! + * Compute the tangents for a Cardinal spline using the + * the cubic Hermite spline. It uses the method for Cardinal splines. + * It uses the same parameter for each tangent. + */ + void getCardinalSplineTangents(const std::vector&, double, + std::vector&) const; + + // Persistence implementer --------------------- + virtual unsigned int getMemSize (void) const; + virtual void Save (Base::Writer &/*writer*/) const; + virtual void Restore(Base::XMLReader &/*reader*/); + // Base implementer ---------------------------- + virtual PyObject *getPyObject(void); + + const Handle_Geom_Geometry& handle() const; + +private: + void compute(const std::vector&, const std::vector&); + +private: + Handle_Geom_BSplineCurve myCurve; + std::vector poles; + std::vector tangents; +}; + class PartExport GeomBSplineCurve : public GeomCurve { TYPESYSTEM_HEADER(); diff --git a/src/Mod/Part/App/HermiteCurvePy.xml b/src/Mod/Part/App/HermiteCurvePy.xml new file mode 100644 index 000000000..b35270dac --- /dev/null +++ b/src/Mod/Part/App/HermiteCurvePy.xml @@ -0,0 +1,30 @@ + + + + + + + Describes a Hermite curve + + + + + Set the poles and tangents to compute the Hermite curve. + + + + + Compute the tangents for a Cardinal spline + + + + diff --git a/src/Mod/Part/App/HermiteCurvePyImp.cpp b/src/Mod/Part/App/HermiteCurvePyImp.cpp new file mode 100644 index 000000000..b87826fc2 --- /dev/null +++ b/src/Mod/Part/App/HermiteCurvePyImp.cpp @@ -0,0 +1,155 @@ +/*************************************************************************** + * Copyright (c) 2016 Werner Mayer * + * * + * 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" + +#include "Geometry.h" +#include + +#include +#include +#include + +using namespace Part; + +// returns a string which represents the object e.g. when printed in python +std::string HermiteCurvePy::representation(void) const +{ + return std::string(""); +} + +PyObject *HermiteCurvePy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper +{ + // create a new instance of HermiteCurvePy and the Twin object + return new HermiteCurvePy(new GeomHermiteCurve); +} + +// constructor method +int HermiteCurvePy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) +{ + return 0; +} + +PyObject* HermiteCurvePy::interpolate(PyObject *args, PyObject *kwds) +{ + PyObject* pts; + PyObject* tgs; + + static char* kwds_interp1[] = {"Points", "Tangents", NULL}; + if (PyArg_ParseTupleAndKeywords(args, kwds, "OO",kwds_interp1, &pts, &tgs)) { + Py::Sequence list(pts); + std::vector interpPoints; + interpPoints.reserve(list.size()); + for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) { + Py::Vector v(*it); + Base::Vector3d pnt = v.toVector(); + interpPoints.push_back(gp_Pnt(pnt.x,pnt.y,pnt.z)); + } + Py::Sequence list2(tgs); + std::vector interpTangents; + interpTangents.reserve(list2.size()); + for (Py::Sequence::iterator it = list2.begin(); it != list2.end(); ++it) { + Py::Vector v(*it); + Base::Vector3d vec = v.toVector(); + interpTangents.push_back(gp_Vec(vec.x,vec.y,vec.z)); + } + + GeomHermiteCurve* hermite = this->getGeomHermiteCurvePtr(); + hermite->interpolate(interpPoints, interpTangents); + Handle_Geom_BSplineCurve aBSpline = Handle_Geom_BSplineCurve::DownCast + (hermite->handle()); + return new BSplineCurvePy(new GeomBSplineCurve(aBSpline)); + } + + return 0; +} + +PyObject* HermiteCurvePy::getCardinalSplineTangents(PyObject *args, PyObject *kwds) +{ + PyObject* pts; + PyObject* tgs; + double parameter; + + static char* kwds_interp1[] = {"Points", "Parameter", NULL}; + if (PyArg_ParseTupleAndKeywords(args, kwds, "Od",kwds_interp1, &pts, ¶meter)) { + Py::Sequence list(pts); + std::vector interpPoints; + interpPoints.reserve(list.size()); + for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) { + Py::Vector v(*it); + Base::Vector3d pnt = v.toVector(); + interpPoints.push_back(gp_Pnt(pnt.x,pnt.y,pnt.z)); + } + + GeomHermiteCurve* hermite = this->getGeomHermiteCurvePtr(); + std::vector tangents; + hermite->getCardinalSplineTangents(interpPoints, parameter, tangents); + + Py::List vec; + for (gp_Vec it : tangents) + vec.append(Py::Vector(Base::Vector3d(it.X(), it.Y(), it.Z()))); + return Py::new_reference_to(vec); + } + + PyErr_Clear(); + static char* kwds_interp2[] = {"Points", "Parameters", NULL}; + if (PyArg_ParseTupleAndKeywords(args, kwds, "OO",kwds_interp2, &pts, &tgs)) { + Py::Sequence list(pts); + std::vector interpPoints; + interpPoints.reserve(list.size()); + for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) { + Py::Vector v(*it); + Base::Vector3d pnt = v.toVector(); + interpPoints.push_back(gp_Pnt(pnt.x,pnt.y,pnt.z)); + } + + Py::Sequence list2(tgs); + std::vector parameters; + parameters.reserve(list2.size()); + for (Py::Sequence::iterator it = list2.begin(); it != list2.end(); ++it) { + Py::Float p(*it); + parameters.push_back(static_cast(p)); + } + + GeomHermiteCurve* hermite = this->getGeomHermiteCurvePtr(); + std::vector tangents; + hermite->getCardinalSplineTangents(interpPoints, parameters, tangents); + + Py::List vec; + for (gp_Vec it : tangents) + vec.append(Py::Vector(Base::Vector3d(it.X(), it.Y(), it.Z()))); + return Py::new_reference_to(vec); + } + + return 0; +} + +PyObject *HermiteCurvePy::getCustomAttributes(const char* /*attr*/) const +{ + return 0; +} + +int HermiteCurvePy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +}