diff --git a/src/Mod/Mesh/App/AppMesh.cpp b/src/Mod/Mesh/App/AppMesh.cpp index e179e90de6..f398fa9408 100644 --- a/src/Mod/Mesh/App/AppMesh.cpp +++ b/src/Mod/Mesh/App/AppMesh.cpp @@ -33,6 +33,7 @@ #include "Mesh.h" #include "MeshPy.h" #include "MeshPointPy.h" +#include "EdgePy.h" #include "FacetPy.h" #include "MeshFeaturePy.h" #include "FeatureMeshImport.h" @@ -66,6 +67,7 @@ PyMOD_INIT_FUNC(Mesh) // add mesh elements Base::Interpreter().addType(&Mesh::MeshPointPy ::Type,meshModule,"MeshPoint"); + Base::Interpreter().addType(&Mesh::EdgePy ::Type,meshModule,"Edge"); Base::Interpreter().addType(&Mesh::FacetPy ::Type,meshModule,"Facet"); Base::Interpreter().addType(&Mesh::MeshPy ::Type,meshModule,"Mesh"); Base::Interpreter().addType(&Mesh::MeshFeaturePy::Type,meshModule,"Feature"); diff --git a/src/Mod/Mesh/App/CMakeLists.txt b/src/Mod/Mesh/App/CMakeLists.txt index c987d475f1..43351fdd9f 100644 --- a/src/Mod/Mesh/App/CMakeLists.txt +++ b/src/Mod/Mesh/App/CMakeLists.txt @@ -28,12 +28,14 @@ if (BUILD_QT5) ) endif() +generate_from_xml(EdgePy) generate_from_xml(FacetPy) generate_from_xml(MeshFeaturePy) generate_from_xml(MeshPointPy) generate_from_xml(MeshPy) SET(Mesh_XML_SRCS + EdgePy.xml FacetPy.xml MeshFeaturePy.xml MeshPointPy.xml @@ -331,6 +333,9 @@ SET(Mesh_SRCS Exporter.h Importer.cpp Importer.h + Edge.cpp + Edge.h + EdgePyImp.cpp Facet.cpp Facet.h FacetPyImp.cpp diff --git a/src/Mod/Mesh/App/Core/Elements.cpp b/src/Mod/Mesh/App/Core/Elements.cpp index 9366fa1415..4e8cad49d3 100644 --- a/src/Mod/Mesh/App/Core/Elements.cpp +++ b/src/Mod/Mesh/App/Core/Elements.cpp @@ -280,6 +280,25 @@ bool MeshGeomEdge::IntersectWithLine (const Base::Vector3f &rclPt, return dist2 + dist3 <= dist1 + eps; } +bool MeshGeomEdge::IsParallel(const MeshGeomEdge &edge) const +{ + Base::Vector3f r(_aclPoints[1] - _aclPoints[0]); + Base::Vector3f s(edge._aclPoints[1] - edge._aclPoints[0]); + Base::Vector3f n = r.Cross(s); + return n.IsNull(); +} + +bool MeshGeomEdge::IsCollinear(const MeshGeomEdge &edge) const +{ + if (IsParallel(edge)) { + Base::Vector3f r(_aclPoints[1] - _aclPoints[0]); + Base::Vector3f d = edge._aclPoints[0] - _aclPoints[0]; + return d.Cross(r).IsNull(); + } + + return false; +} + bool MeshGeomEdge::IntersectWithEdge (const MeshGeomEdge &edge, Base::Vector3f &res) const { const float eps = 1e-06f; diff --git a/src/Mod/Mesh/App/Core/Elements.h b/src/Mod/Mesh/App/Core/Elements.h index db9fa791ca..a0af29e49c 100644 --- a/src/Mod/Mesh/App/Core/Elements.h +++ b/src/Mod/Mesh/App/Core/Elements.h @@ -198,6 +198,16 @@ public: * Checks if the projection point of \a point lies on the edge. */ bool IsProjectionPointOf(const Base::Vector3f& point) const; + /** + * Checks if the two edges are parallel. + * \note Parallel edges could be collinear. + */ + bool IsParallel(const MeshGeomEdge &edge) const; + /** + * Checks if the two edges are collinear. + * \note Collinear edges always are parallel. + */ + bool IsCollinear(const MeshGeomEdge &edge) const; public: Base::Vector3f _aclPoints[2]; /**< Corner points */ diff --git a/src/Mod/Mesh/App/Core/tritritest.h b/src/Mod/Mesh/App/Core/tritritest.h index 35f34b7536..44d790391f 100644 --- a/src/Mod/Mesh/App/Core/tritritest.h +++ b/src/Mod/Mesh/App/Core/tritritest.h @@ -41,7 +41,7 @@ OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE */ -#include +#include #define FABS(x) ((float)fabs(x)) /* implement as is fastest on your machine */ diff --git a/src/Mod/Mesh/App/Edge.cpp b/src/Mod/Mesh/App/Edge.cpp new file mode 100644 index 0000000000..388b4a69b7 --- /dev/null +++ b/src/Mod/Mesh/App/Edge.cpp @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (c) 2021 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., 51 Franklin Street, * + * Fifth Floor, Boston, MA 02110-1301, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +#endif + +#include "Edge.h" +#include "Mesh.h" + +using namespace Mesh; + +Edge::Edge() + : Index(-1) + , Mesh(nullptr) +{ + for (int i=0; i<2; i++) { + PIndex[i] = MeshCore::POINT_INDEX_MAX; + NIndex[i] = MeshCore::FACET_INDEX_MAX; + } +} + +Edge::Edge(const Edge& e) + : MeshCore::MeshGeomEdge(e) + , Index(e.Index) + , Mesh(e.Mesh) +{ + for (int i=0; i<2; i++) { + PIndex[i] = e.PIndex[i]; + NIndex[i] = e.NIndex[i]; + } +} + +Edge::~Edge() +{ +} + +void Edge::operator = (const Edge& e) +{ + MeshCore::MeshGeomEdge::operator = (e); + Mesh = e.Mesh; + Index = e.Index; + for (int i=0; i<2; i++) { + PIndex[i] = e.PIndex[i]; + NIndex[i] = e.NIndex[i]; + } +} + +void Edge::unbound() +{ + Index = -1; + Mesh = nullptr; +} diff --git a/src/Mod/Mesh/App/Edge.h b/src/Mod/Mesh/App/Edge.h new file mode 100644 index 0000000000..0dda23e542 --- /dev/null +++ b/src/Mod/Mesh/App/Edge.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (c) 2021 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., 51 Franklin Street, * + * Fifth Floor, Boston, MA 02110-1301, USA * + * * + ***************************************************************************/ + + +#ifndef MESH_EDGE_H +#define MESH_EDGE_H + +#include +#include +#include + +#include + +namespace Mesh +{ +// forward declaration +class MeshObject; + +/** The Edge helper class + * The Edge class provides an interface for the EdgePy class for + * convenient access to the Mesh data structure. This class should not be used + * for programming algorithms in C++. Use Mesh Core classes instead! + */ +class MeshExport Edge : public MeshCore::MeshGeomEdge +{ +public: + Edge(); + Edge(const Edge& f); + ~Edge(); + + bool isBound() const {return Index != -1;} + void unbound(); + void operator = (const Edge& f); + + int Index; + MeshCore::PointIndex PIndex[2]; + MeshCore::FacetIndex NIndex[2]; + Base::Reference Mesh; +}; + +} // namespace Mesh + + +#endif // MESH_EDGE_H diff --git a/src/Mod/Mesh/App/EdgePy.xml b/src/Mod/Mesh/App/EdgePy.xml new file mode 100644 index 0000000000..4416925a1d --- /dev/null +++ b/src/Mod/Mesh/App/EdgePy.xml @@ -0,0 +1,90 @@ + + + + + + Edge in a Mesh + Edge in mesh +This is an edge of a facet in a MeshObject. You can get it by e.g. iterating over the facets of a +mesh and calling getEdge(index). + + + + + intersectWithEdge(Edge) -> list +Get a list of intersection points with another edge. + + + + + + isParallel(Edge) -> bool +Checks if the two edges are parallel. + + + + + + isCollinear(Edge) -> bool +Checks if the two edges are collinear. + + + + + + method unbound() +Cut the connection to a MeshObject. The edge becomes +free and is more or less a simple edge. +After calling unbound() no topological operation will +work! + + + + + + The index of this edge of the facet + + + + + + A list of points of the edge + + + + + + The index tuple of point vertices of the mesh this edge is built of + + + + + + The index tuple of neighbour facets of the mesh this edge is adjacent with + + + + + + The length of the edge + + + + + + Bound state of the edge + + + + + diff --git a/src/Mod/Mesh/App/EdgePyImp.cpp b/src/Mod/Mesh/App/EdgePyImp.cpp new file mode 100644 index 0000000000..701595297a --- /dev/null +++ b/src/Mod/Mesh/App/EdgePyImp.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** + * Copyright (c) 2021 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., 51 Franklin Street, * + * Fifth Floor, Boston, MA 02110-1301, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#include "Mesh.h" +#include "Edge.h" +#include +#include + +#include +#include +#include + +using namespace Mesh; + +// returns a string which represent the object e.g. when printed in python +std::string EdgePy::representation() const +{ + EdgePy::PointerType ptr = getEdgePtr(); + std::stringstream str; + str << "Edge ("; + str << "(" << ptr->_aclPoints[0].x << ", " << ptr->_aclPoints[0].y << ", " << ptr->_aclPoints[0].z << ", Idx=" << ptr->PIndex[0] << "), "; + str << "(" << ptr->_aclPoints[1].x << ", " << ptr->_aclPoints[1].y << ", " << ptr->_aclPoints[1].z << ", Idx=" << ptr->PIndex[1] << "), "; + str << "Idx=" << ptr->Index << ", (" << ptr->NIndex[0] << ", " << ptr->NIndex[1] << ")"; + str << ")"; + + return str.str(); +} + +PyObject *EdgePy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper +{ + // create a new instance of EdgePy and the Twin object + return new EdgePy(new Edge); +} + +// constructor method +int EdgePy::PyInit(PyObject* args, PyObject* /*kwds*/) +{ + PyObject* pt1 = nullptr; + PyObject* pt2 = nullptr; + if (!PyArg_ParseTuple(args, "|O!O!", &Base::VectorPy::Type, &pt1 + , &Base::VectorPy::Type, &pt2)) + return -1; + + if (pt1) + getEdgePtr()->_aclPoints[0] = Base::convertTo(Py::Vector(pt1, false).toVector()); + if (pt2) + getEdgePtr()->_aclPoints[1] = Base::convertTo(Py::Vector(pt2, false).toVector()); + return 0; +} + +Py::Long EdgePy::getIndex() const +{ + return Py::Long((long) getEdgePtr()->Index); +} + +PyObject* EdgePy::intersectWithEdge(PyObject *args) +{ + PyObject* object; + if (!PyArg_ParseTuple(args, "O!", &EdgePy::Type, &object)) + return nullptr; + EdgePy *edge = static_cast(object); + EdgePy::PointerType edge_ptr = edge->getEdgePtr(); + EdgePy::PointerType this_ptr = this->getEdgePtr(); + Base::Vector3f p; + bool ok = this_ptr->IntersectWithEdge(*edge_ptr, p); + + try { + Py::List sct; + if (ok) { + Py::Tuple pt(3); + pt.setItem(0, Py::Float(p.x)); + pt.setItem(1, Py::Float(p.y)); + pt.setItem(2, Py::Float(p.z)); + sct.append(pt); + } + return Py::new_reference_to(sct); + } + catch (const Py::Exception&) { + return nullptr; + } +} + +PyObject* EdgePy::isParallel(PyObject *args) +{ + PyObject* object; + if (!PyArg_ParseTuple(args, "O!", &EdgePy::Type, &object)) + return nullptr; + EdgePy *edge = static_cast(object); + EdgePy::PointerType edge_ptr = edge->getEdgePtr(); + EdgePy::PointerType this_ptr = this->getEdgePtr(); + bool ok = this_ptr->IsParallel(*edge_ptr); + return Py::new_reference_to(Py::Boolean(ok)); +} + +PyObject* EdgePy::isCollinear(PyObject *args) +{ + PyObject* object; + if (!PyArg_ParseTuple(args, "O!", &EdgePy::Type, &object)) + return nullptr; + EdgePy *edge = static_cast(object); + EdgePy::PointerType edge_ptr = edge->getEdgePtr(); + EdgePy::PointerType this_ptr = this->getEdgePtr(); + bool ok = this_ptr->IsCollinear(*edge_ptr); + return Py::new_reference_to(Py::Boolean(ok)); +} + +PyObject* EdgePy::unbound(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return nullptr; + getEdgePtr()->unbound(); + Py_Return; +} + +Py::Boolean EdgePy::getBound() const +{ + return Py::Boolean(getEdgePtr()->isBound()); +} + +Py::List EdgePy::getPoints() const +{ + EdgePy::PointerType edge = this->getEdgePtr(); + + Py::List pts; + for (int i=0; i<2; i++) { + Py::Tuple pt(3); + pt.setItem(0, Py::Float(edge->_aclPoints[i].x)); + pt.setItem(1, Py::Float(edge->_aclPoints[i].y)); + pt.setItem(2, Py::Float(edge->_aclPoints[i].z)); + pts.append(pt); + } + + return pts; +} + +Py::Tuple EdgePy::getPointIndices() const +{ + EdgePy::PointerType edge = this->getEdgePtr(); + + Py::Tuple idxTuple(2); + for (int i=0; i<2; i++) { + idxTuple.setItem(i, Py::Long(edge->PIndex[i])); + } + return idxTuple; +} + +Py::Tuple EdgePy::getNeighbourIndices() const +{ + EdgePy::PointerType edge = this->getEdgePtr(); + + Py::Tuple idxTuple(2); + for (int i=0; i<2; i++) { + idxTuple.setItem(i, Py::Long(edge->NIndex[i])); + } + return idxTuple; +} + +Py::Float EdgePy::getLength() const +{ + EdgePy::PointerType edge = this->getEdgePtr(); + return Py::Float(Base::Distance(edge->_aclPoints[0], edge->_aclPoints[1])); +} + +PyObject *EdgePy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + +int EdgePy::setCustomAttributes(const char* /*attr*/, PyObject * /*obj*/) +{ + return 0; +} diff --git a/src/Mod/Mesh/App/Facet.cpp b/src/Mod/Mesh/App/Facet.cpp index 73ff133527..01119c1a63 100644 --- a/src/Mod/Mesh/App/Facet.cpp +++ b/src/Mod/Mesh/App/Facet.cpp @@ -69,3 +69,23 @@ void Facet::operator = (const Facet& f) NIndex[i] = f.NIndex[i]; } } + +Edge Facet::getEdge(int index) const +{ + index = index % 3; + Edge edge; + // geometric coordinates + edge._aclPoints[0] = this->_aclPoints[index]; + edge._aclPoints[1] = this->_aclPoints[(index + 1) % 3]; + + // indices + edge.Index = index; + edge.PIndex[0] = this->PIndex[index]; + edge.PIndex[1] = this->PIndex[(index + 1) % 3]; + edge.NIndex[0] = this->Index; + edge.NIndex[1] = this->NIndex[index]; + edge._bBorder = (this->NIndex[index] == MeshCore::FACET_INDEX_MAX); + + edge.Mesh = this->Mesh; + return edge; +} diff --git a/src/Mod/Mesh/App/Facet.h b/src/Mod/Mesh/App/Facet.h index 9fb8224605..893670fe76 100644 --- a/src/Mod/Mesh/App/Facet.h +++ b/src/Mod/Mesh/App/Facet.h @@ -28,7 +28,7 @@ #include #include -#include "Core/Elements.h" +#include namespace Mesh { @@ -49,6 +49,7 @@ public: bool isBound() const {return Index != MeshCore::FACET_INDEX_MAX;} void operator = (const Facet& f); + Edge getEdge(int) const; MeshCore::FacetIndex Index; MeshCore::PointIndex PIndex[3]; diff --git a/src/Mod/Mesh/App/FacetPy.xml b/src/Mod/Mesh/App/FacetPy.xml index c20f970447..1b1295816e 100644 --- a/src/Mod/Mesh/App/FacetPy.xml +++ b/src/Mod/Mesh/App/FacetPy.xml @@ -56,6 +56,13 @@ The two angles are given in radian. + + + getEdge(int) -> Edge +Returns the edge of the facet. + + + The index of this facet in the MeshObject diff --git a/src/Mod/Mesh/App/FacetPyImp.cpp b/src/Mod/Mesh/App/FacetPyImp.cpp index 4610b17e95..899e7a4828 100644 --- a/src/Mod/Mesh/App/FacetPyImp.cpp +++ b/src/Mod/Mesh/App/FacetPyImp.cpp @@ -27,6 +27,7 @@ #include "Facet.h" #include #include +#include #include #include @@ -78,6 +79,16 @@ PyObject* FacetPy::unbound(PyObject *args) Py_Return; } +PyObject* FacetPy::getEdge(PyObject *args) +{ + int index; + if (!PyArg_ParseTuple(args, "i", &index)) + return nullptr; + + Edge edge = getFacetPtr()->getEdge(index); + return new EdgePy(new Edge(edge)); +} + Py::Long FacetPy::getIndex() const { return Py::Long((long) getFacetPtr()->Index); @@ -85,7 +96,7 @@ Py::Long FacetPy::getIndex() const Py::Boolean FacetPy::getBound() const { - return Py::Boolean(getFacetPtr()->Index != UINT_MAX); + return Py::Boolean(getFacetPtr()->isBound()); } Py::Object FacetPy::getNormal() const diff --git a/src/Mod/Mesh/App/MeshTestsApp.py b/src/Mod/Mesh/App/MeshTestsApp.py index 01712ef519..d467b1e016 100644 --- a/src/Mod/Mesh/App/MeshTestsApp.py +++ b/src/Mod/Mesh/App/MeshTestsApp.py @@ -252,6 +252,65 @@ class MeshGeoTestCases(unittest.TestCase): res = f1.intersect(f2) self.assertTrue(len(res) == 2) + def testIntersectionOfIntersectingEdges(self): + self.planarMesh.append( [0.,10.,10.] ) + self.planarMesh.append( [10.,0.,10.] ) + self.planarMesh.append( [10.,10.,10.] ) + self.planarMesh.append( [6.,8.,10.] ) + self.planarMesh.append( [16.,8.,10.] ) + self.planarMesh.append( [6.,18.,10.] ) + planarMeshObject = Mesh.Mesh(self.planarMesh) + + edge1 = planarMeshObject.Facets[0].getEdge(2) + edge2 = planarMeshObject.Facets[1].getEdge(2) + res = edge1.intersectWithEdge(edge2) + self.assertTrue(len(res) == 1) + self.assertEqual(res[0][0], 6.0) + self.assertEqual(res[0][1], 10.0) + self.assertEqual(res[0][2], 10.0) + + def testIntersectionOfParallelEdges(self): + self.planarMesh.append( [0.,10.,10.] ) + self.planarMesh.append( [10.,0.,10.] ) + self.planarMesh.append( [10.,10.,10.] ) + self.planarMesh.append( [6.,8.,10.] ) + self.planarMesh.append( [16.,8.,10.] ) + self.planarMesh.append( [6.,18.,10.] ) + planarMeshObject = Mesh.Mesh(self.planarMesh) + + edge1 = planarMeshObject.Facets[0].getEdge(2) + edge2 = planarMeshObject.Facets[1].getEdge(0) + res = edge1.intersectWithEdge(edge2) + self.assertTrue(len(res) == 0) + + def testIntersectionOfCollinearEdges(self): + self.planarMesh.append( [0.,0.,0.] ) + self.planarMesh.append( [6.,0.,0.] ) + self.planarMesh.append( [3.,4.,0.] ) + self.planarMesh.append( [7.,0.,0.] ) + self.planarMesh.append( [13.,0.,0.] ) + self.planarMesh.append( [10.,4.,0.] ) + planarMeshObject = Mesh.Mesh(self.planarMesh) + + edge1 = planarMeshObject.Facets[0].getEdge(0) + edge2 = planarMeshObject.Facets[1].getEdge(0) + res = edge1.intersectWithEdge(edge2) + self.assertTrue(len(res) == 0) + + def testIntersectionOfWarpedEdges(self): + self.planarMesh.append( [0.,0.,0.] ) + self.planarMesh.append( [6.,0.,0.] ) + self.planarMesh.append( [3.,4.,0.] ) + self.planarMesh.append( [2.,2.,1.] ) + self.planarMesh.append( [8.,2.,1.] ) + self.planarMesh.append( [5.,6.,1.] ) + planarMeshObject = Mesh.Mesh(self.planarMesh) + + edge1 = planarMeshObject.Facets[0].getEdge(1) + edge2 = planarMeshObject.Facets[1].getEdge(0) + res = edge1.intersectWithEdge(edge2) + self.assertTrue(len(res) == 0) + def testSelfIntersection(self): s = b"""solid Simple facet normal 0.0e0 0.0e0 1.0e1