From 1d2c51d66ff7f0d0178ab8d3304866812d4c190c Mon Sep 17 00:00:00 2001 From: David Osterberg Date: Sat, 13 Feb 2021 17:07:37 +0100 Subject: [PATCH 1/8] Draft: Fix regressions in Draft array --- src/Mod/Draft/draftguitools/gui_orthoarray.py | 2 +- src/Mod/Draft/drafttaskpanels/task_circulararray.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Draft/draftguitools/gui_orthoarray.py b/src/Mod/Draft/draftguitools/gui_orthoarray.py index c85e737d65..22b1ed06df 100644 --- a/src/Mod/Draft/draftguitools/gui_orthoarray.py +++ b/src/Mod/Draft/draftguitools/gui_orthoarray.py @@ -73,7 +73,7 @@ class OrthoArray(gui_base.GuiCommandBase): We add callbacks that connect the 3D view with the widgets of the task panel. """ - _log("GuiCommand: {}".format(_self.command_name)) + _log("GuiCommand: {}".format(self.command_name)) #_msg("{}".format(16*"-")) #_msg("GuiCommand: {}".format(self.command_name)) diff --git a/src/Mod/Draft/drafttaskpanels/task_circulararray.py b/src/Mod/Draft/drafttaskpanels/task_circulararray.py index 6b0d86abf9..f6fc819c38 100644 --- a/src/Mod/Draft/drafttaskpanels/task_circulararray.py +++ b/src/Mod/Draft/drafttaskpanels/task_circulararray.py @@ -297,7 +297,7 @@ class TaskPanelCircularArray: "App.ActiveDocument.recompute()"] # We commit the command list through the parent command - self.source_command.commit(translate("draft","Circular array", _cmd_list)) + self.source_command.commit(translate("draft","Circular array"), _cmd_list) def get_distances(self): """Get the distance parameters from the widgets.""" From fb52f3177dca53130d9e2581cfcc94ad86a79f0b Mon Sep 17 00:00:00 2001 From: luz paz Date: Sat, 13 Feb 2021 22:24:22 -0500 Subject: [PATCH 2/8] Path: fix typos in comments [skip ci] Found via codespell --- src/Mod/Path/PathScripts/PathSlot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSlot.py b/src/Mod/Path/PathScripts/PathSlot.py index 32ea97cf15..d5c40c019d 100644 --- a/src/Mod/Path/PathScripts/PathSlot.py +++ b/src/Mod/Path/PathScripts/PathSlot.py @@ -515,7 +515,7 @@ class ObjectSlot(PathOp.ObjectOp): (p1, p2) = pnts begExt = obj.ExtendPathStart.Value endExt = obj.ExtendPathEnd.Value - # invert endExt, begExt args to apply extentions to correct ends + # invert endExt, begExt args to apply extensions to correct ends # XY geom is postitive CCW; Gcode postitive CW pnts = self._extendArcSlot(p1, p2, self.arcCenter, endExt, begExt) @@ -925,7 +925,7 @@ class ObjectSlot(PathOp.ObjectOp): # Check that all Z values are equal (isRoughly same) if (abs(z1 - z2) > tolrnc or abs(z1 - z3) > tolrnc ): -# abs(z2 - z3) > tolrnc): 3rd test reduntant. +# abs(z2 - z3) > tolrnc): 3rd test redundant. return False return True @@ -1230,8 +1230,8 @@ class ObjectSlot(PathOp.ObjectOp): # Convert extension to radians; make a generic chord ( line ) on XY plane from the x axis - # rotate and shift into place so it has same vertices as the required arc extention - # adjust rotation angle to provide +ve or -ve extention as needed + # rotate and shift into place so it has same vertices as the required arc extension + # adjust rotation angle to provide +ve or -ve extension as needed origin = FreeCAD.Vector(0.0, 0.0, 0.0) if begExt: ExtRadians = abs(begExt / self.newRadius) From 00cce75bd51da1a98ad1abc0a1d17fa24aebf58e Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 14 Feb 2021 11:26:53 +0100 Subject: [PATCH 3/8] Mesh: [skip ci] replace tabs with spaces --- src/Mod/Mesh/App/MeshTestsApp.py | 192 +++++++++++++++---------------- 1 file changed, 96 insertions(+), 96 deletions(-) diff --git a/src/Mod/Mesh/App/MeshTestsApp.py b/src/Mod/Mesh/App/MeshTestsApp.py index f91f0f41eb..a13315508a 100644 --- a/src/Mod/Mesh/App/MeshTestsApp.py +++ b/src/Mod/Mesh/App/MeshTestsApp.py @@ -19,120 +19,120 @@ except Exception: class MeshTopoTestCases(unittest.TestCase): - def setUp(self): - # set up a planar face with 18 triangles - self.planarMesh = [] - for x in range(3): - for y in range(3): - self.planarMesh.append( [0.0 + x, 0.0 + y,0.0000] ) - self.planarMesh.append( [1.0 + x, 1.0 + y,0.0000] ) - self.planarMesh.append( [0.0 + x, 1.0 + y,0.0000] ) - self.planarMesh.append( [0.0 + x, 0.0 + y,0.0000] ) - self.planarMesh.append( [1.0 + x, 0.0 + y,0.0000] ) - self.planarMesh.append( [1.0 + x, 1.0 + y,0.0000] ) + def setUp(self): + # set up a planar face with 18 triangles + self.planarMesh = [] + for x in range(3): + for y in range(3): + self.planarMesh.append( [0.0 + x, 0.0 + y,0.0000] ) + self.planarMesh.append( [1.0 + x, 1.0 + y,0.0000] ) + self.planarMesh.append( [0.0 + x, 1.0 + y,0.0000] ) + self.planarMesh.append( [0.0 + x, 0.0 + y,0.0000] ) + self.planarMesh.append( [1.0 + x, 0.0 + y,0.0000] ) + self.planarMesh.append( [1.0 + x, 1.0 + y,0.0000] ) - def testCollapseFacetsSingle(self): - for i in range(18): - planarMeshObject = Mesh.Mesh(self.planarMesh) - planarMeshObject.collapseFacets([i]) + def testCollapseFacetsSingle(self): + for i in range(18): + planarMeshObject = Mesh.Mesh(self.planarMesh) + planarMeshObject.collapseFacets([i]) - def testCollapseFacetsMultible(self): - planarMeshObject = Mesh.Mesh(self.planarMesh) - planarMeshObject.collapseFacets(range(7)) + def testCollapseFacetsMultible(self): + planarMeshObject = Mesh.Mesh(self.planarMesh) + planarMeshObject.collapseFacets(range(7)) - def testCollapseFacetsAll(self): - planarMeshObject = Mesh.Mesh(self.planarMesh) - planarMeshObject.collapseFacets(range(18)) + def testCollapseFacetsAll(self): + planarMeshObject = Mesh.Mesh(self.planarMesh) + planarMeshObject.collapseFacets(range(18)) class MeshGeoTestCases(unittest.TestCase): - def setUp(self): - # set up a planar face with 2 triangles - self.planarMesh = [] + def setUp(self): + # set up a planar face with 2 triangles + self.planarMesh = [] - def testIntersection(self): - self.planarMesh.append( [0.9961,1.5413,4.3943] ) - self.planarMesh.append( [9.4796,10.024,-3.0937] ) - self.planarMesh.append( [1.4308,11.3841,2.6829] ) - self.planarMesh.append( [2.6493,2.2536,3.0679] ) - self.planarMesh.append( [13.1126,0.4857,-4.4417] ) - self.planarMesh.append( [10.2410,8.9040,-3.5002] ) - planarMeshObject = Mesh.Mesh(self.planarMesh) - f1 = planarMeshObject.Facets[0] - f2 = planarMeshObject.Facets[1] - res=f1.intersect(f2) - self.failUnless(len(res) == 0) + def testIntersection(self): + self.planarMesh.append( [0.9961,1.5413,4.3943] ) + self.planarMesh.append( [9.4796,10.024,-3.0937] ) + self.planarMesh.append( [1.4308,11.3841,2.6829] ) + self.planarMesh.append( [2.6493,2.2536,3.0679] ) + self.planarMesh.append( [13.1126,0.4857,-4.4417] ) + self.planarMesh.append( [10.2410,8.9040,-3.5002] ) + planarMeshObject = Mesh.Mesh(self.planarMesh) + f1 = planarMeshObject.Facets[0] + f2 = planarMeshObject.Facets[1] + res=f1.intersect(f2) + self.failUnless(len(res) == 0) - def testIntersection2(self): - self.planarMesh.append( [-16.097176,-29.891157,15.987688] ) - self.planarMesh.append( [-16.176304,-29.859991,15.947966] ) - self.planarMesh.append( [-16.071451,-29.900553,15.912505] ) - self.planarMesh.append( [-16.092241,-29.893408,16.020439] ) - self.planarMesh.append( [-16.007210,-29.926180,15.967641] ) - self.planarMesh.append( [-16.064457,-29.904951,16.090832] ) - planarMeshObject = Mesh.Mesh(self.planarMesh) - f1 = planarMeshObject.Facets[0] - f2 = planarMeshObject.Facets[1] - # does definitely NOT intersect - res=f1.intersect(f2) - self.failUnless(len(res) == 0) + def testIntersection2(self): + self.planarMesh.append( [-16.097176,-29.891157,15.987688] ) + self.planarMesh.append( [-16.176304,-29.859991,15.947966] ) + self.planarMesh.append( [-16.071451,-29.900553,15.912505] ) + self.planarMesh.append( [-16.092241,-29.893408,16.020439] ) + self.planarMesh.append( [-16.007210,-29.926180,15.967641] ) + self.planarMesh.append( [-16.064457,-29.904951,16.090832] ) + planarMeshObject = Mesh.Mesh(self.planarMesh) + f1 = planarMeshObject.Facets[0] + f2 = planarMeshObject.Facets[1] + # does definitely NOT intersect + res=f1.intersect(f2) + self.failUnless(len(res) == 0) class PivyTestCases(unittest.TestCase): - def setUp(self): - # set up a planar face with 2 triangles - self.planarMesh = [] - FreeCAD.newDocument("MeshTest") + def setUp(self): + # set up a planar face with 2 triangles + self.planarMesh = [] + FreeCAD.newDocument("MeshTest") - def testRayPick(self): - if not FreeCAD.GuiUp: - return - self.planarMesh.append( [-16.097176,-29.891157,15.987688] ) - self.planarMesh.append( [-16.176304,-29.859991,15.947966] ) - self.planarMesh.append( [-16.071451,-29.900553,15.912505] ) - self.planarMesh.append( [-16.092241,-29.893408,16.020439] ) - self.planarMesh.append( [-16.007210,-29.926180,15.967641] ) - self.planarMesh.append( [-16.064457,-29.904951,16.090832] ) - planarMeshObject = Mesh.Mesh(self.planarMesh) + def testRayPick(self): + if not FreeCAD.GuiUp: + return + self.planarMesh.append( [-16.097176,-29.891157,15.987688] ) + self.planarMesh.append( [-16.176304,-29.859991,15.947966] ) + self.planarMesh.append( [-16.071451,-29.900553,15.912505] ) + self.planarMesh.append( [-16.092241,-29.893408,16.020439] ) + self.planarMesh.append( [-16.007210,-29.926180,15.967641] ) + self.planarMesh.append( [-16.064457,-29.904951,16.090832] ) + planarMeshObject = Mesh.Mesh(self.planarMesh) - from pivy import coin; import FreeCADGui - Mesh.show(planarMeshObject) - view=FreeCADGui.ActiveDocument.ActiveView.getViewer() - rp=coin.SoRayPickAction(view.getSoRenderManager().getViewportRegion()) - rp.setRay(coin.SbVec3f(-16.05,16.0,16.0),coin.SbVec3f(0,-1,0)) - rp.apply(view.getSoRenderManager().getSceneGraph()) - pp=rp.getPickedPoint() - self.failUnless(pp != None) - det=pp.getDetail() - self.failUnless(det.getTypeId() == coin.SoFaceDetail.getClassTypeId()) - det=coin.cast(det,str(det.getTypeId().getName())) - self.failUnless(det.getFaceIndex() == 1) + from pivy import coin; import FreeCADGui + Mesh.show(planarMeshObject) + view=FreeCADGui.ActiveDocument.ActiveView.getViewer() + rp=coin.SoRayPickAction(view.getSoRenderManager().getViewportRegion()) + rp.setRay(coin.SbVec3f(-16.05,16.0,16.0),coin.SbVec3f(0,-1,0)) + rp.apply(view.getSoRenderManager().getSceneGraph()) + pp=rp.getPickedPoint() + self.failUnless(pp != None) + det=pp.getDetail() + self.failUnless(det.getTypeId() == coin.SoFaceDetail.getClassTypeId()) + det=coin.cast(det,str(det.getTypeId().getName())) + self.failUnless(det.getFaceIndex() == 1) - def testPrimitiveCount(self): - if not FreeCAD.GuiUp: - return - self.planarMesh.append( [-16.097176,-29.891157,15.987688] ) - self.planarMesh.append( [-16.176304,-29.859991,15.947966] ) - self.planarMesh.append( [-16.071451,-29.900553,15.912505] ) - self.planarMesh.append( [-16.092241,-29.893408,16.020439] ) - self.planarMesh.append( [-16.007210,-29.926180,15.967641] ) - self.planarMesh.append( [-16.064457,-29.904951,16.090832] ) - planarMeshObject = Mesh.Mesh(self.planarMesh) + def testPrimitiveCount(self): + if not FreeCAD.GuiUp: + return + self.planarMesh.append( [-16.097176,-29.891157,15.987688] ) + self.planarMesh.append( [-16.176304,-29.859991,15.947966] ) + self.planarMesh.append( [-16.071451,-29.900553,15.912505] ) + self.planarMesh.append( [-16.092241,-29.893408,16.020439] ) + self.planarMesh.append( [-16.007210,-29.926180,15.967641] ) + self.planarMesh.append( [-16.064457,-29.904951,16.090832] ) + planarMeshObject = Mesh.Mesh(self.planarMesh) - from pivy import coin; import FreeCADGui - Mesh.show(planarMeshObject) - view=FreeCADGui.ActiveDocument.ActiveView - view.setAxisCross(False) - pc=coin.SoGetPrimitiveCountAction() - pc.apply(view.getSceneGraph()) - self.failUnless(pc.getTriangleCount() == 2) - #self.failUnless(pc.getPointCount() == 6) + from pivy import coin; import FreeCADGui + Mesh.show(planarMeshObject) + view=FreeCADGui.ActiveDocument.ActiveView + view.setAxisCross(False) + pc=coin.SoGetPrimitiveCountAction() + pc.apply(view.getSceneGraph()) + self.failUnless(pc.getTriangleCount() == 2) + #self.failUnless(pc.getPointCount() == 6) - def tearDown(self): - #closing doc - FreeCAD.closeDocument("MeshTest") + def tearDown(self): + #closing doc + FreeCAD.closeDocument("MeshTest") # Threads From dac937ae355e6b61c96844f6fe6e20b6b2430464 Mon Sep 17 00:00:00 2001 From: David Osterberg Date: Thu, 11 Feb 2021 15:06:12 +0100 Subject: [PATCH 4/8] Part: Allow helix primitive with negative Angle --- src/Mod/Part/App/PrimitiveFeature.cpp | 4 ++-- src/Mod/Part/Gui/DlgPrimitives.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Mod/Part/App/PrimitiveFeature.cpp b/src/Mod/Part/App/PrimitiveFeature.cpp index 4841f871f6..119078c35f 100644 --- a/src/Mod/Part/App/PrimitiveFeature.cpp +++ b/src/Mod/Part/App/PrimitiveFeature.cpp @@ -74,7 +74,7 @@ namespace Part { - const App::PropertyQuantityConstraint::Constraints apexRange = {0.0,90.0,0.1}; + const App::PropertyQuantityConstraint::Constraints apexRange = {-90.0,90.0,0.1}; const App::PropertyQuantityConstraint::Constraints torusRangeV = {-180.0,180.0,1.0}; const App::PropertyQuantityConstraint::Constraints angleRangeU = {0.0,360.0,1.0}; const App::PropertyQuantityConstraint::Constraints angleRangeV = {-90.0,90.0,1.0}; @@ -801,7 +801,7 @@ Helix::Helix(void) Height.setConstraints(&quantityRange); ADD_PROPERTY_TYPE(Radius,(1.0),"Helix",App::Prop_None,"The radius of the helix"); Radius.setConstraints(&quantityRange); - ADD_PROPERTY_TYPE(Angle,(0.0),"Helix",App::Prop_None,"If angle is > 0 a conical otherwise a cylindircal surface is used"); + ADD_PROPERTY_TYPE(Angle,(0.0),"Helix",App::Prop_None,"If angle is != 0 a conical otherwise a cylindircal surface is used"); Angle.setConstraints(&apexRange); ADD_PROPERTY_TYPE(LocalCoord,(long(0)),"Coordinate System",App::Prop_None,"Orientation of the local coordinate system of the helix"); LocalCoord.setEnums(LocalCSEnums); diff --git a/src/Mod/Part/Gui/DlgPrimitives.cpp b/src/Mod/Part/Gui/DlgPrimitives.cpp index 9fef631f58..f1a410bfbe 100644 --- a/src/Mod/Part/Gui/DlgPrimitives.cpp +++ b/src/Mod/Part/Gui/DlgPrimitives.cpp @@ -261,7 +261,7 @@ DlgPrimitives::DlgPrimitives(QWidget* parent, Part::Primitive* feature) ui->helixPitch->setRange(0, INT_MAX); ui->helixHeight->setRange(0, INT_MAX); ui->helixRadius->setRange(0, INT_MAX); - ui->helixAngle->setRange(0, 90); + ui->helixAngle->setRange(-90, 90); // circle ui->circleRadius->setRange(0, INT_MAX); ui->circleAngle0->setRange(0, 360); @@ -560,7 +560,7 @@ DlgPrimitives::DlgPrimitives(QWidget* parent, Part::Primitive* feature) } } -/* +/* * Destroys the object and frees any allocated resources */ DlgPrimitives::~DlgPrimitives() @@ -1937,7 +1937,7 @@ TaskPrimitives::~TaskPrimitives() } QDialogButtonBox::StandardButtons TaskPrimitives::getStandardButtons() const -{ +{ return QDialogButtonBox::Close| QDialogButtonBox::Ok; } From d941c114cbb5ca994927a4ac6f54a77cee6405e2 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 14 Feb 2021 12:02:45 +0100 Subject: [PATCH 5/8] Mesh: [skip ci] expose some mesh evaluation functions to Python --- src/Mod/Mesh/App/Mesh.cpp | 24 ++++++++++++++++++++++++ src/Mod/Mesh/App/Mesh.h | 4 ++++ src/Mod/Mesh/App/MeshPy.xml | 24 ++++++++++++++++++++++-- src/Mod/Mesh/App/MeshPyImp.cpp | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/Mod/Mesh/App/Mesh.cpp b/src/Mod/Mesh/App/Mesh.cpp index f3347c8b32..26b7666be7 100644 --- a/src/Mod/Mesh/App/Mesh.cpp +++ b/src/Mod/Mesh/App/Mesh.cpp @@ -1497,6 +1497,30 @@ void MeshObject::validateIndices() this->_segments.clear(); } +bool MeshObject::hasInvalidNeighbourhood() const +{ + MeshCore::MeshEvalNeighbourhood eval(_kernel); + return !eval.Evaluate(); +} + +bool MeshObject::hasPointsOutOfRange() const +{ + MeshCore::MeshEvalRangePoint eval(_kernel); + return !eval.Evaluate(); +} + +bool MeshObject::hasFacetsOutOfRange() const +{ + MeshCore::MeshEvalRangeFacet eval(_kernel); + return !eval.Evaluate(); +} + +bool MeshObject::hasCorruptedFacets() const +{ + MeshCore::MeshEvalCorruptedFacets eval(_kernel); + return !eval.Evaluate(); +} + void MeshObject::validateDeformations(float fMaxAngle, float fEps) { unsigned long count = _kernel.CountFacets(); diff --git a/src/Mod/Mesh/App/Mesh.h b/src/Mod/Mesh/App/Mesh.h index bcb2148948..1086ee01a5 100644 --- a/src/Mod/Mesh/App/Mesh.h +++ b/src/Mod/Mesh/App/Mesh.h @@ -283,6 +283,10 @@ public: void removeDuplicatedPoints(); void removeDuplicatedFacets(); bool hasNonManifolds() const; + bool hasInvalidNeighbourhood() const; + bool hasPointsOutOfRange() const; + bool hasFacetsOutOfRange() const; + bool hasCorruptedFacets() const; void removeNonManifolds(); void removeNonManifoldPoints(); bool hasSelfIntersections() const; diff --git a/src/Mod/Mesh/App/MeshPy.xml b/src/Mod/Mesh/App/MeshPy.xml index a43402f289..fdaad6cf92 100644 --- a/src/Mod/Mesh/App/MeshPy.xml +++ b/src/Mod/Mesh/App/MeshPy.xml @@ -229,7 +229,7 @@ for c in mesh.getSeparatecomponents(): Check if the mesh has non-manifolds - + Remove non-manifolds @@ -284,7 +284,27 @@ for c in mesh.getSeparatecomponents(): Remove points with invalid coordinates (NaN) - + + + Check if the mesh has invalid neighbourhood indices + + + + + Check if the mesh has point indices that are out of range + + + + + Check if the mesh has facet indices that are out of range + + + + + Check if the mesh has corrupted facets + + + Get the number of topologic independent areas diff --git a/src/Mod/Mesh/App/MeshPyImp.cpp b/src/Mod/Mesh/App/MeshPyImp.cpp index 394a2ed88e..c984ffe3a8 100644 --- a/src/Mod/Mesh/App/MeshPyImp.cpp +++ b/src/Mod/Mesh/App/MeshPyImp.cpp @@ -972,6 +972,38 @@ PyObject* MeshPy::hasNonManifolds(PyObject *args) return Py_BuildValue("O", (ok ? Py_True : Py_False)); } +PyObject* MeshPy::hasInvalidNeighbourhood(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + bool ok = getMeshObjectPtr()->hasInvalidNeighbourhood(); + return Py_BuildValue("O", (ok ? Py_True : Py_False)); +} + +PyObject* MeshPy::hasPointsOutOfRange(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + bool ok = getMeshObjectPtr()->hasPointsOutOfRange(); + return Py_BuildValue("O", (ok ? Py_True : Py_False)); +} + +PyObject* MeshPy::hasFacetsOutOfRange(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + bool ok = getMeshObjectPtr()->hasFacetsOutOfRange(); + return Py_BuildValue("O", (ok ? Py_True : Py_False)); +} + +PyObject* MeshPy::hasCorruptedFacets(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + bool ok = getMeshObjectPtr()->hasFacetsOutOfRange(); + return Py_BuildValue("O", (ok ? Py_True : Py_False)); +} + PyObject* MeshPy::removeNonManifolds(PyObject *args) { if (!PyArg_ParseTuple(args, "")) From ed873038025277f220dfaa058599a0dfbb435835 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 14 Feb 2021 12:33:29 +0100 Subject: [PATCH 6/8] Mesh: add unit tests for splitting facets --- src/Mod/Mesh/App/MeshTestsApp.py | 104 +++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/src/Mod/Mesh/App/MeshTestsApp.py b/src/Mod/Mesh/App/MeshTestsApp.py index a13315508a..449d91a1f0 100644 --- a/src/Mod/Mesh/App/MeshTestsApp.py +++ b/src/Mod/Mesh/App/MeshTestsApp.py @@ -46,6 +46,110 @@ class MeshTopoTestCases(unittest.TestCase): planarMeshObject.collapseFacets(range(18)) +class MeshSplitTestCases(unittest.TestCase): + def setUp(self): + self.mesh = Mesh.createBox(1.0, 1.0, 1.0) + + def testSplitFacetOnOneEdge(self): + p1 = self.mesh.Points[0].Vector + p2 = self.mesh.Points[1].Vector + p3 = self.mesh.Points[2].Vector + self.mesh.splitFacet(0, p1, (p2 + p3) / 2) + self.assertFalse(self.mesh.hasNonManifolds()) + self.assertFalse(self.mesh.hasInvalidNeighbourhood()) + self.assertFalse(self.mesh.hasPointsOutOfRange()) + self.assertFalse(self.mesh.hasFacetsOutOfRange()) + self.assertFalse(self.mesh.hasCorruptedFacets()) + self.assertTrue(self.mesh.isSolid()) + + def testSplitFacetOnTwoEdges_21(self): + p1 = self.mesh.Points[0].Vector + p2 = self.mesh.Points[1].Vector + p3 = self.mesh.Points[2].Vector + self.mesh.splitFacet(0, (p1 + p3) / 2, (p2 + p3) / 2) + self.assertFalse(self.mesh.hasNonManifolds()) + self.assertFalse(self.mesh.hasInvalidNeighbourhood()) + self.assertFalse(self.mesh.hasPointsOutOfRange()) + self.assertFalse(self.mesh.hasFacetsOutOfRange()) + self.assertFalse(self.mesh.hasCorruptedFacets()) + self.assertTrue(self.mesh.isSolid()) + + def testSplitFacetOnTwoEdges_12(self): + p1 = self.mesh.Points[0].Vector + p2 = self.mesh.Points[1].Vector + p3 = self.mesh.Points[2].Vector + self.mesh.splitFacet(0, (p2 + p3) / 2, (p1 + p3) / 2) + self.assertFalse(self.mesh.hasNonManifolds()) + self.assertFalse(self.mesh.hasInvalidNeighbourhood()) + self.assertFalse(self.mesh.hasPointsOutOfRange()) + self.assertFalse(self.mesh.hasFacetsOutOfRange()) + self.assertFalse(self.mesh.hasCorruptedFacets()) + self.assertTrue(self.mesh.isSolid()) + + def testSplitFacetOnTwoEdges_01(self): + p1 = self.mesh.Points[0].Vector + p2 = self.mesh.Points[1].Vector + p3 = self.mesh.Points[2].Vector + self.mesh.splitFacet(0, (p1 + p2) / 2, (p2 + p3) / 2) + self.assertFalse(self.mesh.hasNonManifolds()) + self.assertFalse(self.mesh.hasInvalidNeighbourhood()) + self.assertFalse(self.mesh.hasPointsOutOfRange()) + self.assertFalse(self.mesh.hasFacetsOutOfRange()) + self.assertFalse(self.mesh.hasCorruptedFacets()) + self.assertTrue(self.mesh.isSolid()) + + def testSplitFacetOnTwoEdges_10(self): + p1 = self.mesh.Points[0].Vector + p2 = self.mesh.Points[1].Vector + p3 = self.mesh.Points[2].Vector + self.mesh.splitFacet(0, (p2 + p3) / 2, (p1 + p2) / 2) + self.assertFalse(self.mesh.hasNonManifolds()) + self.assertFalse(self.mesh.hasInvalidNeighbourhood()) + self.assertFalse(self.mesh.hasPointsOutOfRange()) + self.assertFalse(self.mesh.hasFacetsOutOfRange()) + self.assertFalse(self.mesh.hasCorruptedFacets()) + self.assertTrue(self.mesh.isSolid()) + + def testSplitFacetOnTwoEdges_02(self): + p1 = self.mesh.Points[0].Vector + p2 = self.mesh.Points[1].Vector + p3 = self.mesh.Points[2].Vector + self.mesh.splitFacet(0, (p1 + p2) / 2, (p1 + p3) / 2) + self.assertFalse(self.mesh.hasNonManifolds()) + self.assertFalse(self.mesh.hasInvalidNeighbourhood()) + self.assertFalse(self.mesh.hasPointsOutOfRange()) + self.assertFalse(self.mesh.hasFacetsOutOfRange()) + self.assertFalse(self.mesh.hasCorruptedFacets()) + self.assertTrue(self.mesh.isSolid()) + + def testSplitFacetOnTwoEdges_20(self): + p1 = self.mesh.Points[0].Vector + p2 = self.mesh.Points[1].Vector + p3 = self.mesh.Points[2].Vector + self.mesh.splitFacet(0, (p1 + p3) / 2, (p1 + p2) / 2) + self.assertFalse(self.mesh.hasNonManifolds()) + self.assertFalse(self.mesh.hasInvalidNeighbourhood()) + self.assertFalse(self.mesh.hasPointsOutOfRange()) + self.assertFalse(self.mesh.hasFacetsOutOfRange()) + self.assertFalse(self.mesh.hasCorruptedFacets()) + self.assertTrue(self.mesh.isSolid()) + + def testSplitFacetOnTwoEdges_5teps(self): + Vec3d = FreeCAD.Vector + for i in range(5): + f = self.mesh.Facets[0] + p1 = Vec3d(f.Points[0]) + p2 = Vec3d(f.Points[1]) + p3 = Vec3d(f.Points[2]) + self.mesh.splitFacet(0, (p1 + p3) / 2, (p2 + p3) / 2) + + self.assertFalse(self.mesh.hasNonManifolds()) + self.assertFalse(self.mesh.hasInvalidNeighbourhood()) + self.assertFalse(self.mesh.hasPointsOutOfRange()) + self.assertFalse(self.mesh.hasFacetsOutOfRange()) + self.assertFalse(self.mesh.hasCorruptedFacets()) + self.assertTrue(self.mesh.isSolid()) + class MeshGeoTestCases(unittest.TestCase): def setUp(self): # set up a planar face with 2 triangles From a82a103cb3afcdb6cefb1720df4b1854afbb1725 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 14 Feb 2021 19:03:19 +0100 Subject: [PATCH 7/8] TD: fix undo/redo for dimension objects * do not emit dragFinished() signal inside mousePressEvent as nothing has been done. This avoids to create an empty transaction and doesn't touch the document * inside mouseReleaseEvent() check if the mouse has been moved and only if yes emit the signal * improve error handling in mouseDoubleClickEvent * in mouseDoubleClickEvent start to edit the view provider --- src/Mod/TechDraw/Gui/QGIViewDimension.cpp | 24 ++++++++++++-------- src/Mod/TechDraw/Gui/ViewProviderDimension.h | 4 +--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp index 2bef981208..c23ba6d2ec 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp @@ -36,6 +36,8 @@ # include # include + # include + # include # include # include # include @@ -52,7 +54,6 @@ #include #include #include -#include #include @@ -76,7 +77,6 @@ #include "QGIViewDimension.h" #include "ViewProviderDimension.h" #include "DrawGuiUtil.h" -#include "TaskDimension.h" #define NORMAL 0 #define PRE 1 @@ -149,10 +149,7 @@ void QGIDatumLabel::mousePressEvent(QGraphicsSceneMouseEvent * event) m_ctrl = true; } - if(scene() && this == scene()->mouseGrabberItem()) { - Q_EMIT dragFinished(); - } - QGraphicsItem::mousePressEvent(event); + QGraphicsItem::mousePressEvent(event); } void QGIDatumLabel::mouseMoveEvent(QGraphicsSceneMouseEvent * event) @@ -164,8 +161,11 @@ void QGIDatumLabel::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) { // Base::Console().Message("QGIDL::mouseReleaseEvent()\n"); m_ctrl = false; - if(scene() && this == scene()->mouseGrabberItem()) { - Q_EMIT dragFinished(); + if (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)) + .length() > 0) { + if (scene() && this == scene()->mouseGrabberItem()) { + Q_EMIT dragFinished(); + } } QGraphicsItem::mouseReleaseEvent(event); @@ -175,13 +175,17 @@ void QGIDatumLabel::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) { QGIViewDimension* qgivDimension = dynamic_cast(parentItem()); if (qgivDimension == nullptr) { + qWarning() << "QGIDatumLabel::mouseDoubleClickEvent: No parent item"; return; } - auto ViewProvider = static_cast(qgivDimension->getViewProvider(qgivDimension->getViewObject())); + + auto ViewProvider = dynamic_cast(qgivDimension->getViewProvider(qgivDimension->getViewObject())); if (ViewProvider == nullptr) { + qWarning() << "QGIDatumLabel::mouseDoubleClickEvent: No valid view provider"; return; } - Gui::Control().showDialog(new TaskDlgDimension(qgivDimension, ViewProvider)); + + ViewProvider->startDefaultEditMode(); QGraphicsItem::mouseDoubleClickEvent(event); } diff --git a/src/Mod/TechDraw/Gui/ViewProviderDimension.h b/src/Mod/TechDraw/Gui/ViewProviderDimension.h index 6bc047e1aa..156369a840 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderDimension.h +++ b/src/Mod/TechDraw/Gui/ViewProviderDimension.h @@ -76,6 +76,7 @@ public: virtual bool setEdit(int ModNum); virtual void unsetEdit(int ModNum); virtual bool doubleClicked(void); + void startDefaultEditMode(); virtual TechDraw::DrawViewDimension* getViewObject() const; @@ -89,9 +90,6 @@ public: protected: virtual void handleChangedPropertyType(Base::XMLReader &reader, const char *TypeName, App::Property * prop); -private: - void startDefaultEditMode(); - private: static const char *StandardAndStyleEnums[]; static const char *RenderingExtentEnums[]; From 008e97b929865c29b0b284f6282f07a38e0ba65c Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 14 Feb 2021 19:30:06 +0100 Subject: [PATCH 8/8] TD: fix undo/redo for balloon objects * inside mouseReleaseEvent() check if the mouse has been moved and only if yes emit the dragFinished() signal * improve error handling in mouseDoubleClickEvent * in mouseDoubleClickEvent start to edit the view provider --- src/Mod/TechDraw/Gui/QGIViewBalloon.cpp | 22 ++++++++++++++-------- src/Mod/TechDraw/Gui/ViewProviderBalloon.h | 4 +--- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp b/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp index fb094498ce..e5fee48d14 100644 --- a/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp @@ -31,6 +31,7 @@ # include # include + # include # include # include # include @@ -48,7 +49,6 @@ #include #include #include -#include #include #include @@ -74,7 +74,6 @@ #include "QGIViewDimension.h" #include "QGVPage.h" #include "MDIViewPage.h" -#include "TaskBalloon.h" //TODO: hide the Qt coord system (+y down). @@ -140,8 +139,11 @@ void QGIBalloonLabel::mouseMoveEvent(QGraphicsSceneMouseEvent * event) void QGIBalloonLabel::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) { - if(scene() && this == scene()->mouseGrabberItem()) { - Q_EMIT dragFinished(); + if (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)) + .length() > 0) { + if (scene() && this == scene()->mouseGrabberItem()) { + Q_EMIT dragFinished(); + } } m_ctrl = false; m_drag = false; @@ -152,13 +154,17 @@ void QGIBalloonLabel::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event) { QGIViewBalloon* qgivBalloon = dynamic_cast(parentItem()); if (qgivBalloon == nullptr) { + qWarning() << "QGIBalloonLabel::mouseDoubleClickEvent: No parent item"; return; } - auto ViewProvider = static_cast(qgivBalloon->getViewProvider(qgivBalloon->getViewObject())); + + auto ViewProvider = dynamic_cast(qgivBalloon->getViewProvider(qgivBalloon->getViewObject())); if (ViewProvider == nullptr) { + qWarning() << "QGIBalloonLabel::mouseDoubleClickEvent: No valid view provider"; return; } - Gui::Control().showDialog(new TaskDlgBalloon(qgivBalloon, ViewProvider)); + + ViewProvider->startDefaultEditMode(); QGraphicsItem::mouseDoubleClickEvent(event); } @@ -482,7 +488,7 @@ void QGIViewBalloon::balloonLabelDragged(bool ctrl) m_saveOffset = dvb->getOriginOffset(); } } - + double scale = 1.0; DrawView* balloonParent = getSourceView(); if (balloonParent != nullptr) { @@ -499,7 +505,7 @@ void QGIViewBalloon::balloonLabelDragged(bool ctrl) if (ctrl) { Base::Vector3d pos(x, -y, 0.0); Base::Vector3d newOrg = pos - m_saveOffset; - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.OriginX = %f", + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.OriginX = %f", dvb->getNameInDocument(), newOrg.x); Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.OriginY = %f", dvb->getNameInDocument(), newOrg.y); diff --git a/src/Mod/TechDraw/Gui/ViewProviderBalloon.h b/src/Mod/TechDraw/Gui/ViewProviderBalloon.h index 3dcc7580fb..9dcef1b50f 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderBalloon.h +++ b/src/Mod/TechDraw/Gui/ViewProviderBalloon.h @@ -63,14 +63,12 @@ public: virtual void unsetEdit(int ModNum); virtual bool doubleClicked(void); virtual bool canDelete(App::DocumentObject* obj) const; + void startDefaultEditMode(); virtual TechDraw::DrawViewBalloon* getViewObject() const; protected: virtual void handleChangedPropertyType(Base::XMLReader &reader, const char *TypeName, App::Property * prop); - -private: - void startDefaultEditMode(); }; } // namespace TechDrawGui