From 1db692e38bf7919861971da26039df35fdfd8192 Mon Sep 17 00:00:00 2001 From: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Fri, 2 Jul 2021 11:32:51 +0200 Subject: [PATCH 01/41] Draft: Draft_Trimex: Fix wrong angle units in taskpanel The redraw function was changed to return a list [dist, ang] if the real argument is not True. With this list the action function 'knows' which units to display. This also made it possible to move the "self.ui." related stuff that was in the redraw function to the the action function. I have also changed to tooltip texts by removing the article "The". This more in keeping with other tooltips. --- src/Mod/Draft/draftguitools/gui_trimex.py | 30 ++++++++++++++--------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Mod/Draft/draftguitools/gui_trimex.py b/src/Mod/Draft/draftguitools/gui_trimex.py index 85fe2c6799..65f97a3212 100644 --- a/src/Mod/Draft/draftguitools/gui_trimex.py +++ b/src/Mod/Draft/draftguitools/gui_trimex.py @@ -203,9 +203,21 @@ class Trimex(gui_base_original.Modifier): if self.extrudeMode: dist = self.extrude(self.shift) else: - dist = self.redraw(self.point, self.snapped, - self.shift, self.alt) - self.ui.setRadiusValue(dist, unit="Length") + # If the geomType of the edge is "Line" ang will be None, + # else dist will be None. + dist, ang = self.redraw(self.point, self.snapped, + self.shift, self.alt) + + if dist: + self.ui.labelRadius.setText(translate("draft", "Distance")) + self.ui.radiusValue.setToolTip(translate("draft", + "Offset distance")) + self.ui.setRadiusValue(dist, unit="Length") + else: + self.ui.labelRadius.setText(translate("draft", "Angle")) + self.ui.radiusValue.setToolTip(translate("draft", + "Offset angle")) + self.ui.setRadiusValue(ang, unit="Angle") self.ui.radiusValue.setFocus() self.ui.radiusValue.selectAll() gui_tool_utils.redraw3DView() @@ -301,6 +313,7 @@ class Trimex(gui_base_original.Modifier): # modifying active edge if DraftGeomUtils.geomType(edge) == "Line": + ang = None ve = DraftGeomUtils.vec(edge) chord = v1.sub(point) n = ve.cross(chord) @@ -313,9 +326,6 @@ class Trimex(gui_base_original.Modifier): dist = v1.sub(self.newpoint).Length ghost.p1(self.newpoint) ghost.p2(v2) - self.ui.labelRadius.setText(translate("draft", "Distance")) - self.ui.radiusValue.setToolTip(translate("draft", - "The offset distance")) if real: if self.force: ray = self.newpoint.sub(v1) @@ -323,16 +333,14 @@ class Trimex(gui_base_original.Modifier): self.newpoint = App.Vector.add(v1, ray) newedges.append(Part.LineSegment(self.newpoint, v2).toShape()) else: + dist = None center = edge.Curve.Center rad = edge.Curve.Radius ang1 = DraftVecUtils.angle(v2.sub(center)) ang2 = DraftVecUtils.angle(point.sub(center)) _rot_rad = DraftVecUtils.rotate(App.Vector(rad, 0, 0), -ang2) self.newpoint = App.Vector.add(center, _rot_rad) - self.ui.labelRadius.setText(translate("draft", "Angle")) - self.ui.radiusValue.setToolTip(translate("draft", - "The offset angle")) - dist = math.degrees(-ang2) + ang = math.degrees(-ang2) # if ang1 > ang2: # ang1, ang2 = ang2, ang1 # print("last calculated:", @@ -384,7 +392,7 @@ class Trimex(gui_base_original.Modifier): if real: return newedges else: - return dist + return [dist, ang] def trimObject(self): """Trim the actual object.""" From 4946203267ef78c8200e2510975f2bede4772a03 Mon Sep 17 00:00:00 2001 From: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Fri, 2 Jul 2021 11:37:31 +0200 Subject: [PATCH 02/41] Draft: Draft_Trimex: Fix wrong angle units in taskpanel: tooltip in DraftGui.py I have changed the tooltip from "Trim distance" to "Offset distance" to match the tooltip in gui_trimex.py --- src/Mod/Draft/DraftGui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index a3aa93284d..2e254f123b 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -1095,7 +1095,7 @@ class DraftToolBar: self.taskUi(title, icon="Draft_Trimex") self.radiusUi() self.labelRadius.setText(translate("draft","Distance")) - self.radiusValue.setToolTip(translate("draft", "Trim distance")) + self.radiusValue.setToolTip(translate("draft", "Offset distance")) self.radiusValue.setText(FreeCAD.Units.Quantity(0,FreeCAD.Units.Length).UserString) todo.delay(self.radiusValue.setFocus,None) self.radiusValue.selectAll() From 99b218c63def131c420c0b653f3a79282679caac Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Wed, 26 May 2021 23:02:57 -0500 Subject: [PATCH 03/41] Path: Prioritize `self.parent` and `self.obj` declarations This prioritization and conversion of `self.parent` declaration allows for removal of the `setParent()` method. --- src/Mod/Path/PathScripts/PathOpGui.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathOpGui.py b/src/Mod/Path/PathScripts/PathOpGui.py index 6fcdf86e54..9d9ef3d4dd 100644 --- a/src/Mod/Path/PathScripts/PathOpGui.py +++ b/src/Mod/Path/PathScripts/PathOpGui.py @@ -214,11 +214,6 @@ class TaskPanelPage(object): def _installTCUpdate(self): return hasattr(self.form, 'toolController') - def setParent(self, parent): - '''setParent() ... used to transfer parent object link to child class. - Do not overwrite.''' - self.parent = parent - def onDirtyChanged(self, callback): '''onDirtyChanged(callback) ... set callback when dirty state changes.''' self.signalDirtyChanged = callback @@ -1000,8 +995,10 @@ class TaskPanel(object): def __init__(self, obj, deleteOnReject, opPage, selectionFactory): PathLog.track(obj.Label, deleteOnReject, opPage, selectionFactory) FreeCAD.ActiveDocument.openTransaction(translate("Path", "AreaOp Operation")) + self.obj = obj self.deleteOnReject = deleteOnReject self.featurePages = [] + self.parent = None # members initialized later self.clearanceHeight = None @@ -1050,9 +1047,9 @@ class TaskPanel(object): self.featurePages.append(opPage) for page in self.featurePages: + page.parent = self # save pointer to this current class as "parent" page.initPage(obj) page.onDirtyChanged(self.pageDirtyChanged) - page.setParent(self) taskPanelLayout = PathPreferences.defaultTaskPanelLayout() @@ -1092,7 +1089,6 @@ class TaskPanel(object): self.form = forms self.selectionFactory = selectionFactory - self.obj = obj self.isdirty = deleteOnReject self.visibility = obj.ViewObject.Visibility obj.ViewObject.Visibility = True From 495a4e610ab5fe6ae4cb967c14fc0061e03515ff Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Wed, 26 May 2021 23:03:43 -0500 Subject: [PATCH 04/41] Path: Fix readonly lockout bug with blank expression --- src/Mod/Path/PathScripts/PathGui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathGui.py b/src/Mod/Path/PathScripts/PathGui.py index fb37725bf3..3280a93d02 100644 --- a/src/Mod/Path/PathScripts/PathGui.py +++ b/src/Mod/Path/PathScripts/PathGui.py @@ -68,10 +68,10 @@ def updateInputField(obj, prop, widget, onBeforeChange=None): isDiff = True break if noExpr: - widget.setProperty('readonly', False) + widget.setReadOnly(False) widget.setStyleSheet("color: black") else: - widget.setProperty('readonly', True) + widget.setReadOnly(True) widget.setStyleSheet("color: gray") widget.update() From 527e168be275e0f14212cf90e29fcda1dc503684 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Wed, 26 May 2021 23:09:36 -0500 Subject: [PATCH 05/41] Path: Fix bug with inaccurate Task Panel values upon new op creation Path: Fix bug with inaccurate Task Panel values upon new op creation --- src/Mod/Path/PathScripts/PathGui.py | 14 +++++++++++++- src/Mod/Path/PathScripts/PathOpGui.py | 11 +++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Mod/Path/PathScripts/PathGui.py b/src/Mod/Path/PathScripts/PathGui.py index 3280a93d02..1d0d58f358 100644 --- a/src/Mod/Path/PathScripts/PathGui.py +++ b/src/Mod/Path/PathScripts/PathGui.py @@ -100,6 +100,7 @@ class QuantitySpinBox: self.widget = widget self.onBeforeChange = onBeforeChange self.prop = None + self.obj = obj self.attachTo(obj, prop) def attachTo(self, obj, prop = None): @@ -139,9 +140,14 @@ class QuantitySpinBox: If no value is provided the value of the bound property is used. quantity can be of type Quantity or Float.''' PathLog.track(self.prop, self.valid) + if self.valid: + expr = self._hasExpression() if quantity is None: - quantity = PathUtil.getProperty(self.obj, self.prop) + if expr: + quantity = FreeCAD.Units.Quantity(self.obj.evalExpression(expr)) + else: + quantity = PathUtil.getProperty(self.obj, self.prop) value = quantity.Value if hasattr(quantity, 'Value') else quantity self.widget.setProperty('rawValue', value) @@ -151,3 +157,9 @@ class QuantitySpinBox: if self.valid: return updateInputField(self.obj, self.prop, self.widget, self.onBeforeChange) return None + + def _hasExpression(self): + for (prop, exp) in self.obj.ExpressionEngine: + if prop == self.prop: + return exp + return None diff --git a/src/Mod/Path/PathScripts/PathOpGui.py b/src/Mod/Path/PathScripts/PathOpGui.py index 9d9ef3d4dd..33de983c24 100644 --- a/src/Mod/Path/PathScripts/PathOpGui.py +++ b/src/Mod/Path/PathScripts/PathOpGui.py @@ -1203,7 +1203,18 @@ class TaskPanel(object): page.clearBase() page.addBaseGeometry(sel) + # Update properties based upon expressions in case expression value has changed + for (prp, expr) in self.obj.ExpressionEngine: + val = FreeCAD.Units.Quantity(self.obj.evalExpression(expr)) + value = val.Value if hasattr(val, 'Value') else val + prop = getattr(self.obj, prp) + if hasattr(prop, "Value"): + prop.Value = value + else: + prop = value + self.panelSetFields() + for page in self.featurePages: page.pageRegisterSignalHandlers() From d008303ae78d0973309ea54f5f18b502031131a7 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Thu, 27 May 2021 20:20:47 -0500 Subject: [PATCH 06/41] Path: Code cleanup, remove duplication, fix `obj.removalshape` shape --- src/Mod/Path/PathScripts/PathPocketShape.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Mod/Path/PathScripts/PathPocketShape.py b/src/Mod/Path/PathScripts/PathPocketShape.py index 902fb21c44..a35326b9be 100644 --- a/src/Mod/Path/PathScripts/PathPocketShape.py +++ b/src/Mod/Path/PathScripts/PathPocketShape.py @@ -84,6 +84,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return shapes representing the solids to be removed.''' PathLog.track() + self.removalshapes = [] # self.isDebug = True if PathLog.getLevel(PathLog.thisModule()) == 4 else False self.removalshapes = [] @@ -162,7 +163,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket): # shape.tessellate(0.05) # originally 0.1 if self.removalshapes: - obj.removalshape = self.removalshapes[0][0] + obj.removalshape = Part.makeCompound([tup[0] for tup in self.removalshapes]) + return self.removalshapes # Support methods From a021f5d9337f9bb70cb2dd950b3cfe6119d6de31 Mon Sep 17 00:00:00 2001 From: Benjamin Alterauge <5332429+ageeye@users.noreply.github.com> Date: Mon, 2 Aug 2021 21:29:17 +0200 Subject: [PATCH 07/41] Possible to use DefaultShapeVertexColor setting. Add a separate material for vertex. So, it is possible use DefaultShapeVertexColor. [Ticket #4692](https://forum.freecadweb.org/viewtopic.php?f=3&t=59576) --- src/Gui/ViewParams.h | 1 + src/Mod/Part/Gui/ViewProviderExt.cpp | 42 +++++++++++++++++++--------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/Gui/ViewParams.h b/src/Gui/ViewParams.h index cee80eb4fa..6dc897cff0 100644 --- a/src/Gui/ViewParams.h +++ b/src/Gui/ViewParams.h @@ -54,6 +54,7 @@ public: FC_VIEW_PARAM(MarkerSize,int,Int,9) \ FC_VIEW_PARAM(DefaultLinkColor,unsigned long,Unsigned,0x66FFFF00) \ FC_VIEW_PARAM(DefaultShapeLineColor,unsigned long,Unsigned,421075455UL) \ + FC_VIEW_PARAM(DefaultShapeVertexColor,unsigned long,Unsigned,421075455UL) \ FC_VIEW_PARAM(DefaultShapeColor,unsigned long,Unsigned,0xCCCCCC00) \ FC_VIEW_PARAM(DefaultShapeLineWidth,int,Int,2) \ FC_VIEW_PARAM(DefaultShapePointSize,int,Int,2) \ diff --git a/src/Mod/Part/Gui/ViewProviderExt.cpp b/src/Mod/Part/Gui/ViewProviderExt.cpp index 5dcfd97135..ba197f1811 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.cpp +++ b/src/Mod/Part/Gui/ViewProviderExt.cpp @@ -236,11 +236,18 @@ ViewProviderPartExt::ViewProviderPartExt() forceUpdateCount = 0; NormalsFromUV = true; + // get default line color unsigned long lcol = Gui::ViewParams::instance()->getDefaultShapeLineColor(); // dark grey (25,25,25) - float r,g,b; - r = ((lcol >> 24) & 0xff) / 255.0; g = ((lcol >> 16) & 0xff) / 255.0; b = ((lcol >> 8) & 0xff) / 255.0; + float lr,lg,lb; + lr = ((lcol >> 24) & 0xff) / 255.0; lg = ((lcol >> 16) & 0xff) / 255.0; lb = ((lcol >> 8) & 0xff) / 255.0; + // get default vertex color + unsigned long vcol = Gui::ViewParams::instance()->getDefaultShapeVertexColor(); + float vr,vg,vb; + vr = ((vcol >> 24) & 0xff) / 255.0; vg = ((vcol >> 16) & 0xff) / 255.0; vb = ((vcol >> 8) & 0xff) / 255.0; int lwidth = Gui::ViewParams::instance()->getDefaultShapeLineWidth(); int psize = Gui::ViewParams::instance()->getDefaultShapePointSize(); + + ParameterGrp::handle hPart = App::GetApplication().GetParameterGroupByPath ("User parameter:BaseApp/Preferences/Mod/Part"); @@ -256,17 +263,26 @@ ViewProviderPartExt::ViewProviderPartExt() static const char *osgroup = "Object Style"; - App::Material mat; - mat.ambientColor.set(0.2f,0.2f,0.2f); - mat.diffuseColor.set(r,g,b); - mat.specularColor.set(0.0f,0.0f,0.0f); - mat.emissiveColor.set(0.0f,0.0f,0.0f); - mat.shininess = 1.0f; - mat.transparency = 0.0f; - ADD_PROPERTY_TYPE(LineMaterial,(mat), osgroup, App::Prop_None, "Object line material."); - ADD_PROPERTY_TYPE(PointMaterial,(mat), osgroup, App::Prop_None, "Object point material."); - ADD_PROPERTY_TYPE(LineColor, (mat.diffuseColor), osgroup, App::Prop_None, "Set object line color."); - ADD_PROPERTY_TYPE(PointColor, (mat.diffuseColor), osgroup, App::Prop_None, "Set object point color"); + App::Material lmat; + lmat.ambientColor.set(0.2f,0.2f,0.2f); + lmat.diffuseColor.set(lr,lg,lb); + lmat.specularColor.set(0.0f,0.0f,0.0f); + lmat.emissiveColor.set(0.0f,0.0f,0.0f); + lmat.shininess = 1.0f; + lmat.transparency = 0.0f; + + App::Material vmat; + vmat.ambientColor.set(0.2f,0.2f,0.2f); + vmat.diffuseColor.set(vr,vg,vb); + vmat.specularColor.set(0.0f,0.0f,0.0f); + vmat.emissiveColor.set(0.0f,0.0f,0.0f); + vmat.shininess = 1.0f; + vmat.transparency = 0.0f; + + ADD_PROPERTY_TYPE(LineMaterial,(lmat), osgroup, App::Prop_None, "Object line material."); + ADD_PROPERTY_TYPE(PointMaterial,(vmat), osgroup, App::Prop_None, "Object point material."); + ADD_PROPERTY_TYPE(LineColor, (lmat.diffuseColor), osgroup, App::Prop_None, "Set object line color."); + ADD_PROPERTY_TYPE(PointColor, (vmat.diffuseColor), osgroup, App::Prop_None, "Set object point color"); ADD_PROPERTY_TYPE(PointColorArray, (PointColor.getValue()), osgroup, App::Prop_None, "Object point color array."); ADD_PROPERTY_TYPE(DiffuseColor,(ShapeColor.getValue()), osgroup, App::Prop_None, "Object diffuse color."); ADD_PROPERTY_TYPE(LineColorArray,(LineColor.getValue()), osgroup, App::Prop_None, "Object line color array."); From ed1d71e671e9e6d65d133f8d922c1dab65cc3ebc Mon Sep 17 00:00:00 2001 From: Benjamin Alterauge <5332429+ageeye@users.noreply.github.com> Date: Mon, 2 Aug 2021 21:29:17 +0200 Subject: [PATCH 08/41] Possible to use DefaultShapeVertexColor setting. Add a separate material for vertex. So, it is possible use DefaultShapeVertexColor. [Ticket #4692](https://forum.freecadweb.org/viewtopic.php?f=3&t=59576) --- src/Gui/ViewParams.h | 1 + src/Mod/Part/Gui/ViewProviderExt.cpp | 42 +++++++++++++++++++--------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/Gui/ViewParams.h b/src/Gui/ViewParams.h index cee80eb4fa..6dc897cff0 100644 --- a/src/Gui/ViewParams.h +++ b/src/Gui/ViewParams.h @@ -54,6 +54,7 @@ public: FC_VIEW_PARAM(MarkerSize,int,Int,9) \ FC_VIEW_PARAM(DefaultLinkColor,unsigned long,Unsigned,0x66FFFF00) \ FC_VIEW_PARAM(DefaultShapeLineColor,unsigned long,Unsigned,421075455UL) \ + FC_VIEW_PARAM(DefaultShapeVertexColor,unsigned long,Unsigned,421075455UL) \ FC_VIEW_PARAM(DefaultShapeColor,unsigned long,Unsigned,0xCCCCCC00) \ FC_VIEW_PARAM(DefaultShapeLineWidth,int,Int,2) \ FC_VIEW_PARAM(DefaultShapePointSize,int,Int,2) \ diff --git a/src/Mod/Part/Gui/ViewProviderExt.cpp b/src/Mod/Part/Gui/ViewProviderExt.cpp index 5dcfd97135..ba197f1811 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.cpp +++ b/src/Mod/Part/Gui/ViewProviderExt.cpp @@ -236,11 +236,18 @@ ViewProviderPartExt::ViewProviderPartExt() forceUpdateCount = 0; NormalsFromUV = true; + // get default line color unsigned long lcol = Gui::ViewParams::instance()->getDefaultShapeLineColor(); // dark grey (25,25,25) - float r,g,b; - r = ((lcol >> 24) & 0xff) / 255.0; g = ((lcol >> 16) & 0xff) / 255.0; b = ((lcol >> 8) & 0xff) / 255.0; + float lr,lg,lb; + lr = ((lcol >> 24) & 0xff) / 255.0; lg = ((lcol >> 16) & 0xff) / 255.0; lb = ((lcol >> 8) & 0xff) / 255.0; + // get default vertex color + unsigned long vcol = Gui::ViewParams::instance()->getDefaultShapeVertexColor(); + float vr,vg,vb; + vr = ((vcol >> 24) & 0xff) / 255.0; vg = ((vcol >> 16) & 0xff) / 255.0; vb = ((vcol >> 8) & 0xff) / 255.0; int lwidth = Gui::ViewParams::instance()->getDefaultShapeLineWidth(); int psize = Gui::ViewParams::instance()->getDefaultShapePointSize(); + + ParameterGrp::handle hPart = App::GetApplication().GetParameterGroupByPath ("User parameter:BaseApp/Preferences/Mod/Part"); @@ -256,17 +263,26 @@ ViewProviderPartExt::ViewProviderPartExt() static const char *osgroup = "Object Style"; - App::Material mat; - mat.ambientColor.set(0.2f,0.2f,0.2f); - mat.diffuseColor.set(r,g,b); - mat.specularColor.set(0.0f,0.0f,0.0f); - mat.emissiveColor.set(0.0f,0.0f,0.0f); - mat.shininess = 1.0f; - mat.transparency = 0.0f; - ADD_PROPERTY_TYPE(LineMaterial,(mat), osgroup, App::Prop_None, "Object line material."); - ADD_PROPERTY_TYPE(PointMaterial,(mat), osgroup, App::Prop_None, "Object point material."); - ADD_PROPERTY_TYPE(LineColor, (mat.diffuseColor), osgroup, App::Prop_None, "Set object line color."); - ADD_PROPERTY_TYPE(PointColor, (mat.diffuseColor), osgroup, App::Prop_None, "Set object point color"); + App::Material lmat; + lmat.ambientColor.set(0.2f,0.2f,0.2f); + lmat.diffuseColor.set(lr,lg,lb); + lmat.specularColor.set(0.0f,0.0f,0.0f); + lmat.emissiveColor.set(0.0f,0.0f,0.0f); + lmat.shininess = 1.0f; + lmat.transparency = 0.0f; + + App::Material vmat; + vmat.ambientColor.set(0.2f,0.2f,0.2f); + vmat.diffuseColor.set(vr,vg,vb); + vmat.specularColor.set(0.0f,0.0f,0.0f); + vmat.emissiveColor.set(0.0f,0.0f,0.0f); + vmat.shininess = 1.0f; + vmat.transparency = 0.0f; + + ADD_PROPERTY_TYPE(LineMaterial,(lmat), osgroup, App::Prop_None, "Object line material."); + ADD_PROPERTY_TYPE(PointMaterial,(vmat), osgroup, App::Prop_None, "Object point material."); + ADD_PROPERTY_TYPE(LineColor, (lmat.diffuseColor), osgroup, App::Prop_None, "Set object line color."); + ADD_PROPERTY_TYPE(PointColor, (vmat.diffuseColor), osgroup, App::Prop_None, "Set object point color"); ADD_PROPERTY_TYPE(PointColorArray, (PointColor.getValue()), osgroup, App::Prop_None, "Object point color array."); ADD_PROPERTY_TYPE(DiffuseColor,(ShapeColor.getValue()), osgroup, App::Prop_None, "Object diffuse color."); ADD_PROPERTY_TYPE(LineColorArray,(LineColor.getValue()), osgroup, App::Prop_None, "Object line color array."); From b287c8604cad902c1a7d5af8551ecbb710e75c39 Mon Sep 17 00:00:00 2001 From: paul lee Date: Fri, 6 Aug 2021 01:26:46 +0800 Subject: [PATCH 09/41] [ArchWall] Fix MakeBlocks to work if Base is Sketch Forum Discussion - https://forum.freecadweb.org/viewtopic.php?f=23&t=60771 --- src/Mod/Arch/ArchWall.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index 4e4a37ef04..7cb5ffd639 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -850,7 +850,14 @@ class _Wall(ArchComponent.Component): offset = obj.OffsetFirst.Value else: offset = obj.OffsetSecond.Value - for edge in self.basewires[0].Edges: + # only 1 wire (first) is supported + if obj.Base.isDerivedFrom("Sketcher::SketchObject"): + # if obj.Base is Sketch, self.baseWires[0] returned is already a list of edge + baseEdges = self.basewires[0] + else: + # otherwise, it is wire + baseEdges = self.basewires[0].Edges + for edge in baseEdges: while offset < (edge.Length-obj.Joint.Value): #print i," Edge ",edge," : ",edge.Length," - ",offset if offset: From 4dbd2c36daa475c278178d17c0b8893ecb0dd1a3 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Fri, 6 Aug 2021 07:29:02 +0200 Subject: [PATCH 10/41] FEM: do not use the red error print if it is not a error --- src/Mod/Fem/femmesh/meshtools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femmesh/meshtools.py b/src/Mod/Fem/femmesh/meshtools.py index ac4a7aca22..3f04ec7c0c 100644 --- a/src/Mod/Fem/femmesh/meshtools.py +++ b/src/Mod/Fem/femmesh/meshtools.py @@ -2225,7 +2225,7 @@ def get_femmesh_eletype( if not femmesh: FreeCAD.Console.PrintError("Error: No femmesh.\n") if not femelement_table: - FreeCAD.Console.PrintError("The femelement_table need to be calculated.\n") + FreeCAD.Console.PrintWarning("The femelement_table need to be calculated.\n") femelement_table = get_femelement_table(femmesh) # in some cases lowest key in femelement_table is not [1] for elem in sorted(femelement_table): From afd885330a2f7eaa406f7415549a4e63890fbb0e Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Fri, 6 Aug 2021 07:38:51 +0200 Subject: [PATCH 11/41] FEM: z88 writer, code formatting --- src/Mod/Fem/femsolver/z88/writer.py | 77 +++++++++++++++++++---------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/src/Mod/Fem/femsolver/z88/writer.py b/src/Mod/Fem/femsolver/z88/writer.py index 1c796fe728..289434c08d 100644 --- a/src/Mod/Fem/femsolver/z88/writer.py +++ b/src/Mod/Fem/femsolver/z88/writer.py @@ -29,6 +29,7 @@ __url__ = "https://www.freecadweb.org" # @{ import time +from os.path import join import FreeCAD @@ -54,23 +55,26 @@ class FemInputWriterZ88(writerbase.FemInputWriter): member, dir_name ) - from os.path import join self.file_name = join(self.dir_name, "z88") - FreeCAD.Console.PrintLog( - "FemInputWriterZ88 --> self.dir_name --> " + self.dir_name + "\n" - ) - FreeCAD.Console.PrintMessage( - "FemInputWriterZ88 --> self.file_name --> " + self.file_name + "\n" - ) + # ******************************************************************************************** + # write solver input def write_solver_input(self): timestart = time.process_time() - FreeCAD.Console.PrintMessage("Write z88 input files to: {}\n".format(self.dir_name)) - if not self.femnodes_mesh: - self.femnodes_mesh = self.femmesh.Nodes - if not self.femelement_table: - self.femelement_table = meshtools.get_femelement_table(self.femmesh) - self.element_count = len(self.femelement_table) + FreeCAD.Console.PrintMessage("\n") # because of time print in separate line + FreeCAD.Console.PrintMessage("Z88 solver input writing...\n") + FreeCAD.Console.PrintLog( + "FemInputWriterZ88 --> self.dir_name --> {}\n" + .format(self.dir_name) + ) + FreeCAD.Console.PrintMessage( + "FemInputWriterZ88 --> self.file_name --> {}\n" + .format(self.file_name) + ) + FreeCAD.Console.PrintMessage( + "Write z88 input files to: {}\n" + .format(self.dir_name) + ) control = self.set_z88_elparam() if control is False: return None @@ -86,9 +90,11 @@ class FemInputWriterZ88(writerbase.FemInputWriter): "Writing time input file: {} seconds" .format(round((time.process_time() - timestart), 2)) ) - FreeCAD.Console.PrintMessage(writing_time_string + " \n\n") + FreeCAD.Console.PrintMessage( + "{}\n\n".format(writing_time_string)) return self.dir_name + # ******************************************************************************************** def set_z88_elparam(self): # TODO: param should be moved to the solver object like the known analysis z8804 = {"INTORD": "0", "INTOS": "0", "IHFLAG": "0", "ISFLAG": "1"} # seg2 --> stab4 @@ -114,7 +120,13 @@ class FemInputWriterZ88(writerbase.FemInputWriter): FreeCAD.Console.PrintMessage("\n") return True + # ******************************************************************************************** def write_z88_mesh(self): + if not self.femnodes_mesh: + self.femnodes_mesh = self.femmesh.Nodes + if not self.femelement_table: + self.femelement_table = meshtools.get_femelement_table(self.femmesh) + self.element_count = len(self.femelement_table) mesh_file_path = self.file_name + "i1.txt" f = open(mesh_file_path, "w") importZ88Mesh.write_z88_mesh_to_file( @@ -125,6 +137,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter): ) f.close() + # ******************************************************************************************** def write_z88_constraints(self): constraints_data = [] # will be a list of tuple for better sorting @@ -134,9 +147,9 @@ class FemInputWriterZ88(writerbase.FemInputWriter): # write nodes to constraints_data (different from writing to file in ccxInpWriter for femobj in self.fixed_objects: for n in femobj["Nodes"]: - constraints_data.append((n, str(n) + " 1 2 0\n")) - constraints_data.append((n, str(n) + " 2 2 0\n")) - constraints_data.append((n, str(n) + " 3 2 0\n")) + constraints_data.append((n, "{} 1 2 0\n".format(n))) + constraints_data.append((n, "{} 2 2 0\n".format(n))) + constraints_data.append((n, "{} 3 2 0\n".format(n))) # forces constraints # check shape type of reference shape and get node loads @@ -151,13 +164,13 @@ class FemInputWriterZ88(writerbase.FemInputWriter): node_load = ref_shape[1][n] if (direction_vec.x != 0.0): v1 = direction_vec.x * node_load - constraints_data.append((n, str(n) + " 1 1 " + str(v1) + "\n")) + constraints_data.append((n, "{} 1 1 {}\n".format(n, v1))) if (direction_vec.y != 0.0): v2 = direction_vec.y * node_load - constraints_data.append((n, str(n) + " 2 1 " + str(v2) + "\n")) + constraints_data.append((n, "{} 2 1 {}\n".format(n, v2))) if (direction_vec.z != 0.0): v3 = direction_vec.z * node_load - constraints_data.append((n, str(n) + " 3 1 " + str(v3) + "\n")) + constraints_data.append((n, "{} 3 1 {}\n".format(n, v3))) # write constraints_data to file constraints_file_path = self.file_name + "i2.txt" @@ -167,6 +180,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter): f.write(c[1]) f.close() + # ******************************************************************************************** def write_z88_face_loads(self): # not yet supported face_load_file_path = self.file_name + "i5.txt" @@ -175,16 +189,17 @@ class FemInputWriterZ88(writerbase.FemInputWriter): f.write("\n") f.close() + # ******************************************************************************************** def write_z88_materials(self): mat_obj = self.material_objects[0]["Object"] material_data_file_name = "51.txt" materials_file_path = self.file_name + "mat.txt" fms = open(materials_file_path, "w") fms.write("1\n") - fms.write("1 " + str(self.element_count) + " " + material_data_file_name) + fms.write("1 {} {}".format(self.element_count, material_data_file_name)) fms.write("\n") fms.close() - material_data_file_path = self.dir_name + "/" + material_data_file_name + material_data_file_path = join(self.dir_name, material_data_file_name) fmd = open(material_data_file_path, "w") YM = FreeCAD.Units.Quantity(mat_obj.Material["YoungsModulus"]) YM_in_MPa = YM.getValueAs("MPa") @@ -193,6 +208,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter): fmd.write("\n") fmd.close() + # ******************************************************************************************** def write_z88_elements_properties(self): element_properties_file_path = self.file_name + "elp.txt" elements_data = [] @@ -225,12 +241,16 @@ class FemInputWriterZ88(writerbase.FemInputWriter): ) elif meshtools.is_face_femmesh(self.femmesh): thick_obj = self.shellthickness_objects[0]["Object"] - thickness = str(thick_obj.Thickness.getValueAs("mm")) + thickness = thick_obj.Thickness.getValueAs("mm").Value elements_data.append( - "1 " + str(self.element_count) + " " + thickness + " 0 0 0 0 0 0 " + "1 {} {} 0 0 0 0 0 0 " + .format(self.element_count, thickness) ) elif meshtools.is_solid_femmesh(self.femmesh): - elements_data.append("1 " + str(self.element_count) + " 0 0 0 0 0 0 0") + elements_data.append( + "1 {} 0 0 0 0 0 0 0" + .format(self.element_count) + ) else: FreeCAD.Console.PrintError("Error!\n") f = open(element_properties_file_path, "w") @@ -240,6 +260,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter): f.write("\n") f.close() + # ******************************************************************************************** def write_z88_integration_properties(self): integration_data = [] integration_data.append("1 {} {} {}".format( @@ -249,12 +270,13 @@ class FemInputWriterZ88(writerbase.FemInputWriter): )) integration_properties_file_path = self.file_name + "int.txt" f = open(integration_properties_file_path, "w") - f.write(str(len(integration_data)) + "\n") + f.write("{}\n".format(len(integration_data))) for i in integration_data: f.write(i) f.write("\n") f.close() + # ******************************************************************************************** def write_z88_solver_parameter(self): global z88_man_template z88_man_template = z88_man_template.replace( @@ -268,13 +290,14 @@ class FemInputWriterZ88(writerbase.FemInputWriter): f.write(z88_man_template) f.close() + # ******************************************************************************************** def write_z88_memory_parameter(self): # self.z88_param_maxgs = 6000000 self.z88_param_maxgs = 50000000 # vierkantrohr global z88_dyn_template z88_dyn_template = z88_dyn_template.replace( "$z88_param_maxgs", - str(self.z88_param_maxgs) + "{}".format(self.z88_param_maxgs) ) solver_parameter_file_path = self.file_name + ".dyn" f = open(solver_parameter_file_path, "w") From 6dbe713d7bf2d2482bb87010ba6df881af80c1f2 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Fri, 6 Aug 2021 09:20:09 +0200 Subject: [PATCH 12/41] FEM: tasks module for z88, mystran and calculix, code improvements --- src/Mod/Fem/femsolver/calculix/tasks.py | 58 +++++++++++++++---------- src/Mod/Fem/femsolver/mystran/tasks.py | 52 +++++++++------------- src/Mod/Fem/femsolver/z88/tasks.py | 49 ++++++++++++++------- 3 files changed, 89 insertions(+), 70 deletions(-) diff --git a/src/Mod/Fem/femsolver/calculix/tasks.py b/src/Mod/Fem/femsolver/calculix/tasks.py index a6c6c1018f..6cce64a458 100644 --- a/src/Mod/Fem/femsolver/calculix/tasks.py +++ b/src/Mod/Fem/femsolver/calculix/tasks.py @@ -51,7 +51,7 @@ _inputFileName = None class Check(run.Check): def run(self): - self.pushStatus("Checking analysis...\n") + self.pushStatus("Checking analysis member...\n") self.check_mesh_exists() # workaround use Calculix ccxtools pre checks @@ -73,13 +73,12 @@ class Prepare(run.Prepare): def run(self): global _inputFileName - self.pushStatus("Preparing input files...\n") - - mesh_obj = membertools.get_mesh_to_solve(self.analysis)[0] # pre check done already + self.pushStatus("Preparing input...\n") # get mesh set data # TODO evaluate if it makes sense to add new task # between check and prepare to the solver frame work + mesh_obj = membertools.get_mesh_to_solve(self.analysis)[0] # pre check done already meshdatagetter = meshsetsgetter.MeshSetsGetter( self.analysis, self.solver, @@ -88,7 +87,7 @@ class Prepare(run.Prepare): ) meshdatagetter.get_mesh_sets() - # write input file + # write solver input w = writer.FemInputWriterCcx( self.analysis, self.solver, @@ -100,9 +99,9 @@ class Prepare(run.Prepare): path = w.write_solver_input() # report to user if task succeeded if path != "" and os.path.isfile(path): - self.pushStatus("Write completed.") + self.pushStatus("Writing solver input completed.") else: - self.pushStatus("Writing CalculiX solver input file failed,") + self.pushStatus("Writing solver input failed.") self.fail() _inputFileName = os.path.splitext(os.path.basename(path))[0] @@ -112,7 +111,13 @@ class Solve(run.Solve): def run(self): self.pushStatus("Executing solver...\n") + # get solver binary + self.pushStatus("Get solver binary...\n") binary = settings.get_binary("Calculix") + if binary is None: + self.fail() # a print has been made in settings module + + # run solver self._process = subprocess.Popen( [binary, "-i", _inputFileName], cwd=self.directory, @@ -131,10 +136,6 @@ class Solve(run.Solve): class Results(run.Results): def run(self): - if not _inputFileName: - # TODO do not run solver - # do not try to read results in a smarter way than an Exception - raise Exception("Error on writing CalculiX input file.\n") prefs = FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Mod/Fem/General") if not prefs.GetBool("KeepResultsOnReRun", False): @@ -142,10 +143,12 @@ class Results(run.Results): self.load_results() def purge_results(self): - - # dat file will not be removed - # results from other solvers will be removed too - # the user should decide if purge should only delete the solver results or all results + self.pushStatus("Purge existing results...\n") + # TODO dat file will not be removed + # TODO implement a generic purge method + # TODO results from other solvers will be removed too + # the user should decide if purge should only + # delete this solver results or results from all solvers for m in membertools.get_member(self.analysis, "Fem::FemResultObject"): if m.Mesh and femutils.is_of_type(m.Mesh, "Fem::MeshResult"): self.analysis.Document.removeObject(m.Mesh.Name) @@ -153,10 +156,11 @@ class Results(run.Results): self.analysis.Document.recompute() def load_results(self): - self.load_results_ccxfrd() - self.load_results_ccxdat() + self.pushStatus("Import new results...\n") + self.load_ccxfrd_results() + self.load_ccxdat_results() - def load_results_ccxfrd(self): + def load_ccxfrd_results(self): frd_result_file = os.path.join( self.directory, _inputFileName + ".frd") if os.path.isfile(frd_result_file): @@ -164,18 +168,26 @@ class Results(run.Results): importCcxFrdResults.importFrd( frd_result_file, self.analysis, result_name_prefix) else: - raise Exception( - "FEM: No results found at {}!".format(frd_result_file)) + # TODO: use solver framework status message system + FreeCAD.Console.PrintError( + "FEM: No results found at {}!\n" + .format(frd_result_file) + ) + self.fail() - def load_results_ccxdat(self): + def load_ccxdat_results(self): dat_result_file = os.path.join( self.directory, _inputFileName + ".dat") if os.path.isfile(dat_result_file): mode_frequencies = importCcxDatResults.import_dat( dat_result_file, self.analysis) else: - raise Exception( - "FEM: No .dat results found at {}!".format(dat_result_file)) + # TODO: use solver framework status message system + FreeCAD.Console.PrintError( + "FEM: No results found at {}!\n" + .format(dat_result_file) + ) + self.fail() if mode_frequencies: for m in membertools.get_member(self.analysis, "Fem::FemResultObject"): if m.Eigenmode > 0: diff --git a/src/Mod/Fem/femsolver/mystran/tasks.py b/src/Mod/Fem/femsolver/mystran/tasks.py index 72c2560bd0..b1e0ce0f51 100644 --- a/src/Mod/Fem/femsolver/mystran/tasks.py +++ b/src/Mod/Fem/femsolver/mystran/tasks.py @@ -57,7 +57,7 @@ _inputFileName = None class Check(run.Check): def run(self): - self.pushStatus("Checking analysis...\n") + self.pushStatus("Checking analysis member...\n") self.check_mesh_exists() self.check_material_exists() self.check_material_single() # no multiple material @@ -70,13 +70,11 @@ class Prepare(run.Prepare): def run(self): global _inputFileName - self.pushStatus("Preparing input files...\n") - - mesh_obj = membertools.get_mesh_to_solve(self.analysis)[0] # pre check done already + self.pushStatus("Preparing solver input...\n") # get mesh set data - # TODO evaluate if it makes sense to add new task - # between check and prepare to the solver frame work + # TODO see calculix tasks get mesh set data + mesh_obj = membertools.get_mesh_to_solve(self.analysis)[0] # pre check done already meshdatagetter = meshsetsgetter.MeshSetsGetter( self.analysis, self.solver, @@ -85,44 +83,36 @@ class Prepare(run.Prepare): ) meshdatagetter.get_mesh_sets() - # write input file + # write solver input w = writer.FemInputWriterMystran( self.analysis, self.solver, mesh_obj, meshdatagetter.member, self.directory, - meshdatagetter.mat_geo_sets ) path = w.write_solver_input() # report to user if task succeeded if path != "": - self.pushStatus("Write completed!") + self.pushStatus("Writing solver input completed.") else: - self.pushStatus("Writing CalculiX input file failed!") + self.pushStatus("Writing solver input failed.") + self.fail() _inputFileName = os.path.splitext(os.path.basename(path))[0] class Solve(run.Solve): def run(self): - # print(_inputFileName) - if not _inputFileName: - # TODO do not run solver, do not try to read results in a smarter way than an Exception - raise Exception("Error on writing Mystran input file.\n") + self.pushStatus("Executing solver...\n") + infile = _inputFileName + ".bdf" - # TODO use solver framework status system - FreeCAD.Console.PrintMessage("Mystran: solver input file: {} \n\n".format(infile)) - - # get binary - self.pushStatus("Get solver...\n") + # get solver binary + self.pushStatus("Get solver binary...\n") binary = settings.get_binary("Mystran") - # use preferences editor to add a group Mystran and the prefs: - # "UseStandardMystranLocation" --> bool, set to False - # "mystranBinaryPath, string" --> the binary path if binary is None: - return # a print has been made in settings module + self.fail() # a print has been made in settings module # run solver self.pushStatus("Executing solver...\n") @@ -147,19 +137,19 @@ class Results(run.Results): if not prefs.GetBool("KeepResultsOnReRun", False): self.purge_results() if result_reading is True: - self.load_results() # ToDo in all solvers generischer name + self.load_results() def purge_results(self): + self.pushStatus("Purge existing results...\n") + # TODO see calculix result tasks for m in membertools.get_member(self.analysis, "Fem::FemResultObject"): if femutils.is_of_type(m.Mesh, "Fem::MeshResult"): self.analysis.Document.removeObject(m.Mesh.Name) self.analysis.Document.removeObject(m.Name) self.analysis.Document.recompute() - # deletes all results from any solver - # TODO: delete only the mystran results, fix in all solver def load_results(self): - self.pushStatus("Import results...\n") + self.pushStatus("Import new results...\n") neu_result_file = os.path.join(self.directory, _inputFileName + ".NEU") if os.path.isfile(neu_result_file): hfcMystranNeuIn.import_neu(neu_result_file) @@ -169,11 +159,11 @@ class Results(run.Results): self.analysis.addObject(o) break else: - # TODO: use solver framework error and status message system + # TODO: use solver framework status message system FreeCAD.Console.PrintError( - "FEM: No results found at {}!\n".format(neu_result_file) + "FEM: No results found at {}!\n" + .format(neu_result_file) ) - return - + self.fail() ## @} diff --git a/src/Mod/Fem/femsolver/z88/tasks.py b/src/Mod/Fem/femsolver/z88/tasks.py index 6a626fe662..023e1a6eda 100644 --- a/src/Mod/Fem/femsolver/z88/tasks.py +++ b/src/Mod/Fem/femsolver/z88/tasks.py @@ -45,7 +45,7 @@ from femtools import membertools class Check(run.Check): def run(self): - self.pushStatus("Checking analysis...\n") + self.pushStatus("Checking analysis member...\n") self.check_mesh_exists() self.check_material_exists() self.check_material_single() # no multiple material @@ -57,7 +57,7 @@ class Check(run.Check): class Prepare(run.Prepare): def run(self): - self.pushStatus("Preparing input files...\n") + self.pushStatus("Preparing solver input...\n") w = writer.FemInputWriterZ88( self.analysis, self.solver, @@ -68,33 +68,45 @@ class Prepare(run.Prepare): path = w.write_solver_input() # report to user if task succeeded if path is not None: - self.pushStatus("Write completed!") + self.pushStatus("Writing solver input completed.") else: - self.pushStatus("Writing Z88 solver input files failed!") + self.pushStatus("Writing solver input failed.") self.fail() # print(path) + # z88 does not pass a main input file to the solver + # it passes the directory all input files are in + # not _inputFileName is needed class Solve(run.Solve): def run(self): - # AFAIK: z88r needs to be run twice, once in test mode and once in real solve mode - # the subprocess was just copied, it seems to work :-) - # TODO: search out for "Vektor GS" and "Vektor KOI" and print values - # may be compared with the used ones self.pushStatus("Executing test solver...\n") + + # get solver binary + self.pushStatus("Get solver binary...\n") binary = settings.get_binary("Z88") + if binary is None: + self.fail() # a print has been made in settings module + + # run solver test mode + # AFAIK: z88r needs to be run twice + # once in test mode and once in real solve mode + # the subprocess was just copied, it works :-) + # TODO: search out for "Vektor GS" and "Vektor KOI" and print values + # may be compare with the used ones + self.pushStatus("Executing solver in test mode...\n") self._process = subprocess.Popen( [binary, "-t", "-choly"], cwd=self.directory, stdout=subprocess.PIPE, stderr=subprocess.PIPE) self.signalAbort.add(self._process.terminate) - # output = self._observeSolver(self._process) self._process.communicate() self.signalAbort.remove(self._process.terminate) - self.pushStatus("Executing real solver...\n") + # run solver real mode + self.pushStatus("Executing solver in real mode...\n") binary = settings.get_binary("Z88") self._process = subprocess.Popen( [binary, "-c", "-choly"], @@ -102,12 +114,10 @@ class Solve(run.Solve): stdout=subprocess.PIPE, stderr=subprocess.PIPE) self.signalAbort.add(self._process.terminate) - # output = self._observeSolver(self._process) self._process.communicate() self.signalAbort.remove(self._process.terminate) - # if not self.aborted: - # self._updateOutput(output) - # del output # get flake8 quiet + + # for chatching the output see CalculiX or Elmer solver tasks module class Results(run.Results): @@ -120,6 +130,8 @@ class Results(run.Results): self.load_results() def purge_results(self): + self.pushStatus("Purge existing results...\n") + # TODO see calculix result tasks for m in membertools.get_member(self.analysis, "Fem::FemResultObject"): if femutils.is_of_type(m.Mesh, "Fem::MeshResult"): self.analysis.Document.removeObject(m.Mesh.Name) @@ -127,6 +139,7 @@ class Results(run.Results): self.analysis.Document.recompute() def load_results(self): + self.pushStatus("Import new results...\n") # displacements from z88o2 file disp_result_file = os.path.join( self.directory, "z88o2.txt") @@ -135,7 +148,11 @@ class Results(run.Results): importZ88O2Results.import_z88_disp( disp_result_file, self.analysis, result_name_prefix) else: - raise Exception( - "FEM: No results found at {}!".format(disp_result_file)) + # TODO: use solver framework status message system + FreeCAD.Console.PrintError( + "FEM: No results found at {}!\n" + .format(disp_result_file) + ) + self.fail() ## @} From ec3b79aa9d2484f4e22ef66f65b317c3ade6564a Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Fri, 6 Aug 2021 09:29:14 +0200 Subject: [PATCH 13/41] FEM: z88 tasks and writer, make use of mesh set data getter class --- src/Mod/Fem/femsolver/z88/tasks.py | 18 ++++++++++++++++-- src/Mod/Fem/femsolver/z88/writer.py | 4 ---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Mod/Fem/femsolver/z88/tasks.py b/src/Mod/Fem/femsolver/z88/tasks.py index 023e1a6eda..6568b5c4a8 100644 --- a/src/Mod/Fem/femsolver/z88/tasks.py +++ b/src/Mod/Fem/femsolver/z88/tasks.py @@ -38,6 +38,7 @@ from . import writer from .. import run from .. import settings from feminout import importZ88O2Results +from femmesh import meshsetsgetter from femtools import femutils from femtools import membertools @@ -58,11 +59,24 @@ class Prepare(run.Prepare): def run(self): self.pushStatus("Preparing solver input...\n") + + # get mesh set data + # TODO see calculix tasks get mesh set data + mesh_obj = membertools.get_mesh_to_solve(self.analysis)[0] # pre check done already + meshdatagetter = meshsetsgetter.MeshSetsGetter( + self.analysis, + self.solver, + mesh_obj, + membertools.AnalysisMember(self.analysis), + ) + meshdatagetter.get_mesh_sets() + + # write solver input w = writer.FemInputWriterZ88( self.analysis, self.solver, - membertools.get_mesh_to_solve(self.analysis)[0], # pre check has been done already - membertools.AnalysisMember(self.analysis), + mesh_obj, + meshdatagetter.member, self.directory ) path = w.write_solver_input() diff --git a/src/Mod/Fem/femsolver/z88/writer.py b/src/Mod/Fem/femsolver/z88/writer.py index 289434c08d..21412d4b25 100644 --- a/src/Mod/Fem/femsolver/z88/writer.py +++ b/src/Mod/Fem/femsolver/z88/writer.py @@ -142,8 +142,6 @@ class FemInputWriterZ88(writerbase.FemInputWriter): constraints_data = [] # will be a list of tuple for better sorting # fixed constraints - # get nodes - self.get_constraints_fixed_nodes() # write nodes to constraints_data (different from writing to file in ccxInpWriter for femobj in self.fixed_objects: for n in femobj["Nodes"]: @@ -152,8 +150,6 @@ class FemInputWriterZ88(writerbase.FemInputWriter): constraints_data.append((n, "{} 3 2 0\n".format(n))) # forces constraints - # check shape type of reference shape and get node loads - self.get_constraints_force_nodeloads() # write node loads to constraints_data # a bit different from writing to file for ccxInpWriter for femobj in self.force_objects: From dff0537fab018b53db588bf16791ba78822e9a24 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Fri, 6 Aug 2021 09:53:06 +0200 Subject: [PATCH 14/41] FEM: z88 and mystran writer, get rid of deprecated member assignments --- src/Mod/Fem/femsolver/mystran/add_con_fixed.py | 2 +- src/Mod/Fem/femsolver/mystran/add_con_force.py | 2 +- src/Mod/Fem/femsolver/z88/writer.py | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Mod/Fem/femsolver/mystran/add_con_fixed.py b/src/Mod/Fem/femsolver/mystran/add_con_fixed.py index 81be959d58..605c330d39 100644 --- a/src/Mod/Fem/femsolver/mystran/add_con_fixed.py +++ b/src/Mod/Fem/femsolver/mystran/add_con_fixed.py @@ -35,7 +35,7 @@ def add_con_fixed(f, model, mystran_writer): # spc1 card spc_ids = [] fixed_code = "# spc1 card, Defines a set of single-point constraints\n" - for i, femobj in enumerate(mystran_writer.fixed_objects): + for i, femobj in enumerate(mystran_writer.member.cons_fixed): conid = i + 2 # 1 will be the conid of the spcadd card spc_ids.append(conid) diff --git a/src/Mod/Fem/femsolver/mystran/add_con_force.py b/src/Mod/Fem/femsolver/mystran/add_con_force.py index db40356c04..152d7572cc 100644 --- a/src/Mod/Fem/femsolver/mystran/add_con_force.py +++ b/src/Mod/Fem/femsolver/mystran/add_con_force.py @@ -36,7 +36,7 @@ def add_con_force(f, model, mystran_writer): scale_factors = [] load_ids = [] force_code = "# force cards, mesh node loads\n" - for i, femobj in enumerate(mystran_writer.force_objects): + for i, femobj in enumerate(mystran_writer.member.cons_force): sid = i + 2 # 1 will be the id of the load card scale_factors.append(1.0) diff --git a/src/Mod/Fem/femsolver/z88/writer.py b/src/Mod/Fem/femsolver/z88/writer.py index 21412d4b25..1824e26c88 100644 --- a/src/Mod/Fem/femsolver/z88/writer.py +++ b/src/Mod/Fem/femsolver/z88/writer.py @@ -143,7 +143,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter): # fixed constraints # write nodes to constraints_data (different from writing to file in ccxInpWriter - for femobj in self.fixed_objects: + for femobj in self.member.cons_fixed: for n in femobj["Nodes"]: constraints_data.append((n, "{} 1 2 0\n".format(n))) constraints_data.append((n, "{} 2 2 0\n".format(n))) @@ -152,7 +152,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter): # forces constraints # write node loads to constraints_data # a bit different from writing to file for ccxInpWriter - for femobj in self.force_objects: + for femobj in self.member.cons_force: # femobj --> dict, FreeCAD document object is femobj["Object"] direction_vec = femobj["Object"].DirectionVector for ref_shape in femobj["NodeLoadTable"]: @@ -187,7 +187,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter): # ******************************************************************************************** def write_z88_materials(self): - mat_obj = self.material_objects[0]["Object"] + mat_obj = self.member.mats_linear[0]["Object"] material_data_file_name = "51.txt" materials_file_path = self.file_name + "mat.txt" fms = open(materials_file_path, "w") @@ -209,7 +209,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter): element_properties_file_path = self.file_name + "elp.txt" elements_data = [] if meshtools.is_edge_femmesh(self.femmesh): - beam_obj = self.beamsection_objects[0]["Object"] + beam_obj = self.member.geos_beamsection[0]["Object"] area = 0 if beam_obj.SectionType == "Rectangular": width = beam_obj.RectWidth.getValueAs("mm").Value @@ -236,7 +236,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter): "Be aware, only trusses are supported for edge meshes!\n" ) elif meshtools.is_face_femmesh(self.femmesh): - thick_obj = self.shellthickness_objects[0]["Object"] + thick_obj = self.member.geos_shellthickness[0]["Object"] thickness = thick_obj.Thickness.getValueAs("mm").Value elements_data.append( "1 {} {} 0 0 0 0 0 0 " From aff41725508852d8dbcbbbbe441a9e8a5ef39b53 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Fri, 6 Aug 2021 19:32:48 +0200 Subject: [PATCH 15/41] FEM: solver framework member checks, extend them --- src/Mod/Fem/femsolver/run.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/Mod/Fem/femsolver/run.py b/src/Mod/Fem/femsolver/run.py index 99fa849a70..5d35dccb0f 100644 --- a/src/Mod/Fem/femsolver/run.py +++ b/src/Mod/Fem/femsolver/run.py @@ -450,7 +450,15 @@ class Check(BaseTask): def check_material_single(self): objs = self.get_several_member("App::MaterialObjectPython") if len(objs) > 1: - self.report.error("Only one Material allowed for this solver.") + self.report.error("Only one Material is supported for this solver.") + self.fail() + return False + return True + + def check_geos_beamsection_no(self): + objs = self.get_several_member("Fem::ElementGeometry1D") + if len(objs) > 0: + self.report.error("Beamsections are not supported for this solver.") self.fail() return False return True @@ -458,7 +466,15 @@ class Check(BaseTask): def check_geos_beamsection_single(self): objs = self.get_several_member("Fem::ElementGeometry1D") if len(objs) > 1: - self.report.error("Only one beamsection allowed for this solver.") + self.report.error("Only one beamsection is supported for this solver.") + self.fail() + return False + return True + + def check_geos_shellthickness_no(self): + objs = self.get_several_member("Fem::ElementGeometry2D") + if len(objs) > 0: + self.report.error("Shellsections are not supported for this solver.") self.fail() return False return True @@ -466,7 +482,7 @@ class Check(BaseTask): def check_geos_shellthickness_single(self): objs = self.get_several_member("Fem::ElementGeometry2D") if len(objs) > 1: - self.report.error("Only one shellthickness allowed for this solver.") + self.report.error("Only one shellthickness is supported for this solver.") self.fail() return False return True @@ -476,8 +492,8 @@ class Check(BaseTask): shellth_obj = self.get_several_member("Fem::ElementGeometry2D") if len(beamsec_obj) > 0 and len(shellth_obj) > 0: self.report.error( - "Either beamsection or shellthickness objects are allowed for this solver, " - "but not both in one analysis." + "Either beamsection or shellthickness objects are " + "supported for this solver, but not both in one analysis." ) self.fail() return False From 1f38d89cc45cdc77e08c0e5b19910cf2c8ee5bb4 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Mon, 9 Aug 2021 15:01:14 +0200 Subject: [PATCH 16/41] FEM: material task panel, improve handling of input fields without unit --- src/Mod/Fem/femtaskpanels/task_material_common.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Mod/Fem/femtaskpanels/task_material_common.py b/src/Mod/Fem/femtaskpanels/task_material_common.py index e4cac0bf6b..b25b376fb3 100644 --- a/src/Mod/Fem/femtaskpanels/task_material_common.py +++ b/src/Mod/Fem/femtaskpanels/task_material_common.py @@ -545,8 +545,10 @@ class _TaskPanel: old_value = Units.Quantity(self.material[matProperty]).getValueAs(qUnit) else: # for example PoissonRatio - value = float(inputfield_text) - old_value = float(self.material[matProperty]) + value = Units.Quantity(inputfield_text).Value + old_value = Units.Quantity(self.material[matProperty]).Value + # value = float(inputfield_text) # this fails on locale with komma + # https://forum.freecadweb.org/viewtopic.php?f=18&t=56912&p=523313#p523313 if value: if not (1 - variation < float(old_value) / value < 1 + variation): material = self.material From d635f03a60a7ae1e6f9d390b9cbd7bdb91692996 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Mon, 9 Aug 2021 15:32:00 +0200 Subject: [PATCH 17/41] FEM: solver task panel, get rid off error message --- src/Mod/Fem/femsolver/solver_taskpanel.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mod/Fem/femsolver/solver_taskpanel.py b/src/Mod/Fem/femsolver/solver_taskpanel.py index 84f60437b2..a399ab843d 100644 --- a/src/Mod/Fem/femsolver/solver_taskpanel.py +++ b/src/Mod/Fem/femsolver/solver_taskpanel.py @@ -76,9 +76,9 @@ class ControlTaskPanel(QtCore.QObject): # as soon as the widget of the task panel gets destroyed. self.form.destroyed.connect(self._disconnectMachine) self.form.destroyed.connect(self._timer.stop) - self.form.destroyed.connect( - lambda: self.machineStatusChanged.disconnect( - self.form.appendStatus)) + # self.form.destroyed.connect( + # lambda: self.machineStatusChanged.disconnect( + # self.form.appendStatus)) # Connect all proxy signals. self.machineStarted.connect(self._timer.start) From 4e6a09be3747d65e0f33826a0688cd2f7cab43cd Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Mon, 9 Aug 2021 21:21:10 +0200 Subject: [PATCH 18/41] FEM: examples, add ccxtools solver --- src/Mod/Fem/femexamples/boxanalysis_frequency.py | 2 +- src/Mod/Fem/femexamples/boxanalysis_static.py | 2 +- src/Mod/Fem/femexamples/buckling_lateraltorsionalbuckling.py | 2 +- src/Mod/Fem/femexamples/buckling_platebuckling.py | 2 +- src/Mod/Fem/femexamples/ccx_buckling_flexuralbuckling.py | 2 +- src/Mod/Fem/femexamples/ccx_cantilever_beam_circle.py | 2 +- src/Mod/Fem/femexamples/ccx_cantilever_beam_pipe.py | 2 +- src/Mod/Fem/femexamples/ccx_cantilever_beam_rect.py | 2 +- src/Mod/Fem/femexamples/ccx_cantilever_ele_hexa20.py | 2 +- src/Mod/Fem/femexamples/ccx_cantilever_ele_quad4.py | 2 +- src/Mod/Fem/femexamples/ccx_cantilever_ele_quad8.py | 2 +- src/Mod/Fem/femexamples/ccx_cantilever_ele_seg2.py | 2 +- src/Mod/Fem/femexamples/ccx_cantilever_ele_seg3.py | 2 +- src/Mod/Fem/femexamples/ccx_cantilever_ele_tetra4.py | 2 +- src/Mod/Fem/femexamples/ccx_cantilever_ele_tria3.py | 2 +- src/Mod/Fem/femexamples/ccx_cantilever_ele_tria6.py | 2 +- src/Mod/Fem/femexamples/ccx_cantilever_faceload.py | 2 +- src/Mod/Fem/femexamples/ccx_cantilever_nodeload.py | 2 +- .../Fem/femexamples/ccx_cantilever_prescribeddisplacement.py | 2 +- src/Mod/Fem/femexamples/constraint_centrif.py | 2 +- src/Mod/Fem/femexamples/constraint_contact_shell_shell.py | 2 +- src/Mod/Fem/femexamples/constraint_contact_solid_solid.py | 2 +- src/Mod/Fem/femexamples/constraint_section_print.py | 2 +- src/Mod/Fem/femexamples/constraint_selfweight_cantilever.py | 2 +- src/Mod/Fem/femexamples/constraint_tie.py | 2 +- src/Mod/Fem/femexamples/constraint_transform_beam_hinged.py | 2 +- src/Mod/Fem/femexamples/constraint_transform_torque.py | 2 +- src/Mod/Fem/femexamples/frequency_beamsimple.py | 2 +- .../Fem/femexamples/material_multiple_bendingbeam_fiveboxes.py | 2 +- .../Fem/femexamples/material_multiple_bendingbeam_fivefaces.py | 2 +- .../Fem/femexamples/material_multiple_tensionrod_twoboxes.py | 2 +- src/Mod/Fem/femexamples/material_nl_platewithhole.py | 2 +- src/Mod/Fem/femexamples/mystran_plate.py | 2 +- src/Mod/Fem/femexamples/rc_wall_2d.py | 2 +- src/Mod/Fem/femexamples/square_pipe_end_twisted_edgeforces.py | 2 +- src/Mod/Fem/femexamples/square_pipe_end_twisted_nodeforces.py | 2 +- src/Mod/Fem/femexamples/thermomech_bimetall.py | 2 +- src/Mod/Fem/femexamples/thermomech_flow1d.py | 2 +- src/Mod/Fem/femexamples/thermomech_spine.py | 2 +- src/Mod/Fem/femexamples/truss_3d_cs_circle_ele_seg3.py | 2 +- 40 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/Mod/Fem/femexamples/boxanalysis_frequency.py b/src/Mod/Fem/femexamples/boxanalysis_frequency.py index b00e1a70af..a12065cd15 100644 --- a/src/Mod/Fem/femexamples/boxanalysis_frequency.py +++ b/src/Mod/Fem/femexamples/boxanalysis_frequency.py @@ -35,7 +35,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": [], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "frequency" } diff --git a/src/Mod/Fem/femexamples/boxanalysis_static.py b/src/Mod/Fem/femexamples/boxanalysis_static.py index e08ac1194c..95bfb07311 100644 --- a/src/Mod/Fem/femexamples/boxanalysis_static.py +++ b/src/Mod/Fem/femexamples/boxanalysis_static.py @@ -37,7 +37,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed", "force", "pressure"], - "solvers": ["calculix", "elmer"], + "solvers": ["calculix", "ccxtools", "elmer"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/buckling_lateraltorsionalbuckling.py b/src/Mod/Fem/femexamples/buckling_lateraltorsionalbuckling.py index 527b14e6e9..06dcb8b3f7 100644 --- a/src/Mod/Fem/femexamples/buckling_lateraltorsionalbuckling.py +++ b/src/Mod/Fem/femexamples/buckling_lateraltorsionalbuckling.py @@ -37,7 +37,7 @@ def get_information(): "meshtype": "face", "meshelement": "Tria6", "constraints": ["displacement", "force"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "buckling" } diff --git a/src/Mod/Fem/femexamples/buckling_platebuckling.py b/src/Mod/Fem/femexamples/buckling_platebuckling.py index 16f7f3caa0..71917f99c0 100644 --- a/src/Mod/Fem/femexamples/buckling_platebuckling.py +++ b/src/Mod/Fem/femexamples/buckling_platebuckling.py @@ -37,7 +37,7 @@ def get_information(): "meshtype": "face", "meshelement": "Tria6", "constraints": ["displacement", "force"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "buckling" } diff --git a/src/Mod/Fem/femexamples/ccx_buckling_flexuralbuckling.py b/src/Mod/Fem/femexamples/ccx_buckling_flexuralbuckling.py index dafae06425..b4a75cf3f8 100644 --- a/src/Mod/Fem/femexamples/ccx_buckling_flexuralbuckling.py +++ b/src/Mod/Fem/femexamples/ccx_buckling_flexuralbuckling.py @@ -38,7 +38,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Hexa8", "constraints": ["fixed", "force"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "buckling" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_beam_circle.py b/src/Mod/Fem/femexamples/ccx_cantilever_beam_circle.py index f4e26bdc68..a32273d4ac 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_beam_circle.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_beam_circle.py @@ -34,7 +34,7 @@ def get_information(): "meshtype": "edge", "meshelement": "Seg3", "constraints": ["fixed", "force"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_beam_pipe.py b/src/Mod/Fem/femexamples/ccx_cantilever_beam_pipe.py index 0fb9b85e78..46fbe8c726 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_beam_pipe.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_beam_pipe.py @@ -34,7 +34,7 @@ def get_information(): "meshtype": "edge", "meshelement": "Seg3", "constraints": ["fixed", "force"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_beam_rect.py b/src/Mod/Fem/femexamples/ccx_cantilever_beam_rect.py index 6ac31dd33a..577a9fae50 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_beam_rect.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_beam_rect.py @@ -34,7 +34,7 @@ def get_information(): "meshtype": "edge", "meshelement": "Seg3", "constraints": ["fixed", "force"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_ele_hexa20.py b/src/Mod/Fem/femexamples/ccx_cantilever_ele_hexa20.py index 012c1ea9e8..87f8d3ed7e 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_ele_hexa20.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_ele_hexa20.py @@ -38,7 +38,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Hexa20", "constraints": ["fixed", "force"], - "solvers": ["calculix", "elmer", "z88"], + "solvers": ["calculix", "ccxtools", "elmer", "z88"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_ele_quad4.py b/src/Mod/Fem/femexamples/ccx_cantilever_ele_quad4.py index 29bcf33718..42bcd33fce 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_ele_quad4.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_ele_quad4.py @@ -37,7 +37,7 @@ def get_information(): "meshtype": "face", "meshelement": "Quad4", "constraints": ["fixed", "force"], - "solvers": ["calculix", "mystran"], + "solvers": ["calculix", "ccxtools", "mystran"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_ele_quad8.py b/src/Mod/Fem/femexamples/ccx_cantilever_ele_quad8.py index ddba8a2b52..11ce8d0e3f 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_ele_quad8.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_ele_quad8.py @@ -37,7 +37,7 @@ def get_information(): "meshtype": "face", "meshelement": "Quad8", "constraints": ["fixed", "force"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_ele_seg2.py b/src/Mod/Fem/femexamples/ccx_cantilever_ele_seg2.py index cbdadbc8a3..b8d30bcc99 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_ele_seg2.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_ele_seg2.py @@ -37,7 +37,7 @@ def get_information(): "meshtype": "edge", "meshelement": "Seg2", "constraints": ["fixed", "force"], - "solvers": ["calculix", "mystran"], + "solvers": ["calculix", "ccxtools", "mystran"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_ele_seg3.py b/src/Mod/Fem/femexamples/ccx_cantilever_ele_seg3.py index ce075ecb15..3f2d86da31 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_ele_seg3.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_ele_seg3.py @@ -32,7 +32,7 @@ def get_information(): "meshtype": "edge", "meshelement": "Seg3", "constraints": ["fixed", "force"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_ele_tetra4.py b/src/Mod/Fem/femexamples/ccx_cantilever_ele_tetra4.py index 3d30a8009d..42c33b54f6 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_ele_tetra4.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_ele_tetra4.py @@ -36,7 +36,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tetra4", "constraints": ["fixed", "force"], - "solvers": ["calculix", "elmer", "mystran", "z88"], + "solvers": ["calculix", "ccxtools", "elmer", "mystran", "z88"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_ele_tria3.py b/src/Mod/Fem/femexamples/ccx_cantilever_ele_tria3.py index 081bde28fd..a16c752a1c 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_ele_tria3.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_ele_tria3.py @@ -37,7 +37,7 @@ def get_information(): "meshtype": "face", "meshelement": "Tria3", "constraints": ["fixed", "force"], - "solvers": ["calculix", "mystran"], + "solvers": ["calculix", "ccxtools", "mystran"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_ele_tria6.py b/src/Mod/Fem/femexamples/ccx_cantilever_ele_tria6.py index 036fc3587f..6423bdf782 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_ele_tria6.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_ele_tria6.py @@ -32,7 +32,7 @@ def get_information(): "meshtype": "face", "meshelement": "Tria6", "constraints": ["fixed", "force"], - "solvers": ["calculix", "z88"], + "solvers": ["calculix", "ccxtools", "z88"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_faceload.py b/src/Mod/Fem/femexamples/ccx_cantilever_faceload.py index 7928b48d30..4b8bf5e81d 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_faceload.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_faceload.py @@ -35,7 +35,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed", "force"], - "solvers": ["calculix", "elmer", "mystran", "z88"], + "solvers": ["calculix", "ccxtools", "elmer", "mystran", "z88"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_nodeload.py b/src/Mod/Fem/femexamples/ccx_cantilever_nodeload.py index fd82e68d4d..d88066d88f 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_nodeload.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_nodeload.py @@ -35,7 +35,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed", "force"], - "solvers": ["calculix", "elmer", "mystran", "z88"], + "solvers": ["calculix", "ccxtools", "elmer", "mystran", "z88"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_prescribeddisplacement.py b/src/Mod/Fem/femexamples/ccx_cantilever_prescribeddisplacement.py index 8b601737a5..6dd8a8586f 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_prescribeddisplacement.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_prescribeddisplacement.py @@ -35,7 +35,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed", "displacement"], - "solvers": ["calculix", "elmer"], + "solvers": ["calculix", "ccxtools", "elmer"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/constraint_centrif.py b/src/Mod/Fem/femexamples/constraint_centrif.py index d22749d98e..44ab08280b 100644 --- a/src/Mod/Fem/femexamples/constraint_centrif.py +++ b/src/Mod/Fem/femexamples/constraint_centrif.py @@ -42,7 +42,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["centrif", "fixed"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "multimaterial", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/constraint_contact_shell_shell.py b/src/Mod/Fem/femexamples/constraint_contact_shell_shell.py index 74f1be4dff..259fe26194 100644 --- a/src/Mod/Fem/femexamples/constraint_contact_shell_shell.py +++ b/src/Mod/Fem/femexamples/constraint_contact_shell_shell.py @@ -41,7 +41,7 @@ def get_information(): "meshtype": "face", "meshelement": "Tria3", "constraints": ["fixed", "force", "contact"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/constraint_contact_solid_solid.py b/src/Mod/Fem/femexamples/constraint_contact_solid_solid.py index 5b10f5cf6d..a914ecd468 100644 --- a/src/Mod/Fem/femexamples/constraint_contact_solid_solid.py +++ b/src/Mod/Fem/femexamples/constraint_contact_solid_solid.py @@ -42,7 +42,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed", "pressure", "contact"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/constraint_section_print.py b/src/Mod/Fem/femexamples/constraint_section_print.py index cd50b133dc..92979181f6 100644 --- a/src/Mod/Fem/femexamples/constraint_section_print.py +++ b/src/Mod/Fem/femexamples/constraint_section_print.py @@ -48,7 +48,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["section_print", "fixed", "pressure"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/constraint_selfweight_cantilever.py b/src/Mod/Fem/femexamples/constraint_selfweight_cantilever.py index 650db6459b..a6c1074e67 100644 --- a/src/Mod/Fem/femexamples/constraint_selfweight_cantilever.py +++ b/src/Mod/Fem/femexamples/constraint_selfweight_cantilever.py @@ -37,7 +37,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed", "self weight"], - "solvers": ["calculix", "elmer"], + "solvers": ["calculix", "ccxtools", "elmer"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/constraint_tie.py b/src/Mod/Fem/femexamples/constraint_tie.py index ead2f05820..f476771101 100644 --- a/src/Mod/Fem/femexamples/constraint_tie.py +++ b/src/Mod/Fem/femexamples/constraint_tie.py @@ -42,7 +42,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed", "force", "tie"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/constraint_transform_beam_hinged.py b/src/Mod/Fem/femexamples/constraint_transform_beam_hinged.py index 14260ac99d..5bd5c4e158 100644 --- a/src/Mod/Fem/femexamples/constraint_transform_beam_hinged.py +++ b/src/Mod/Fem/femexamples/constraint_transform_beam_hinged.py @@ -42,7 +42,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["pressure", "displacement", "transform"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/constraint_transform_torque.py b/src/Mod/Fem/femexamples/constraint_transform_torque.py index 4f9370a731..f11de2dc99 100644 --- a/src/Mod/Fem/femexamples/constraint_transform_torque.py +++ b/src/Mod/Fem/femexamples/constraint_transform_torque.py @@ -50,7 +50,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed", "force", "transform"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/frequency_beamsimple.py b/src/Mod/Fem/femexamples/frequency_beamsimple.py index 0362047c97..b412aab389 100644 --- a/src/Mod/Fem/femexamples/frequency_beamsimple.py +++ b/src/Mod/Fem/femexamples/frequency_beamsimple.py @@ -37,7 +37,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "frequency" } diff --git a/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fiveboxes.py b/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fiveboxes.py index 1a85f49fd4..02c7afbff9 100644 --- a/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fiveboxes.py +++ b/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fiveboxes.py @@ -40,7 +40,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed", "force"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "multimaterial", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fivefaces.py b/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fivefaces.py index 3a2ee24267..0d57d93635 100644 --- a/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fivefaces.py +++ b/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fivefaces.py @@ -38,7 +38,7 @@ def get_information(): "meshtype": "face", "meshelement": "Tria6", "constraints": ["fixed", "force"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "multimaterial", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/material_multiple_tensionrod_twoboxes.py b/src/Mod/Fem/femexamples/material_multiple_tensionrod_twoboxes.py index 056f74f706..89674f8db6 100644 --- a/src/Mod/Fem/femexamples/material_multiple_tensionrod_twoboxes.py +++ b/src/Mod/Fem/femexamples/material_multiple_tensionrod_twoboxes.py @@ -41,7 +41,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed", "pressure"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "multimaterial", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/material_nl_platewithhole.py b/src/Mod/Fem/femexamples/material_nl_platewithhole.py index 9f2d635069..2941aae1f8 100644 --- a/src/Mod/Fem/femexamples/material_nl_platewithhole.py +++ b/src/Mod/Fem/femexamples/material_nl_platewithhole.py @@ -49,7 +49,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed", "force"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "nonlinear", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/mystran_plate.py b/src/Mod/Fem/femexamples/mystran_plate.py index 70a7d5ef39..6f3f3a74fd 100644 --- a/src/Mod/Fem/femexamples/mystran_plate.py +++ b/src/Mod/Fem/femexamples/mystran_plate.py @@ -39,7 +39,7 @@ def get_information(): "meshtype": "face", "meshelement": "Quad4", "constraints": ["displacement", "force"], - "solvers": ["calculix", "elmer", "mystran"], + "solvers": ["calculix", "ccxtools", "elmer", "mystran"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/rc_wall_2d.py b/src/Mod/Fem/femexamples/rc_wall_2d.py index 5f1f0dd2a0..8baa02739c 100644 --- a/src/Mod/Fem/femexamples/rc_wall_2d.py +++ b/src/Mod/Fem/femexamples/rc_wall_2d.py @@ -42,7 +42,7 @@ def get_information(): "meshtype": "face", "meshelement": "Tria6", "constraints": ["fixed", "force", "displacement"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "reinforced", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/square_pipe_end_twisted_edgeforces.py b/src/Mod/Fem/femexamples/square_pipe_end_twisted_edgeforces.py index ea5baa76e9..b47461d95a 100644 --- a/src/Mod/Fem/femexamples/square_pipe_end_twisted_edgeforces.py +++ b/src/Mod/Fem/femexamples/square_pipe_end_twisted_edgeforces.py @@ -41,7 +41,7 @@ def get_information(): "meshtype": "face", "meshelement": "Tria6", "constraints": ["force", "fixed"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/square_pipe_end_twisted_nodeforces.py b/src/Mod/Fem/femexamples/square_pipe_end_twisted_nodeforces.py index fc0d6fb079..8ddebcfa7a 100644 --- a/src/Mod/Fem/femexamples/square_pipe_end_twisted_nodeforces.py +++ b/src/Mod/Fem/femexamples/square_pipe_end_twisted_nodeforces.py @@ -41,7 +41,7 @@ def get_information(): "meshtype": "face", "meshelement": "Tria6", "constraints": ["force", "fixed"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } diff --git a/src/Mod/Fem/femexamples/thermomech_bimetall.py b/src/Mod/Fem/femexamples/thermomech_bimetall.py index 28acc934e9..a12c95ae2d 100644 --- a/src/Mod/Fem/femexamples/thermomech_bimetall.py +++ b/src/Mod/Fem/femexamples/thermomech_bimetall.py @@ -49,7 +49,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed", "initial temperature", "temperature"], - "solvers": ["calculix", "elmer"], + "solvers": ["calculix", "ccxtools", "elmer"], "material": "multimaterial", "equation": "thermomechanical" } diff --git a/src/Mod/Fem/femexamples/thermomech_flow1d.py b/src/Mod/Fem/femexamples/thermomech_flow1d.py index 905739297e..df9059c47c 100644 --- a/src/Mod/Fem/femexamples/thermomech_flow1d.py +++ b/src/Mod/Fem/femexamples/thermomech_flow1d.py @@ -41,7 +41,7 @@ def get_information(): "meshtype": "edge", "meshelement": "Seg3", "constraints": ["self weight"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "fluid", "equation": "thermomechanical" } diff --git a/src/Mod/Fem/femexamples/thermomech_spine.py b/src/Mod/Fem/femexamples/thermomech_spine.py index 7820d986a8..6ea6cce4f8 100644 --- a/src/Mod/Fem/femexamples/thermomech_spine.py +++ b/src/Mod/Fem/femexamples/thermomech_spine.py @@ -38,7 +38,7 @@ def get_information(): "meshtype": "solid", "meshelement": "Tet10", "constraints": ["fixed", "initial temperature", "temperature", "heatflux"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "thermomechanical" } diff --git a/src/Mod/Fem/femexamples/truss_3d_cs_circle_ele_seg3.py b/src/Mod/Fem/femexamples/truss_3d_cs_circle_ele_seg3.py index f945655931..d123da7f72 100644 --- a/src/Mod/Fem/femexamples/truss_3d_cs_circle_ele_seg3.py +++ b/src/Mod/Fem/femexamples/truss_3d_cs_circle_ele_seg3.py @@ -41,7 +41,7 @@ def get_information(): "meshtype": "edge", "meshelement": "Seg3", "constraints": ["fixed", "force"], - "solvers": ["calculix"], + "solvers": ["calculix", "ccxtools"], "material": "solid", "equation": "mechanical" } From ad63322ac761bb8902e34e40cb0c8f2178ae0a03 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Mon, 9 Aug 2021 21:28:25 +0200 Subject: [PATCH 19/41] FEM: examples, delete not needed material values --- src/Mod/Fem/femexamples/ccx_cantilever_base_solid.py | 1 - src/Mod/Fem/femexamples/constraint_transform_beam_hinged.py | 2 -- .../Fem/femexamples/material_multiple_bendingbeam_fiveboxes.py | 3 --- .../Fem/femexamples/material_multiple_bendingbeam_fivefaces.py | 3 --- .../Fem/femexamples/material_multiple_tensionrod_twoboxes.py | 2 -- src/Mod/Fem/femexamples/material_nl_platewithhole.py | 1 - src/Mod/Fem/femexamples/rc_wall_2d.py | 1 - src/Mod/Fem/femexamples/square_pipe_end_twisted_edgeforces.py | 1 - src/Mod/Fem/femexamples/square_pipe_end_twisted_nodeforces.py | 1 - src/Mod/Fem/femexamples/thermomech_bimetall.py | 1 - 10 files changed, 16 deletions(-) diff --git a/src/Mod/Fem/femexamples/ccx_cantilever_base_solid.py b/src/Mod/Fem/femexamples/ccx_cantilever_base_solid.py index f92fc3377a..0b15acc2c1 100644 --- a/src/Mod/Fem/femexamples/ccx_cantilever_base_solid.py +++ b/src/Mod/Fem/femexamples/ccx_cantilever_base_solid.py @@ -84,7 +84,6 @@ def setup_cantilever_base_solid(doc=None, solvertype="ccxtools"): mat["Name"] = "CalculiX-Steel" mat["YoungsModulus"] = "210000 MPa" mat["PoissonRatio"] = "0.30" - mat["Density"] = "7900 kg/m^3" material_obj.Material = mat analysis.addObject(material_obj) diff --git a/src/Mod/Fem/femexamples/constraint_transform_beam_hinged.py b/src/Mod/Fem/femexamples/constraint_transform_beam_hinged.py index 5bd5c4e158..bb6e326c06 100644 --- a/src/Mod/Fem/femexamples/constraint_transform_beam_hinged.py +++ b/src/Mod/Fem/femexamples/constraint_transform_beam_hinged.py @@ -141,8 +141,6 @@ def setup(doc=None, solvertype="ccxtools"): mat["Name"] = "CalculiX-Steel" mat["YoungsModulus"] = "210000 MPa" mat["PoissonRatio"] = "0.30" - mat["Density"] = "7900 kg/m^3" - mat["ThermalExpansionCoefficient"] = "0.012 mm/m/K" material_obj.Material = mat analysis.addObject(material_obj) diff --git a/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fiveboxes.py b/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fiveboxes.py index 02c7afbff9..7f90c44a4b 100644 --- a/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fiveboxes.py +++ b/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fiveboxes.py @@ -148,7 +148,6 @@ def setup(doc=None, solvertype="ccxtools"): mat["Name"] = "Concrete-Generic" mat["YoungsModulus"] = "32000 MPa" mat["PoissonRatio"] = "0.17" - mat["Density"] = "0 kg/m^3" material_obj1.Material = mat analysis.addObject(material_obj1) @@ -158,7 +157,6 @@ def setup(doc=None, solvertype="ccxtools"): mat["Name"] = "PLA" mat["YoungsModulus"] = "3640 MPa" mat["PoissonRatio"] = "0.36" - mat["Density"] = "0 kg/m^3" material_obj2.Material = mat analysis.addObject(material_obj2) @@ -168,7 +166,6 @@ def setup(doc=None, solvertype="ccxtools"): mat["Name"] = "Steel-Generic" mat["YoungsModulus"] = "200000 MPa" mat["PoissonRatio"] = "0.30" - mat["Density"] = "7900 kg/m^3" material_obj3.Material = mat analysis.addObject(material_obj3) diff --git a/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fivefaces.py b/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fivefaces.py index 0d57d93635..d5bac29b75 100644 --- a/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fivefaces.py +++ b/src/Mod/Fem/femexamples/material_multiple_bendingbeam_fivefaces.py @@ -141,7 +141,6 @@ def setup(doc=None, solvertype="ccxtools"): mat["Name"] = "Concrete-Generic" mat["YoungsModulus"] = "32000 MPa" mat["PoissonRatio"] = "0.17" - mat["Density"] = "0 kg/m^3" material_obj1.Material = mat analysis.addObject(material_obj1) @@ -154,7 +153,6 @@ def setup(doc=None, solvertype="ccxtools"): mat["Name"] = "PLA" mat["YoungsModulus"] = "3640 MPa" mat["PoissonRatio"] = "0.36" - mat["Density"] = "0 kg/m^3" material_obj2.Material = mat analysis.addObject(material_obj2) @@ -164,7 +162,6 @@ def setup(doc=None, solvertype="ccxtools"): mat["Name"] = "Steel-Generic" mat["YoungsModulus"] = "200000 MPa" mat["PoissonRatio"] = "0.30" - mat["Density"] = "7900 kg/m^3" material_obj3.Material = mat analysis.addObject(material_obj3) diff --git a/src/Mod/Fem/femexamples/material_multiple_tensionrod_twoboxes.py b/src/Mod/Fem/femexamples/material_multiple_tensionrod_twoboxes.py index 89674f8db6..8ae258cf40 100644 --- a/src/Mod/Fem/femexamples/material_multiple_tensionrod_twoboxes.py +++ b/src/Mod/Fem/femexamples/material_multiple_tensionrod_twoboxes.py @@ -132,7 +132,6 @@ def setup(doc=None, solvertype="ccxtools"): mat["Name"] = "Aluminium-Generic" mat["YoungsModulus"] = "70000 MPa" mat["PoissonRatio"] = "0.35" - mat["Density"] = "2700 kg/m^3" material_obj_low.Material = mat material_obj_low.References = [(boxlow, "Solid1")] analysis.addObject(material_obj_low) @@ -142,7 +141,6 @@ def setup(doc=None, solvertype="ccxtools"): mat["Name"] = "Steel-Generic" mat["YoungsModulus"] = "200000 MPa" mat["PoissonRatio"] = "0.30" - mat["Density"] = "7980 kg/m^3" material_obj_upp.Material = mat material_obj_upp.References = [(boxupp, "Solid1")] analysis.addObject(material_obj_upp) diff --git a/src/Mod/Fem/femexamples/material_nl_platewithhole.py b/src/Mod/Fem/femexamples/material_nl_platewithhole.py index 2941aae1f8..e81d72f71a 100644 --- a/src/Mod/Fem/femexamples/material_nl_platewithhole.py +++ b/src/Mod/Fem/femexamples/material_nl_platewithhole.py @@ -143,7 +143,6 @@ def setup(doc=None, solvertype="ccxtools"): matprop["Name"] = "CalculiX-Steel" matprop["YoungsModulus"] = "210000 MPa" matprop["PoissonRatio"] = "0.30" - matprop["Density"] = "7900 kg/m^3" material_obj.Material = matprop analysis.addObject(material_obj) diff --git a/src/Mod/Fem/femexamples/rc_wall_2d.py b/src/Mod/Fem/femexamples/rc_wall_2d.py index 8baa02739c..aee9eb067d 100644 --- a/src/Mod/Fem/femexamples/rc_wall_2d.py +++ b/src/Mod/Fem/femexamples/rc_wall_2d.py @@ -133,7 +133,6 @@ def setup(doc=None, solvertype="ccxtools"): matrixprop["CompressiveStrength"] = "15.75 MPa" # make some hint on the possible angle units in material system matrixprop["AngleOfFriction"] = "30 deg" - matrixprop["Density"] = "2500 kg/m^3" reinfoprop = {} reinfoprop["Name"] = "Reinforcement-FIB-B500" reinfoprop["YieldStrength"] = "315 MPa" diff --git a/src/Mod/Fem/femexamples/square_pipe_end_twisted_edgeforces.py b/src/Mod/Fem/femexamples/square_pipe_end_twisted_edgeforces.py index b47461d95a..e34bd0da16 100644 --- a/src/Mod/Fem/femexamples/square_pipe_end_twisted_edgeforces.py +++ b/src/Mod/Fem/femexamples/square_pipe_end_twisted_edgeforces.py @@ -120,7 +120,6 @@ def setup(doc=None, solvertype="ccxtools"): mat["Name"] = "Steel-Generic" mat["YoungsModulus"] = "200000 MPa" mat["PoissonRatio"] = "0.30" - mat["Density"] = "7900 kg/m^3" material_obj.Material = mat analysis.addObject(material_obj) diff --git a/src/Mod/Fem/femexamples/square_pipe_end_twisted_nodeforces.py b/src/Mod/Fem/femexamples/square_pipe_end_twisted_nodeforces.py index 8ddebcfa7a..1b70ffb811 100644 --- a/src/Mod/Fem/femexamples/square_pipe_end_twisted_nodeforces.py +++ b/src/Mod/Fem/femexamples/square_pipe_end_twisted_nodeforces.py @@ -230,7 +230,6 @@ def setup(doc=None, solvertype="ccxtools"): mat["Name"] = "Steel-Generic" mat["YoungsModulus"] = "200000 MPa" mat["PoissonRatio"] = "0.30" - mat["Density"] = "7900 kg/m^3" material_obj.Material = mat analysis.addObject(material_obj) diff --git a/src/Mod/Fem/femexamples/thermomech_bimetall.py b/src/Mod/Fem/femexamples/thermomech_bimetall.py index a12c95ae2d..ef4c4b2a88 100644 --- a/src/Mod/Fem/femexamples/thermomech_bimetall.py +++ b/src/Mod/Fem/femexamples/thermomech_bimetall.py @@ -174,7 +174,6 @@ def setup(doc=None, solvertype="ccxtools"): mat["SpecificHeat"] = "510 J/kg/K" mat["ThermalConductivity"] = "13 W/m/K" mat["ThermalExpansionCoefficient"] = "0.0000012 m/m/K" - mat["Density"] = "1.00 kg/m^3" material_obj_top.Material = mat material_obj_top.References = [(geom_obj, "Solid2")] analysis.addObject(material_obj_top) From a0d860679ef6c932e2997d712210e2832f7ebb65 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Mon, 9 Aug 2021 22:15:11 +0200 Subject: [PATCH 20/41] FEM: examples, displacement constraint needs to set Free and Fix --- src/Mod/Fem/femexamples/rc_wall_2d.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/Fem/femexamples/rc_wall_2d.py b/src/Mod/Fem/femexamples/rc_wall_2d.py index aee9eb067d..ca6bcf6695 100644 --- a/src/Mod/Fem/femexamples/rc_wall_2d.py +++ b/src/Mod/Fem/femexamples/rc_wall_2d.py @@ -159,6 +159,7 @@ def setup(doc=None, solvertype="ccxtools"): # constraint displacement con_disp = ObjectsFem.makeConstraintDisplacement(doc, "ConstraintDisplacmentPrescribed") con_disp.References = [(geom_obj, "Face1")] + con_disp.zFree = False con_disp.zFix = True analysis.addObject(con_disp) From 273a7d5e3f6f03a0bb0007f9399a606be37012e2 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 10 Aug 2021 22:11:05 +0200 Subject: [PATCH 21/41] FEM: examples, use correct constraint in information for mystran plate example --- src/Mod/Fem/femexamples/mystran_plate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/femexamples/mystran_plate.py b/src/Mod/Fem/femexamples/mystran_plate.py index 6f3f3a74fd..0f187dd969 100644 --- a/src/Mod/Fem/femexamples/mystran_plate.py +++ b/src/Mod/Fem/femexamples/mystran_plate.py @@ -38,7 +38,7 @@ def get_information(): "name": "Mystran Plate", "meshtype": "face", "meshelement": "Quad4", - "constraints": ["displacement", "force"], + "constraints": ["fixed", "force"], "solvers": ["calculix", "ccxtools", "elmer", "mystran"], "material": "solid", "equation": "mechanical" From e2e7733d09b86cc391f031d413848627c5bbee47 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Tue, 10 Aug 2021 20:33:03 -0500 Subject: [PATCH 22/41] [GUI] Add autoload option to Workbench preferences page (#4957) * Add autoload option to Workbench preferences page * Address bugs identified in the forum. * Add forward declaration of QCheckBox --- src/Gui/Application.cpp | 15 +++ src/Gui/DlgSettingsLazyLoaded.ui | 87 ++++++------------ src/Gui/DlgSettingsLazyLoadedImp.cpp | 133 ++++++++++++++++++++++----- src/Gui/DlgSettingsLazyLoadedImp.h | 8 +- 4 files changed, 163 insertions(+), 80 deletions(-) diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index d59831672b..b4eb6fc118 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -2173,6 +2173,21 @@ void Application::runApplication(void) SoDebugError::setHandlerCallback( messageHandlerCoin, 0 ); #endif + // Now run the background autoload, for workbenches that should be loaded at startup, but not + // displayed to the user immediately + std::string autoloadCSV = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")-> + GetASCII("BackgroundAutoloadModules", ""); + + // Tokenize the comma-separated list and load the requested workbenches if they exist in this installation + std::vector backgroundAutoloadedModules; + std::stringstream stream(autoloadCSV); + std::string workbench; + while (std::getline(stream, workbench, ',')) + if (wb.contains(QString::fromLatin1(workbench.c_str()))) + app.activateWorkbench(workbench.c_str()); + + // Reactivate the startup workbench + app.activateWorkbench(start.c_str()); Instance->d->startingUp = false; diff --git a/src/Gui/DlgSettingsLazyLoaded.ui b/src/Gui/DlgSettingsLazyLoaded.ui index 3bfd0e9490..656dd1eaee 100644 --- a/src/Gui/DlgSettingsLazyLoaded.ui +++ b/src/Gui/DlgSettingsLazyLoaded.ui @@ -14,53 +14,37 @@ Unloaded Workbenches - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - <html><head/><body><p>Load the selected workbenches, adding their preference windows to the preferences dialog.</p></body></html> - - - Load Selected - - - - - - - - - 0 - 0 - + + + false - - - 0 - 150 - - - - <html><head/><body><p>Available unloaded workbenches</p></body></html> - - - QAbstractItemView::ExtendedSelection + + false + + false + + + + + + + + + Workbench Name + + + + + Autoload? + + + + + Load Now + + @@ -78,26 +62,13 @@ - <html><head/><body><p>To preserve resources, FreeCAD does not load workbenches until they are used. Loading them may provide access to additional preferences related to their functionality.</p><p>The following workbenches are available in your installation, but are not yet loaded:</p></body></html> + <html><head/><body><p>To preserve resources, FreeCAD does not load workbenches until they are used. Loading them may provide access to additional preferences related to their functionality.</p><p>The following workbenches are available in your installation:</p></body></html> true - - - - Qt::Vertical - - - - 429 - 37 - - - - diff --git a/src/Gui/DlgSettingsLazyLoadedImp.cpp b/src/Gui/DlgSettingsLazyLoadedImp.cpp index 660c10c79b..9d0a9b9bfa 100644 --- a/src/Gui/DlgSettingsLazyLoadedImp.cpp +++ b/src/Gui/DlgSettingsLazyLoadedImp.cpp @@ -32,6 +32,10 @@ #include "WorkbenchManager.h" #include "Workbench.h" +#include + +#include + using namespace Gui::Dialog; const uint DlgSettingsLazyLoadedImp::WorkbenchNameRole = Qt::UserRole; @@ -46,8 +50,6 @@ DlgSettingsLazyLoadedImp::DlgSettingsLazyLoadedImp( QWidget* parent ) , ui(new Ui_DlgSettingsLazyLoaded) { ui->setupUi(this); - buildUnloadedWorkbenchList(); - connect(ui->loadButton, SIGNAL(clicked()), this, SLOT(onLoadClicked())); } /** @@ -60,22 +62,45 @@ DlgSettingsLazyLoadedImp::~DlgSettingsLazyLoadedImp() void DlgSettingsLazyLoadedImp::saveSettings() { - + std::ostringstream csv; + for (const auto& checkbox : _autoloadCheckboxes) { + if (checkbox.second->isChecked()) { + if (!csv.str().empty()) + csv << ","; + csv << checkbox.first.toStdString(); + } + } + App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")-> + SetASCII("BackgroundAutoloadModules", csv.str().c_str()); } void DlgSettingsLazyLoadedImp::loadSettings() { - + // There are two different "autoload" settings: the first, in FreeCAD since 2004, + // controls the module the user sees first when starting FreeCAD, and defaults to the Start workbench + std::string start = App::Application::Config()["StartWorkbench"]; + _startupModule = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")-> + GetASCII("AutoloadModule", start.c_str()); + + // The second autoload setting does a background autoload of any number of other modules + std::string autoloadCSV = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")-> + GetASCII("BackgroundAutoloadModules", ""); + + // Tokenize the comma-separated list + _backgroundAutoloadedModules.clear(); + std::stringstream stream(autoloadCSV); + std::string workbench; + while (std::getline(stream, workbench, ',')) + _backgroundAutoloadedModules.push_back(workbench); + + + buildUnloadedWorkbenchList(); } -void DlgSettingsLazyLoadedImp::onLoadClicked() +void DlgSettingsLazyLoadedImp::onLoadClicked(const QString &wbName) { Workbench* originalActiveWB = WorkbenchManager::instance()->active(); - auto selection = ui->workbenchList->selectedItems(); - for (const auto& item : selection) { - auto name = item->data(WorkbenchNameRole).toString().toStdString(); - Application::Instance->activateWorkbench(name.c_str()); - } + Application::Instance->activateWorkbench(wbName.toStdString().c_str()); Application::Instance->activateWorkbench(originalActiveWB->name().c_str()); buildUnloadedWorkbenchList(); } @@ -86,21 +111,87 @@ Build the list of unloaded workbenches. */ void DlgSettingsLazyLoadedImp::buildUnloadedWorkbenchList() { - ui->workbenchList->clear(); QStringList workbenches = Application::Instance->workbenches(); + workbenches.sort(); + + ui->workbenchTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); + ui->workbenchTable->setRowCount(0); + _autoloadCheckboxes.clear(); // setRowCount(0) just invalidated all of these pointers + ui->workbenchTable->setColumnCount(4); + ui->workbenchTable->setSelectionMode(QAbstractItemView::SelectionMode::NoSelection); + ui->workbenchTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::ResizeToContents); + ui->workbenchTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch); + ui->workbenchTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeMode::ResizeToContents); + ui->workbenchTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeMode::ResizeToContents); + QStringList columnHeaders; + columnHeaders << QString() << tr("Workbench") << tr("Autoload") << QString(); + ui->workbenchTable->setHorizontalHeaderLabels(columnHeaders); + + unsigned int rowNumber = 0; for (const auto& wbName : workbenches) { - const auto& wb = WorkbenchManager::instance()->getWorkbench(wbName.toStdString()); - if (!wb) { - auto wbIcon = Application::Instance->workbenchIcon(wbName); - auto wbDisplayName = Application::Instance->workbenchMenuText(wbName); - auto wbTooltip = Application::Instance->workbenchToolTip(wbName); - QListWidgetItem *wbRow = new QListWidgetItem(wbIcon, wbDisplayName); - wbRow->setData(WorkbenchNameRole, QVariant(wbName)); // Store the actual internal name for easier loading - wbRow->setToolTip(wbTooltip); - ui->workbenchList->addItem(wbRow); // Transfers ownership to the QListWidget + if (wbName.toStdString() == "NoneWorkbench") + continue; // Do not list the default empty Workbench + + ui->workbenchTable->insertRow(rowNumber); + auto wbTooltip = Application::Instance->workbenchToolTip(wbName); + + // Column 1: Workbench Icon + auto wbIcon = Application::Instance->workbenchIcon(wbName); + auto iconLabel = new QLabel(); + iconLabel->setPixmap(wbIcon.scaled(QSize(20,20), Qt::AspectRatioMode::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation)); + iconLabel->setToolTip(wbTooltip); + iconLabel->setContentsMargins(5, 3, 3, 3); // Left, top, right, bottom + ui->workbenchTable->setCellWidget(rowNumber, 0, iconLabel); + + // Column 2: Workbench Display Name + auto wbDisplayName = Application::Instance->workbenchMenuText(wbName); + auto textLabel = new QLabel(wbDisplayName); + textLabel->setToolTip(wbTooltip); + ui->workbenchTable->setCellWidget(rowNumber, 1, textLabel); + + // Column 3: Autoloaded checkbox + // + // To get the checkbox centered, we have to jump through some hoops... + QWidget* checkWidget = new QWidget(this); + auto autoloadCheckbox = new QCheckBox(this); + autoloadCheckbox->setToolTip(tr("If checked") + + QString::fromUtf8(", ") + wbDisplayName + QString::fromUtf8(" ") + + tr("will be loaded automatically when FreeCAD starts up")); + QHBoxLayout* checkLayout = new QHBoxLayout(checkWidget); + checkLayout->addWidget(autoloadCheckbox); + checkLayout->setAlignment(Qt::AlignCenter); + checkLayout->setContentsMargins(0, 0, 0, 0); + + // Figure out whether to check and/or disable this checkbox: + if (wbName.toStdString() == _startupModule) { + autoloadCheckbox->setChecked(true); + autoloadCheckbox->setEnabled(false); + autoloadCheckbox->setToolTip(tr("This is the current startup module, and must be autoloaded. See Preferences/General/Autoload to change.")); } + else if (std::find(_backgroundAutoloadedModules.begin(), _backgroundAutoloadedModules.end(), + wbName.toStdString()) != _backgroundAutoloadedModules.end()) { + autoloadCheckbox->setChecked(true); + _autoloadCheckboxes.insert(std::make_pair(wbName, autoloadCheckbox)); + } + else { + _autoloadCheckboxes.insert(std::make_pair(wbName, autoloadCheckbox)); + } + ui->workbenchTable->setCellWidget(rowNumber, 2, checkWidget); + + // Column 4: Load button/loaded indicator + if (WorkbenchManager::instance()->getWorkbench(wbName.toStdString())) { + auto label = new QLabel(tr("Loaded")); + label->setAlignment(Qt::AlignCenter); + ui->workbenchTable->setCellWidget(rowNumber, 3, label); + } + else { + auto button = new QPushButton(tr("Load now")); + connect(button, &QPushButton::clicked, this, [this,wbName]() { onLoadClicked(wbName); }); + ui->workbenchTable->setCellWidget(rowNumber, 3, button); + } + + ++rowNumber; } - ui->workbenchList->sortItems(); } /** diff --git a/src/Gui/DlgSettingsLazyLoadedImp.h b/src/Gui/DlgSettingsLazyLoadedImp.h index 71c9ae07d0..4521afc023 100644 --- a/src/Gui/DlgSettingsLazyLoadedImp.h +++ b/src/Gui/DlgSettingsLazyLoadedImp.h @@ -27,6 +27,8 @@ #include "PropertyPage.h" #include +class QCheckBox; + namespace Gui { namespace Dialog { class Ui_DlgSettingsLazyLoaded; @@ -49,7 +51,7 @@ public: void loadSettings(); protected Q_SLOTS: - void onLoadClicked(); + void onLoadClicked(const QString& wbName); protected: void buildUnloadedWorkbenchList(); @@ -58,6 +60,10 @@ protected: private: std::unique_ptr ui; static const uint WorkbenchNameRole; + + std::vector _backgroundAutoloadedModules; + std::string _startupModule; + std::map _autoloadCheckboxes; }; } // namespace Dialog From c6697bbc78786ced31e4dc01f5a3ec22b578954d Mon Sep 17 00:00:00 2001 From: fandaL Date: Mon, 9 Aug 2021 08:28:49 +0200 Subject: [PATCH 23/41] FEM:request for reaction forces on Constraint displacement --- .../Fem/femsolver/calculix/write_step_output.py | 14 ++++++++++++-- .../ccx_cantilever_prescribeddisplacement.inp | 3 +++ .../femtest/data/calculix/frequency_beamsimple.inp | 7 +++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Mod/Fem/femsolver/calculix/write_step_output.py b/src/Mod/Fem/femsolver/calculix/write_step_output.py index 117cf15f44..d7ac4d3b6c 100644 --- a/src/Mod/Fem/femsolver/calculix/write_step_output.py +++ b/src/Mod/Fem/femsolver/calculix/write_step_output.py @@ -60,8 +60,9 @@ def write_step_output(f, ccxwriter): # reaction forces: freecadweb.org/tracker/view.php?id=2934 # some hint can be found in this topic: # https://forum.freecadweb.org/viewtopic.php?f=18&t=20664&start=10#p520642 - if ccxwriter.member.cons_fixed: + if ccxwriter.member.cons_fixed or ccxwriter.member.cons_displacement: f.write("** outputs --> dat file\n") + if ccxwriter.member.cons_fixed: # reaction forces for all Constraint fixed f.write("** reaction forces for Constraint fixed\n") for femobj in ccxwriter.member.cons_fixed: @@ -69,7 +70,16 @@ def write_step_output(f, ccxwriter): fix_obj_name = femobj["Object"].Name f.write("*NODE PRINT, NSET={}, TOTALS=ONLY\n".format(fix_obj_name)) f.write("RF\n") - # TODO: add Constraint Displacement if nodes are restrained + if ccxwriter.member.cons_displacement: + # reaction forces for Constraint displacement constraining translation + f.write("** reaction forces for Constraint displacement constraining translations\n") + for femobj in ccxwriter.member.cons_displacement: + if not femobj["Object"].xFree or not femobj["Object"].yFree or not femobj["Object"].zFree: + # femobj --> dict, FreeCAD document object is femobj["Object"] + disp_obj_name = femobj["Object"].Name + f.write("*NODE PRINT, NSET={}, TOTALS=ONLY\n".format(disp_obj_name)) + f.write("RF\n") + if ccxwriter.member.cons_fixed or ccxwriter.member.cons_displacement: f.write("\n") # there is no need to write all integration point results diff --git a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_prescribeddisplacement.inp b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_prescribeddisplacement.inp index 0a75d6db51..c3d571db8c 100644 --- a/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_prescribeddisplacement.inp +++ b/src/Mod/Fem/femtest/data/calculix/ccx_cantilever_prescribeddisplacement.inp @@ -407,6 +407,9 @@ S, E ** reaction forces for Constraint fixed *NODE PRINT, NSET=ConstraintFixed, TOTALS=ONLY RF +** reaction forces for Constraint displacement constraining translation +*NODE PRINT, NSET=ConstraintDisplacmentPrescribed, TOTALS=ONLY +RF *********************************************************** diff --git a/src/Mod/Fem/femtest/data/calculix/frequency_beamsimple.inp b/src/Mod/Fem/femtest/data/calculix/frequency_beamsimple.inp index d176b65293..3203aa1474 100644 --- a/src/Mod/Fem/femtest/data/calculix/frequency_beamsimple.inp +++ b/src/Mod/Fem/femtest/data/calculix/frequency_beamsimple.inp @@ -17062,6 +17062,13 @@ Fix_YZ,3 U *EL FILE S, E +** outputs --> dat file +** reaction forces for Constraint displacement constraining translation +*NODE PRINT, NSET=Fix_XYZ, TOTALS=ONLY +RF +*NODE PRINT, NSET=Fix_YZ, TOTALS=ONLY +RF + *********************************************************** *END STEP From 4edf18a630bfffd21fbec90ca32ba5f28a8f65bf Mon Sep 17 00:00:00 2001 From: Tomas Pavlicek Date: Mon, 28 Jun 2021 15:37:23 +0200 Subject: [PATCH 24/41] TechDraw - Undo for Cosmetics Vertices and Lines --- src/Mod/TechDraw/App/DrawViewPart.cpp | 5 ++++- src/Mod/TechDraw/Gui/CommandAnnotate.cpp | 10 ++++++++++ src/Mod/TechDraw/Gui/TaskCosVertex.cpp | 4 ++++ src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp | 4 ++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Mod/TechDraw/App/DrawViewPart.cpp b/src/Mod/TechDraw/App/DrawViewPart.cpp index 658b194c04..46590dadae 100644 --- a/src/Mod/TechDraw/App/DrawViewPart.cpp +++ b/src/Mod/TechDraw/App/DrawViewPart.cpp @@ -324,7 +324,10 @@ short DrawViewPart::mustExecute() const SeamHidden.isTouched() || IsoHidden.isTouched() || IsoCount.isTouched() || - CoarseView.isTouched()); + CoarseView.isTouched() || + CosmeticVertexes.isTouched() || + CosmeticEdges.isTouched() || + CenterLines.isTouched()); } if (result) { diff --git a/src/Mod/TechDraw/Gui/CommandAnnotate.cpp b/src/Mod/TechDraw/Gui/CommandAnnotate.cpp index 369e84a776..2cb79cefb2 100644 --- a/src/Mod/TechDraw/Gui/CommandAnnotate.cpp +++ b/src/Mod/TechDraw/Gui/CommandAnnotate.cpp @@ -348,6 +348,8 @@ void execMidpoints(Gui::Command* cmd) return; } + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Midpont Vertices")); + const std::vector edges = dvp->getEdgeGeometry(); double scale = dvp->getScale(); for (auto& s: selectedEdges) { @@ -357,6 +359,9 @@ void execMidpoints(Gui::Command* cmd) mid = DrawUtil::invertY(mid); dvp->addCosmeticVertex(mid / scale); } + + Gui::Command::commitCommand(); + dvp->recomputeFeature(); } @@ -371,6 +376,8 @@ void execQuadrants(Gui::Command* cmd) return; } + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Quadrant Vertices")); + const std::vector edges = dvp->getEdgeGeometry(); double scale = dvp->getScale(); for (auto& s: selectedEdges) { @@ -382,6 +389,9 @@ void execQuadrants(Gui::Command* cmd) dvp->addCosmeticVertex(iq / scale); } } + + Gui::Command::commitCommand(); + dvp->recomputeFeature(); } diff --git a/src/Mod/TechDraw/Gui/TaskCosVertex.cpp b/src/Mod/TechDraw/Gui/TaskCosVertex.cpp index bc2eaec6fe..d5532159cd 100644 --- a/src/Mod/TechDraw/Gui/TaskCosVertex.cpp +++ b/src/Mod/TechDraw/Gui/TaskCosVertex.cpp @@ -161,11 +161,15 @@ void TaskCosVertex::updateUi(void) void TaskCosVertex::addCosVertex(QPointF qPos) { + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Cosmetic Vertex")); + // Base::Console().Message("TCV::addCosVertex(%s)\n", TechDraw::DrawUtil::formatVector(qPos).c_str()); Base::Vector3d pos(qPos.x(), -qPos.y()); // int idx = (void) m_baseFeat->addCosmeticVertex(pos); m_baseFeat->requestPaint(); + + Gui::Command::commitCommand(); } diff --git a/src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp b/src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp index b439ab4972..67d13b954a 100644 --- a/src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp +++ b/src/Mod/TechDraw/Gui/TaskCosmeticLine.cpp @@ -203,6 +203,8 @@ void TaskCosmeticLine::setUiEdit() //****************************************************************************** void TaskCosmeticLine::createCosmeticLine(void) { + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Cosmetic Line")); + double x = ui->qsbx1->value().getValue(); double y = ui->qsby1->value().getValue(); double z = ui->qsbz1->value().getValue(); @@ -227,6 +229,8 @@ void TaskCosmeticLine::createCosmeticLine(void) m_tag = m_partFeat->addCosmeticEdge(p0, p1); m_ce = m_partFeat->getCosmeticEdge(m_tag); + + Gui::Command::commitCommand(); } void TaskCosmeticLine::updateCosmeticLine(void) From ed8e63270827afddd2f3e59cea2384af66e0f0d5 Mon Sep 17 00:00:00 2001 From: Tomas Pavlicek Date: Wed, 11 Aug 2021 18:33:59 +0200 Subject: [PATCH 25/41] TechDraw - Remove code artifacts from CommandDecorate.cpp (#4904) --- src/Mod/TechDraw/Gui/CommandDecorate.cpp | 118 ----------------------- 1 file changed, 118 deletions(-) diff --git a/src/Mod/TechDraw/Gui/CommandDecorate.cpp b/src/Mod/TechDraw/Gui/CommandDecorate.cpp index 6678a98971..7dabfb89c8 100644 --- a/src/Mod/TechDraw/Gui/CommandDecorate.cpp +++ b/src/Mod/TechDraw/Gui/CommandDecorate.cpp @@ -73,124 +73,6 @@ using namespace std; //internal functions bool _checkSelectionHatch(Gui::Command* cmd); -////=========================================================================== -//// TechDraw_Leader -////=========================================================================== - -//DEF_STD_CMD_A(CmdTechDrawLeaderLine) - -//CmdTechDrawLeaderLine::CmdTechDrawLeaderLine() -// : Command("TechDraw_LeaderLine") -//{ -// sAppModule = "TechDraw"; -// sGroup = QT_TR_NOOP("TechDraw"); -// sMenuText = QT_TR_NOOP("Add a line to a view"); -// sToolTipText = QT_TR_NOOP("Add a line to a view"); -// sWhatsThis = "TechDraw_LeaderLine"; -// sStatusTip = sToolTipText; -// sPixmap = "actions/techdraw-LeaderLine"; -//} - -//void CmdTechDrawLeaderLine::activated(int iMsg) -//{ -// Q_UNUSED(iMsg); - -// Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); -// if (dlg != nullptr) { -// QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Task In Progress"), -// QObject::tr("Close active task dialog and try again.")); -// return; -// } - -// TechDraw::DrawPage* page = DrawGuiUtil::findPage(this); -// if (!page) { -// return; -// } - -// std::vector selection = getSelection().getSelectionEx(); -// TechDraw::DrawView* baseFeat = nullptr; -// if (!selection.empty()) { -// baseFeat = dynamic_cast(selection[0].getObject()); -// if( baseFeat == nullptr ) { -// QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection Error"), -// QObject::tr("Can not attach leader. No base View selected.")); -// return; -// } -// } else { -// QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection Error"), -// QObject::tr("You must select a base View for the line.")); -// return; -// } - -// Gui::Control().showDialog(new TechDrawGui::TaskDlgLeaderLine(baseFeat, -// page)); -//} - -//bool CmdTechDrawLeaderLine::isActive(void) -//{ -// bool havePage = DrawGuiUtil::needPage(this); -// bool haveView = DrawGuiUtil::needView(this, false); -// return (havePage && haveView); -//} - -////=========================================================================== -//// TechDraw_RichTextAnnotation -////=========================================================================== - -//DEF_STD_CMD_A(CmdTechDrawRichTextAnnotation) - -//CmdTechDrawRichTextAnnotation::CmdTechDrawRichTextAnnotation() -// : Command("TechDraw_RichTextAnnotation") -//{ -// sAppModule = "TechDraw"; -// sGroup = QT_TR_NOOP("TechDraw"); -// sMenuText = QT_TR_NOOP("Add Rich Text Annotation"); -// sToolTipText = sMenuText; -// sWhatsThis = "TechDraw_RichTextAnnotation"; -// sStatusTip = sToolTipText; -// sPixmap = "actions/techdraw-RichTextAnnotation"; -//} - -//void CmdTechDrawRichTextAnnotation::activated(int iMsg) -//{ -// Q_UNUSED(iMsg); -// Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); -// if (dlg != nullptr) { -// QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Task In Progress"), -// QObject::tr("Close active task dialog and try again.")); -// return; -// } - -// TechDraw::DrawPage* page = DrawGuiUtil::findPage(this); -// if (!page) { -// return; -// } - -// std::vector selection = getSelection().getSelectionEx(); -// TechDraw::DrawView* baseFeat = nullptr; -// if (!selection.empty()) { -// baseFeat = dynamic_cast(selection[0].getObject()); -//// if( baseFeat == nullptr ) { -//// QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection Error"), -//// QObject::tr("Can not attach leader. No base View selected.")); -//// return; -//// } -//// } else { -//// QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection Error"), -//// QObject::tr("You must select a base View for the line.")); -//// return; -// } - -// Gui::Control().showDialog(new TaskDlgRichAnno(baseFeat, -// page)); -//} - -//bool CmdTechDrawRichTextAnnotation::isActive(void) -//{ -// bool havePage = DrawGuiUtil::needPage(this); -// bool haveView = DrawGuiUtil::needView(this, false); -// return (havePage && haveView); -//} //=========================================================================== // TechDraw_Hatch From 6d136c9b1bb8c3d39c60c5b3726849d9b8607c0f Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Wed, 11 Aug 2021 14:51:18 -0500 Subject: [PATCH 26/41] Add PartDesign Python commands for translation (#4961) Fixes #4583 --- src/Tools/updatets.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Tools/updatets.py b/src/Tools/updatets.py index 96e6ece07b..1f9e2a0a43 100755 --- a/src/Tools/updatets.py +++ b/src/Tools/updatets.py @@ -102,6 +102,12 @@ PyCommands = [["src/Mod/Draft", 'lconvert -i Gui/Resources/translations/Partpy.ts Gui/Resources/translations/Part.ts -o Gui/Resources/translations/Part.ts'], ["src/Mod/Part", 'rm Gui/Resources/translations/Partpy.ts'], + ["src/Mod/PartDesign", + 'pylupdate `find ./ -name "*.py"` -ts Gui/Resources/translations/PartDesignpy.ts'], + ["src/Mod/PartDesign", + 'lconvert -i Gui/Resources/translations/PartDesignpy.ts Gui/Resources/translations/PartDesign.ts -o Gui/Resources/translations/PartDesign.ts'], + ["src/Mod/PartDesign", + 'rm Gui/Resources/translations/PartDesignpy.ts'], ["src/Mod/Image", 'pylupdate `find ./ -name "*.py"` -ts Gui/Resources/translations/Imagepy.ts'], ["src/Mod/Image", From b0b4b321cb7e90a15311c0a1deea35d618706733 Mon Sep 17 00:00:00 2001 From: luzpaz Date: Wed, 11 Aug 2021 16:02:58 -0400 Subject: [PATCH 27/41] [PartDesign] Linting and cleaning superfluous whitespace (#4851) * [PartDesign] Linted Mod/PartDesign/Scripts for pep8 compliance * [PartDesign] Fix superfluous whitespace in WizardShaft/ * Added spaces per @chennes's feedback --- src/Mod/PartDesign/Scripts/FilletArc.py | 190 +++++++------ src/Mod/PartDesign/Scripts/Gear.py | 268 +++++++++--------- src/Mod/PartDesign/Scripts/Spring.py | 33 +-- src/Mod/PartDesign/WizardShaft/WizardShaft.py | 68 ++--- .../WizardShaft/WizardShaftTable.py | 14 +- 5 files changed, 297 insertions(+), 276 deletions(-) diff --git a/src/Mod/PartDesign/Scripts/FilletArc.py b/src/Mod/PartDesign/Scripts/FilletArc.py index c59646c8b6..b710dde829 100644 --- a/src/Mod/PartDesign/Scripts/FilletArc.py +++ b/src/Mod/PartDesign/Scripts/FilletArc.py @@ -13,108 +13,120 @@ import math # 3d vector class class Vector: - def __init__(self,x,y,z): - self.x=x - self.y=y - self.z=z - def add(self,vec): - return Vector(self.x+vec.x,self.y+vec.y,self.z+vec.z) - def sub(self,vec): - return Vector(self.x-vec.x,self.y-vec.y,self.z-vec.z) - def dot(self,vec): - return self.x*vec.x+self.y*vec.y+self.z*vec.z - def mult(self,s): - return Vector(self.x*s,self.y*s,self.z*s) - def cross(self,vec): - return Vector( - self.y * vec.z - self.z * vec.y, - self.z * vec.x - self.x * vec.z, - self.x * vec.y - self.y * vec.x) - def length(self): - return math.sqrt(self.x*self.x+self.y*self.y+self.z*self.z) - def norm(self): - l = self.length() - if l > 0: - self.x /= l - self.y /= l - self.z /= l - def __repr__(self): - return "(%f,%f,%f)" % (self.x,self.y,self.z) + def __init__(self, x, y, z): + self.x = x + self.y = y + self.z = z + + def add(self, vec): + return Vector(self.x+vec.x, self.y+vec.y, self.z+vec.z) + + def sub(self, vec): + return Vector(self.x-vec.x, self.y-vec.y, self.z-vec.z) + + def dot(self, vec): + return self.x*vec.x+self.y*vec.y+self.z*vec.z + + def mult(self, s): + return Vector(self.x*s, self.y*s, self.z*s) + + def cross(self,vec): + return Vector( + self.y * vec.z - self.z * vec.y, + self.z * vec.x - self.x * vec.z, + self.x * vec.y - self.y * vec.x) + + def length(self): + return math.sqrt(self.x*self.x+self.y*self.y+self.z*self.z) + + def norm(self): + l = self.length() + if l > 0: + self.x /= l + self.y /= l + self.z /= l + + def __repr__(self): + return "(%f,%f,%f)" % (self.x, self.y, self.z) + # A signum function def sgn(val): - if val > 0: - return 1 - elif val < 0: - return -1 - else: - return 0 + if val > 0: + return 1 + elif val < 0: + return -1 + else: + return 0 + # M1 ... is the center of the arc # P ... is the end point of the arc and start point of the line # Q .. is a second point on the line -# N ... is the normal of the plane where the arc and the line lie on, usually N=(0,0,1) +# N ... is the normal of the plane where the arc and the line lie on, usually N=(0,0,1) # r2 ... the fillet radius -# ccw ... counter-clockwise means which part of the arc is given. ccw must be either True or False +# ccw ... counter-clockwise means which part of the arc is given. ccw must be either True or False + + def makeFilletArc(M1,P,Q,N,r2,ccw): - u = Q.sub(P) - v = P.sub(M1) - if ccw: - b = u.cross(N) - else: - b = N.cross(u) - b.norm() - - uu = u.dot(u) - uv = u.dot(v) - r1 = v.length() + u = Q.sub(P) + v = P.sub(M1) + if ccw: + b = u.cross(N) + else: + b = N.cross(u) + b.norm() + + uu = u.dot(u) + uv = u.dot(v) + r1 = v.length() + + # distinguish between internal and external fillets + r2 *= sgn(uv) + + cc = 2.0 * r2 * (b.dot(v)-r1) + dd = uv * uv - uu * cc + if dd < 0: + raise RuntimeError("Unable to calculate intersection points") + t1 = (-uv + math.sqrt(dd)) / uu + t2 = (-uv - math.sqrt(dd)) / uu + + if (abs(t1) < abs(t2)): + t = t1 + else: + t = t2 + + br2 = b.mult(r2) + print(br2) + ut = u.mult(t) + print(ut) + M2 = P.add(ut).add(br2) + S1 = M1.mult(r2/(r1+r2)).add(M2.mult(r1/(r1+r2))) + S2 = M2.sub(br2) + + return (S1, S2, M2) - # distinguish between internal and external fillets - r2 *= sgn(uv); - cc = 2.0 * r2 * (b.dot(v)-r1) - dd = uv * uv - uu * cc - if dd < 0: - raise RuntimeError("Unable to calculate intersection points") - t1 = (-uv + math.sqrt(dd)) / uu - t2 = (-uv - math.sqrt(dd)) / uu - - if (abs(t1) < abs(t2)): - t = t1 - else: - t = t2 - - br2 = b.mult(r2) - print(br2) - ut = u.mult(t) - print(ut) - M2 = P.add(ut).add(br2) - S1 = M1.mult(r2/(r1+r2)).add(M2.mult(r1/(r1+r2))) - S2 = M2.sub(br2) - - return (S1,S2,M2) - - def test(): - from FreeCAD import Base - import Part + from FreeCAD import Base + import Part - P1=Base.Vector(1,-5,0) - P2=Base.Vector(-5,2,0) - P3=Base.Vector(1,5,0) - #Q=Base.Vector(5,10,0) - #Q=Base.Vector(5,11,0) - Q=Base.Vector(5,0,0) - r2=3.0 - axis=Base.Vector(0,0,1) - ccw=False + P1 = Base.Vector(1, -5, 0) + P2 = Base.Vector(-5, 2, 0) + P3 = Base.Vector(1, 5, 0) + # Q = Base.Vector(5, 10, 0) + # Q = Base.Vector(5, 11, 0) + Q = Base.Vector(5, 0, 0) + r2 = 3.0 + axis = Base.Vector(0, 0, 1) + ccw = False - arc=Part.ArcOfCircle(P1,P2,P3) - C=arc.Center - Part.show(Part.makeLine(P3,Q)) - Part.show(arc.toShape()) + arc = Part.ArcOfCircle(P1, P2, P3) + C = arc.Center + Part.show(Part.makeLine(P3, Q)) + Part.show(arc.toShape()) - (S1,S2,M2) = makeArc(Vector(C.x,C.y,C.z),Vector(P3.x,P3.y,P3.z),Vector(Q.x,Q.y,Q.z),Vector(axis.x,axis.y,axis.z),r2,ccw) - circle=Part.Circle(Base.Vector(M2.x,M2.y,M2.z), Base.Vector(0,0,1), math.fabs(r2)) - Part.show(circle.toShape()) + (S1, S2, M2) = makeArc(Vector(C.x,C.y,C.z), Vector(P3.x,P3.y,P3.z), Vector(Q.x, Q.y, Q.z), Vector(axis.x, axis.y, axis.z), r2, ccw) + circle = Part.Circle(Base.Vector(M2.x, M2.y, M2.z), Base.Vector(0, 0, 1), math.fabs(r2)) + Part.show(circle.toShape()) diff --git a/src/Mod/PartDesign/Scripts/Gear.py b/src/Mod/PartDesign/Scripts/Gear.py index 75f617e0cd..508bd6081f 100644 --- a/src/Mod/PartDesign/Scripts/Gear.py +++ b/src/Mod/PartDesign/Scripts/Gear.py @@ -1,12 +1,20 @@ -#Involute Gears Generation Script -#by Marcin Wanczyk (dj_who) -#(c) 2011 LGPL +# Involute Gears Generation Script +# by Marcin Wanczyk (dj_who) +# (c) 2011 LGPL -import FreeCAD, FreeCADGui, Part, Draft, math, MeshPart, Mesh -from PySide import QtGui,QtCore -App=FreeCAD -Gui=FreeCADGui +import FreeCAD +import FreeCADGui +import Part +import Draft +import MeshPart +import Mesh +import math +from PySide import QtGui, QtCore + +App = FreeCAD +Gui = FreeCADGui + def proceed(): try: @@ -15,150 +23,150 @@ def proceed(): hide() QtGui.QApplication.restoreOverrideCursor() -def compute(): + +def compute(): QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) if FreeCAD.ActiveDocument is None: FreeCAD.newDocument("Gear") - oldDocumentObjects=App.ActiveDocument.Objects + oldDocumentObjects = App.ActiveDocument.Objects try: - N = int(l1.text()) + N = int(l1.text()) p = float(l2.text()) alfa = int(l3.text()) - y = float(l4.text()) #standard value y<1 for gear drives y>1 for Gear pumps - m=p/math.pi #standard value 0.06, 0.12, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 60 (polish norm) - c = float(l5.text())*m #standard value 0,1*m - 0,3*m - j = float(l6.text())*m #standard value 0,015 - 0,04*m - width = float(l7.text()) #gear width + y = float(l4.text()) # standard value y<1 for gear drives y>1 for Gear pumps + m = p/math.pi # standard value 0.06, 0.12, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 60 (polish norm) + c = float(l5.text())*m # standard value 0,1*m - 0,3*m + j = float(l6.text())*m # standard value 0,015 - 0,04*m + width = float(l7.text()) # gear width except ValueError: FreeCAD.Console.PrintError("Wrong input! Only numbers allowed...\n") - - - #tooth height - h=2*y*m+c - #pitch diameter - d=N*m - #root diameter - df=d - 2*y*m - 2*c #df=d-2hf where and hf=y*m+c + # tooth height + h = 2*y*m+c - #addendum diameter - da=d + 2*y*m #da=d+2ha where ha=y*m + # pitch diameter + d = N*m - #base diameter for involute - db=d * math.cos(math.radians(alfa)) + # root diameter + df = d - 2*y*m - 2*c # df=d-2hf where and hf=y*m+c + + # addendum diameter + da = d + 2*y*m # da=d+2ha where ha=y*m + + # base diameter for involute + db = d * math.cos(math.radians(alfa)) #Base circle - baseCircle=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","BaseCircle") + baseCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "BaseCircle") Draft._Circle(baseCircle) Draft._ViewProviderDraft(baseCircle.ViewObject) baseCircle.Radius = db/2 - baseCircle.FirstAngle=0.0 - baseCircle.LastAngle=0.0 - - #Root circle - rootCircle=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","RootCircle") + baseCircle.FirstAngle = 0.0 + baseCircle.LastAngle = 0.0 + + # Root circle + rootCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "RootCircle") Draft._Circle(rootCircle) Draft._ViewProviderDraft(rootCircle.ViewObject) rootCircle.Radius = df/2 - rootCircle.FirstAngle=0.0 - rootCircle.LastAngle=0.0 + rootCircle.FirstAngle = 0.0 + rootCircle.LastAngle = 0.0 - #Addendum circle - addendumCircle=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","AddendumCircle") + # Addendum circle + addendumCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "AddendumCircle") Draft._Circle(addendumCircle) Draft._ViewProviderDraft(addendumCircle.ViewObject) addendumCircle.Radius = da/2 - addendumCircle.FirstAngle=0.0 - addendumCircle.LastAngle=0.0 + addendumCircle.FirstAngle = 0.0 + addendumCircle.LastAngle = 0.0 - #Pitch circle - pitchCircle=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","PitchCircle") + # Pitch circle + pitchCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "PitchCircle") Draft._Circle(pitchCircle) Draft._ViewProviderDraft(pitchCircle.ViewObject) pitchCircle.Radius = d/2 - pitchCircle.FirstAngle=0.0 - pitchCircle.LastAngle=0.0 + pitchCircle.FirstAngle = 0.0 + pitchCircle.LastAngle = 0.0 #************ Calculating right sides of teeth - #Involute of base circle - involute=[] - involutee=[] - involutesav=[] + # Involute of base circle + involute = [] + involutee = [] + involutesav = [] - for t in range(0,60,1): - x=db/2*(math.cos(math.radians(t))+math.radians(t)*math.sin(math.radians(t))) - y=db/2*(math.sin(math.radians(t))-math.radians(t)*math.cos(math.radians(t))) - involute.append(Part.Vertex(x,y,0).Point) + for t in range(0, 60, 1): + x = db/2*(math.cos(math.radians(t))+math.radians(t)*math.sin(math.radians(t))) + y = db/2*(math.sin(math.radians(t))-math.radians(t)*math.cos(math.radians(t))) + involute.append(Part.Vertex(x, y, 0).Point) -#************ Drawing right sides of teeth +#************ Drawing right sides of teeth involutesav.extend(involute) involutee.extend(involute) - for angle in range(1,N+1,1): - involuteobj = FreeCAD.ActiveDocument.addObject("Part::Feature","InvoluteL"+str(angle)) - involutee.insert(0,(0,0,0)) - involuteshape = Part.makePolygon(involutee) + for angle in range(1, N+1, 1): + involuteobj = FreeCAD.ActiveDocument.addObject("Part::Feature", "InvoluteL" + str(angle)) + involutee.insert(0, (0, 0, 0)) + involuteshape = Part.makePolygon(involutee) involuteobj.Shape=involuteshape - involutee=[] - for num in range(0,60,1): - point=involute.pop() - pointt=Part.Vertex(point.x*math.cos(math.radians(angle*360/N)) - point.y*math.sin(math.radians(angle*360/N)),point.x*math.sin(math.radians(angle*360/N)) + point.y*math.cos(math.radians(angle*360/N)),0).Point + involutee = [] + for num in range(0, 60, 1): + point = involute.pop() + pointt = Part.Vertex(point.x*math.cos(math.radians(angle*360/N)) - point.y*math.sin(math.radians(angle*360/N)),point.x*math.sin(math.radians(angle*360/N)) + point.y*math.cos(math.radians(angle*360/N)),0).Point involutee.insert(0,pointt) involute.extend(involutesav) - involutee=[] - + involutee = [] + #************ Calculating difference between tooth spacing on BaseCircle and PitchCircle - pc=App.ActiveDocument.getObject("PitchCircle") - inv=App.ActiveDocument.getObject("InvoluteL1") - cut=inv.Shape.cut(pc.Shape) + pc = App.ActiveDocument.getObject("PitchCircle") + inv = App.ActiveDocument.getObject("InvoluteL1") + cut = inv.Shape.cut(pc.Shape) # FreeCAD.ActiveDocument.addObject("Part::Feature","CutInv").Shape=cut - invPoint=cut.Vertexes[0].Point + invPoint = cut.Vertexes[0].Point - - diff=invPoint.y*2 # instead of making axial symmetry and calculating point distance. - anglediff=2*math.asin(diff/d) + diff = invPoint.y*2 # instead of making axial symmetry and calculating point distance. + anglediff = 2*math.asin(diff/d) #************ Calculating left sides of teeth #************ Inversing Involute - for num in range(0,60,1): - point=involute.pop() - pointt=Part.Vertex(point.x,point.y*-1,0).Point - involutee.insert(0,pointt) + for num in range(0, 60, 1): + point = involute.pop() + pointt = Part.Vertex(point.x, point.y*-1, 0).Point + involutee.insert(0, pointt) involute.extend(involutee) - involutee=[] + involutee = [] -#Normal tooth size calculated as: 0,5* p - j j=m * 0,1 below are calculations +#Normal tooth size calculated as: 0,5* p - j j = m * 0,1 below are calculations # 0,5* p - m * 0,1 # 0,5* p - p /pi * 0,1 # 0,5*360/N - ((360/N)/pi)* 0,1 -# 0,5*360/N - (360/N)*((1/pi)*0,1) j=(p/pi)*0,1 +# 0,5*360/N - (360/N)*((1/pi)*0,1) j = (p/pi)*0,1 # 0,5*360/N - (360/N)*((p/pi)*0,1)/p # 0,5*360/N - (360/N)*( j )/p - for num in range(0,60,1): - point=involute.pop() - pointt=Part.Vertex(point.x*math.cos(math.radians(180/N-(360/N)*(j/p))+anglediff) - point.y*math.sin(math.radians(180/N-(360/N)*(j/p))+anglediff),point.x*math.sin(math.radians(180/N-(360/N)*(j/p))+anglediff) + point.y*math.cos(math.radians(180/N-(360/N)*(j/p))+anglediff),0).Point - involutee.insert(0,pointt) + for num in range(0, 60, 1): + point = involute.pop() + pointt = Part.Vertex(point.x*math.cos(math.radians(180/N-(360/N)*(j/p))+anglediff) - point.y*math.sin(math.radians(180/N-(360/N)*(j/p))+anglediff),point.x*math.sin(math.radians(180/N-(360/N)*(j/p))+anglediff) + point.y*math.cos(math.radians(180/N-(360/N)*(j/p))+anglediff),0).Point + involutee.insert(0, pointt) involute.extend(involutee) - involutesav=[] + involutesav = [] involutesav.extend(involute) #************ Drawing left sides of teeth - for angle in range(1,N+1,1): - involuteobj = FreeCAD.ActiveDocument.addObject("Part::Feature","InvoluteR"+str(angle)) - involutee.insert(0,(0,0,0)) + for angle in range(1, N+1, 1): + involuteobj = FreeCAD.ActiveDocument.addObject("Part::Feature", "InvoluteR" + str(angle)) + involutee.insert(0, (0, 0, 0)) involuteshape = Part.makePolygon(involutee) - involuteobj.Shape=involuteshape - involutee=[] + involuteobj.Shape = involuteshape + involutee = [] for num in range(0,60,1): - point=involute.pop() - pointt=Part.Vertex(point.x*math.cos(math.radians(angle*360/N)) - point.y*math.sin(math.radians(angle*360/N)),point.x*math.sin(math.radians(angle*360/N)) + point.y*math.cos(math.radians(angle*360/N)),0).Point + point = involute.pop() + pointt = Part.Vertex(point.x*math.cos(math.radians(angle*360/N)) - point.y*math.sin(math.radians(angle*360/N)),point.x*math.sin(math.radians(angle*360/N)) + point.y*math.cos(math.radians(angle*360/N)),0).Point involutee.insert(0,pointt) involute.extend(involutesav) @@ -166,56 +174,56 @@ def compute(): #************ Forming teeth - cutCircle=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","CutCircle") + cutCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "CutCircle") Draft._Circle(cutCircle) Draft._ViewProviderDraft(cutCircle.ViewObject) - cutCircle.Radius = da # da because must be bigger than addendumCircle and bigger than whole construction da is right for this but it not has to be. - cutCircle.FirstAngle=0.0 - cutCircle.LastAngle=0.0 + cutCircle.Radius = da # da because must be bigger than addendumCircle and bigger than whole construction da is right for this but it not has to be. + cutCircle.FirstAngle = 0.0 + cutCircle.LastAngle = 0.0 - - cutTool=cutCircle.Shape.cut(addendumCircle.Shape) - #cutshape=Part.show(cutTool) - - gearShape=rootCircle.Shape - for invNum in range(1,N+1,1): - invL=App.ActiveDocument.getObject("InvoluteL"+str(invNum)) - invR=App.ActiveDocument.getObject("InvoluteR"+str(invNum)) - cutL=invL.Shape.cut(cutTool) - cutR=invR.Shape.cut(cutTool) - pointL=cutL.Vertexes.pop().Point - pointR=cutR.Vertexes.pop().Point - faceEdge=Part.makeLine(pointL,pointR) - - toothWhole=cutL.fuse(cutR) - toothWhole=toothWhole.fuse(faceEdge) - toothWire=Part.Wire(toothWhole.Edges) - toothShape=Part.Face(toothWire) -# tooth=App.ActiveDocument.addObject("Part::Feature","Tooth"+str(invNum)) + cutTool = cutCircle.Shape.cut(addendumCircle.Shape) + # cutshape = Part.show(cutTool) + + gearShape = rootCircle.Shape + + for invNum in range(1, N+1, 1): + invL = App.ActiveDocument.getObject("InvoluteL" + str(invNum)) + invR = App.ActiveDocument.getObject("InvoluteR" + str(invNum)) + cutL = invL.Shape.cut(cutTool) + cutR = invR.Shape.cut(cutTool) + pointL = cutL.Vertexes.pop().Point + pointR = cutR.Vertexes.pop().Point + faceEdge = Part.makeLine(pointL, pointR) + + toothWhole = cutL.fuse(cutR) + toothWhole = toothWhole.fuse(faceEdge) + toothWire = Part.Wire(toothWhole.Edges) + toothShape = Part.Face(toothWire) +# tooth = App.ActiveDocument.addObject("Part::Feature", "Tooth" +str(invNum)) # tooth.Shape=toothShape - gearShape=gearShape.fuse(toothShape) + gearShape = gearShape.fuse(toothShape) for o in App.ActiveDocument.Objects: - if oldDocumentObjects.count(o)==0: + if oldDocumentObjects.count(o) == 0: App.ActiveDocument.removeObject(o.Name) - gearFlat=App.ActiveDocument.addObject("Part::Feature","GearFlat") - gearFlat.Shape=gearShape - Gui.ActiveDocument.getObject(gearFlat.Name).Visibility=False + gearFlat = App.ActiveDocument.addObject("Part::Feature", "GearFlat") + gearFlat.Shape = gearShape + Gui.ActiveDocument.getObject(gearFlat.Name).Visibility = False - gear=App.ActiveDocument.addObject("Part::Extrusion","Gear3D") - gear.Base=gearFlat - gear.Dir=(0,0,width) + gear = App.ActiveDocument.addObject("Part::Extrusion", "Gear3D") + gear.Base = gearFlat + gear.Dir = (0, 0, width) App.ActiveDocument.recompute() - + if c1.isChecked()==True: - gearMesh=App.ActiveDocument.addObject("Mesh::Feature","Gear3D-mesh") + gearMesh = App.ActiveDocument.addObject("Mesh::Feature", "Gear3D-mesh") faces = [] - triangles = gear.Shape.tessellate(1) # the number represents the precision of the tessellation) + triangles = gear.Shape.tessellate(1) # the number represents the precision of the tessellation) for tri in triangles[1]: face = [] for i in range(3): @@ -224,16 +232,16 @@ def compute(): faces.append(face) mesh = Mesh.Mesh(faces) - gearMesh.Mesh=mesh + gearMesh.Mesh = mesh App.ActiveDocument.removeObject(gear.Name) App.ActiveDocument.removeObject(gearFlat.Name) App.ActiveDocument.recompute() Gui.SendMsgToActiveView("ViewFit") - + QtGui.QApplication.restoreOverrideCursor() - + hide() @@ -259,22 +267,22 @@ la.addWidget(t3) l3 = QtGui.QLineEdit() l3.setText("20") la.addWidget(l3) -t4 = QtGui.QLabel("Tooth height factor (y)") +t4 = QtGui.QLabel("Tooth height factor (y)") la.addWidget(t4) l4 = QtGui.QLineEdit() l4.setText("1.0") la.addWidget(l4) -t5 = QtGui.QLabel("Tooth clearance (c)") +t5 = QtGui.QLabel("Tooth clearance (c)") la.addWidget(t5) l5 = QtGui.QLineEdit() l5.setText("0.1") la.addWidget(l5) -t6 = QtGui.QLabel("Tooth lateral clearance (j)") +t6 = QtGui.QLabel("Tooth lateral clearance (j)") la.addWidget(t6) l6 = QtGui.QLineEdit() l6.setText("0.04") la.addWidget(l6) -t7 = QtGui.QLabel("Gear width") +t7 = QtGui.QLabel("Gear width") la.addWidget(t7) l7 = QtGui.QLineEdit() l7.setText("6.0") @@ -282,7 +290,7 @@ la.addWidget(l7) c1 = QtGui.QCheckBox("Create as a Mesh") la.addWidget(c1) e1 = QtGui.QLabel("(for faster rendering)") -commentFont=QtGui.QFont("Times",8,True) +commentFont = QtGui.QFont("Times", 8, True) e1.setFont(commentFont) la.addWidget(e1) diff --git a/src/Mod/PartDesign/Scripts/Spring.py b/src/Mod/PartDesign/Scripts/Spring.py index 0018c166ef..08337fc70f 100644 --- a/src/Mod/PartDesign/Scripts/Spring.py +++ b/src/Mod/PartDesign/Scripts/Spring.py @@ -9,10 +9,10 @@ from FreeCAD import Base class MySpring: def __init__(self, obj): ''' Add the properties: Pitch, Diameter, Height, BarDiameter ''' - obj.addProperty("App::PropertyLength","Pitch","MySpring","Pitch of the helix").Pitch=5.0 - obj.addProperty("App::PropertyLength","Diameter","MySpring","Diameter of the helix").Diameter=6.0 - obj.addProperty("App::PropertyLength","Height","MySpring","Height of the helix").Height=30.0 - obj.addProperty("App::PropertyLength","BarDiameter","MySpring","Diameter of the bar").BarDiameter=3.0 + obj.addProperty("App::PropertyLength", "Pitch", "MySpring", "Pitch of the helix").Pitch = 5.0 + obj.addProperty("App::PropertyLength", "Diameter", "MySpring", "Diameter of the helix").Diameter = 6.0 + obj.addProperty("App::PropertyLength", "Height", "MySpring", "Height of the helix").Height = 30.0 + obj.addProperty("App::PropertyLength", "BarDiameter", "MySpring", "Diameter of the bar").BarDiameter = 3.0 obj.Proxy = self def onChanged(self, fp, prop): @@ -24,28 +24,29 @@ class MySpring: radius = fp.Diameter/2 height = fp.Height barradius = fp.BarDiameter/2 - myhelix=Part.makeHelix(pitch,height,radius) - g=myhelix.Edges[0].Curve - c=Part.Circle() - c.Center=g.value(0) # start point of the helix - c.Axis=(0,1,0) - c.Radius=barradius - p=c.toShape() + myhelix = Part.makeHelix(pitch, height, radius) + g = myhelix.Edges[0].Curve + c = Part.Circle() + c.Center = g.value(0) # start point of the helix + c.Axis = (0, 1, 0) + c.Radius = barradius + p = c.toShape() section = Part.Wire([p]) - makeSolid=1 #change to 1 to make a solid - isFrenet=1 - myspring=Part.Wire(myhelix).makePipeShell([section],makeSolid,isFrenet) + makeSolid = 1 # change to 1 to make a solid + isFrenet = 1 + myspring = Part.Wire(myhelix).makePipeShell([section], makeSolid, isFrenet) fp.Shape = myspring def makeMySpring(): doc = FreeCAD.activeDocument() if doc is None: doc = FreeCAD.newDocument() - spring=doc.addObject("Part::FeaturePython","My_Spring") + spring = doc.addObject("Part::FeaturePython", "My_Spring") spring.Label = "My Spring" MySpring(spring) - spring.ViewObject.Proxy=0 + spring.ViewObject.Proxy = 0 doc.recompute() + if __name__ == "__main__": makeMySpring() diff --git a/src/Mod/PartDesign/WizardShaft/WizardShaft.py b/src/Mod/PartDesign/WizardShaft/WizardShaft.py index 85895b90f1..7dabcbaa9c 100644 --- a/src/Mod/PartDesign/WizardShaft/WizardShaft.py +++ b/src/Mod/PartDesign/WizardShaft/WizardShaft.py @@ -31,7 +31,7 @@ class TaskWizardShaft: "Shaft Wizard" App = FreeCAD Gui = FreeCADGui - + def __init__(self, doc): mw = QtGui.QApplication.activeWindow() #cw = mw.centralWidget() # This is a qmdiarea widget @@ -48,20 +48,20 @@ class TaskWizardShaft: featureWindow = cw.subWindowList()[-1] else: featureWindow = cw.activeSubWindow() - + # Buttons for diagram display - buttonLayout = QtGui.QGridLayout() - bnames = [["All [x]", "All [y]", "All [z]" ], - ["N [x]", "Q [y]", "Q [z]"], - ["Mt [x]", "Mb [z]", "Mb [y]"], - ["", "w [y]", "w [z]"], - ["sigma [x]", "sigma [y]", "sigma [z]"], + buttonLayout = QtGui.QGridLayout() + bnames = [["All [x]", "All [y]", "All [z]" ], + ["N [x]", "Q [y]", "Q [z]"], + ["Mt [x]", "Mb [z]", "Mb [y]"], + ["", "w [y]", "w [z]"], + ["sigma [x]", "sigma [y]", "sigma [z]"], ["tau [x]", "sigmab [z]", "sigmab [y]"]] - slots = [[self.slotAllx, self.slotAlly, self.slotAllz], - [self.slotFx, self.slotQy, self.slotQz], - [self.slotMx, self.slotMz, self.slotMy], - [self.slotNone, self.slotWy, self.slotWz], - [self.slotSigmax, self.slotSigmay, self.slotSigmaz], + slots = [[self.slotAllx, self.slotAlly, self.slotAllz], + [self.slotFx, self.slotQy, self.slotQz], + [self.slotMx, self.slotMz, self.slotMy], + [self.slotNone, self.slotWy, self.slotWz], + [self.slotSigmax, self.slotSigmay, self.slotSigmaz], [self.slotTaut, self.slotSigmabz, self.slotSigmaby]] self.buttons = [[None, None, None], [None, None, None], [None, None, None], [None, None, None], [None, None, None], [None, None, None]] @@ -71,23 +71,23 @@ class TaskWizardShaft: buttonLayout.addWidget(button, row, col) self.buttons[row][col] = button button.clicked.connect(slots[row][col]) - + # Create Shaft object self.shaft = Shaft(self) # Create table widget self.form = QtGui.QWidget() self.table = WizardShaftTable(self, self.shaft) - + # The top layout will contain the Shaft Wizard layout plus the elements of the FEM constraints dialog layout = QtGui.QVBoxLayout() layout.setObjectName("ShaftWizard") # Do not change or translate: Required to detect whether Shaft Wizard is running in FemGui::ViewProviderFemConstraintXXX sublayout = QtGui.QVBoxLayout() - sublayout.setObjectName("ShaftWizardLayout") # Do not change or translate + sublayout.setObjectName("ShaftWizardLayout") # Do not change or translate sublayout.addWidget(self.table.widget) sublayout.addLayout(buttonLayout) layout.addLayout(sublayout) self.form.setLayout(layout) - + # Switch to feature window mdi=FreeCADGui.getMainWindow().findChild(QtGui.QMdiArea) cw.setActiveSubWindow(featureWindow) @@ -108,49 +108,49 @@ class TaskWizardShaft: self.showDiagram("Ally") def slotAllz(self): self.showDiagram("Allz") - + def slotFx(self): self.showDiagram("Nx") def slotQy(self): self.showDiagram("Qy") def slotQz(self): self.showDiagram("Qz") - + def slotMx(self): self.showDiagram("Mx") def slotMz(self): self.showDiagram("Mz") def slotMy(self): self.showDiagram("My") - + def slotNone(self): pass def slotWy(self): self.showDiagram("wy") def slotWz(self): self.showDiagram("wz") - + def slotSigmax(self): self.showDiagram("sigmax") def slotSigmay(self): self.showDiagram("sigmay") def slotSigmaz(self): self.showDiagram("sigmaz") - + def slotTaut(self): self.showDiagram("taut") def slotSigmabz(self): self.showDiagram("sigmabz") def slotSigmaby(self): self.showDiagram("sigmaby") - + def updateButton(self, row, col, flag): self.buttons[row][col].setEnabled(flag) - + def updateButtons(self, col, flag): for row in range(len(self.buttons)): self.updateButton(row, col, flag) - + def getStandardButtons(self): return int(QtGui.QDialogButtonBox.Ok) @@ -162,7 +162,7 @@ class TaskWizardShaft: if self.form: del self.form return True - + def isAllowedAlterDocument(self): return False @@ -170,16 +170,16 @@ class TaskWizardShaft: # Problem: From the FemConstraint ViewProvider, we need to tell the Shaft instance that the user finished editing the constraint # We can find the Shaft Wizard dialog object from C++, but there is no way to reach the Shaft instance # Also it seems to be impossible to access the active dialog from Python, so Gui::Command::runCommand() is not an option either -# Note: Another way would be to create a hidden widget in the Shaft Wizard dialog and write some data to it, triggering a slot +# Note: Another way would be to create a hidden widget in the Shaft Wizard dialog and write some data to it, triggering a slot # in the python code WizardShaftDlg = None -class WizardShaftGui: +class WizardShaftGui: def Activated(self): global WizardShaftDlg WizardShaftDlg = TaskWizardShaft(FreeCAD.ActiveDocument) FreeCADGui.Control.showDialog(WizardShaftDlg) - + def GetResources(self): IconPath = FreeCAD.ConfigGet("AppHomePath") + "Mod/PartDesign/WizardShaft/WizardShaft.svg" MenuText = 'Shaft design wizard...' @@ -188,21 +188,21 @@ class WizardShaftGui: def IsActive(self): return FreeCAD.ActiveDocument != None - + def __del__(self): global WizardShaftDlg WizardShaftDlg = None - -class WizardShaftGuiCallback: + +class WizardShaftGuiCallback: def Activated(self): global WizardShaftDlg if WizardShaftDlg != None and WizardShaftDlg.table != None: WizardShaftDlg.table.finishEditConstraint() - + def isActive(self): global WizardShaftDlg return (WizardShaftDlg is not None) - + def GetResources(self): IconPath = FreeCAD.ConfigGet("AppHomePath") + "Mod/PartDesign/WizardShaft/WizardShaft.svg" MenuText = 'Shaft design wizard...' diff --git a/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py b/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py index a37b205183..0ecb7a87ff 100644 --- a/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py +++ b/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py @@ -32,7 +32,7 @@ class WizardShaftTable: "Length" : 0, "Diameter" : 1, "InnerDiameter" : 2, - "ConstraintType" : 3, + "ConstraintType": 3, "StartEdgeType" : 4, "StartEdgeSize" : 5, "EndEdgeType" : 6, @@ -57,7 +57,7 @@ class WizardShaftTable: self.wizard = w self.shaft = s # Create table widget - self.widget = QtGui.QTableWidget(len(self.rowDict), 0) + self.widget = QtGui.QTableWidget(len(self.rowDict), 0) self.widget.setObjectName("ShaftWizardTable") # Do not change or translate: Used in ViewProviderFemConstraintXXX self.widget.setWindowTitle("Shaft wizard") self.widget.resize(QtCore.QSize(300,200)) @@ -102,7 +102,7 @@ class WizardShaftTable: index = self.widget.columnCount() # Make an intelligent guess at the length/dia of the next segment if index > 0: - length = self.shaft.segments[index-1].length + length = self.shaft.segments[index-1].length diameter = self.shaft.segments[index-1].diameter if index > 2: diameter -= 5.0 @@ -156,7 +156,7 @@ class WizardShaftTable: widget.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) self.widget.setCellWidget(self.rowDict["ConstraintType"], index, widget) widget.setCurrentIndex(0) - self.widget.connect(widget, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.slotConstraintType) + self.widget.connect(widget, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.slotConstraintType) # Start edge type widget = QtGui.QComboBox(self.widget) widget.insertItem(0, "None",) @@ -221,13 +221,13 @@ class WizardShaftTable: elif rowName == "EndEdgeSize": pass - def slotEditConstraint(self): + def slotEditConstraint(self): (self.editedRow, self.editedColumn) = self.getFocusedCell() # Because finishEditConstraint() will trigger slotEditingFinished() which requires this information self.shaft.editConstraint(self.editedColumn) - + def finishEditConstraint(self): self.shaft.updateConstraint(self.editedColumn, self.getConstraintType(self.editedColumn)) - + def setLength(self, column, l): self.setDoubleValue("Length", column, l) self.shaft.updateSegment(column, length = l) From f3c2622468ebe06571fb211cae296751fc1af847 Mon Sep 17 00:00:00 2001 From: Syres916 <46537884+Syres916@users.noreply.github.com> Date: Thu, 12 Aug 2021 10:48:36 +0100 Subject: [PATCH 28/41] [PD] Gui - Primitives change Base Quantity Format --- src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp b/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp index 9563e773cf..64f0ac1b58 100644 --- a/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskPrimitiveParameters.cpp @@ -691,7 +691,7 @@ bool TaskBoxPrimitives::setPrimitive(App::DocumentObject *obj) return false; } - Base::QuantityFormat format(Base::QuantityFormat::Default, Base::UnitsApi::getDecimals()); + Base::QuantityFormat format(Base::QuantityFormat::Fixed, Base::UnitsApi::getDecimals()); switch(ui->widgetStack->currentIndex()) { case 1: // box cmd = QString::fromLatin1( From dddf76b47fe6daca9d341cefca8f936975b21af0 Mon Sep 17 00:00:00 2001 From: Syres916 <46537884+Syres916@users.noreply.github.com> Date: Thu, 12 Aug 2021 10:52:25 +0100 Subject: [PATCH 29/41] [Gui] PropertyItem change Base Quantity Format --- src/Gui/propertyeditor/PropertyItem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp index 512e7589cb..e94075eb73 100644 --- a/src/Gui/propertyeditor/PropertyItem.cpp +++ b/src/Gui/propertyeditor/PropertyItem.cpp @@ -1051,7 +1051,7 @@ void PropertyUnitItem::setValue(const QVariant& value) return; const Base::Quantity& val = value.value(); - Base::QuantityFormat format(Base::QuantityFormat::Default, decimals()); + Base::QuantityFormat format(Base::QuantityFormat::Fixed, decimals()); QString unit = Base::UnitsApi::toString(val, format); setPropertyValue(unit); } @@ -1644,7 +1644,7 @@ void PropertyVectorDistanceItem::setValue(const QVariant& variant) Base::Quantity y = Base::Quantity(value.y, Base::Unit::Length); Base::Quantity z = Base::Quantity(value.z, Base::Unit::Length); - Base::QuantityFormat format(Base::QuantityFormat::Default, decimals()); + Base::QuantityFormat format(Base::QuantityFormat::Fixed, decimals()); QString data = QString::fromLatin1("(%1, %2, %3)") .arg(Base::UnitsApi::toNumber(x, format)) .arg(Base::UnitsApi::toNumber(y, format)) @@ -2370,7 +2370,7 @@ void PropertyPlacementItem::setValue(const QVariant& value) const Base::Placement& val = value.value(); Base::Vector3d pos = val.getPosition(); - Base::QuantityFormat format(Base::QuantityFormat::Default, decimals()); + Base::QuantityFormat format(Base::QuantityFormat::Fixed, decimals()); QString data = QString::fromLatin1("App.Placement(" "App.Vector(%1,%2,%3)," "App.Rotation(App.Vector(%4,%5,%6),%7))") From 463ff88a5760b433a5085f7067e0dff8e10ecb66 Mon Sep 17 00:00:00 2001 From: Syres916 <46537884+Syres916@users.noreply.github.com> Date: Thu, 12 Aug 2021 10:55:50 +0100 Subject: [PATCH 30/41] [Base] Increase precision --- src/Base/UnitsApi.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Base/UnitsApi.cpp b/src/Base/UnitsApi.cpp index 0cde90a3b0..7b607f153a 100644 --- a/src/Base/UnitsApi.cpp +++ b/src/Base/UnitsApi.cpp @@ -156,7 +156,7 @@ void UnitsApi::setSchema(UnitSystem s) QString UnitsApi::toString(const Base::Quantity& q, const QuantityFormat& f) { - QString value = QString::fromLatin1("'%1 %2'").arg(q.getValue(), 0, f.toFormat(), f.precision+1) + QString value = QString::fromLatin1("'%1 %2'").arg(q.getValue(), 0, f.toFormat(), f.precision+2) .arg(q.getUnit().getString()); return value; } @@ -168,7 +168,7 @@ QString UnitsApi::toNumber(const Base::Quantity& q, const QuantityFormat& f) QString UnitsApi::toNumber(double d, const QuantityFormat& f) { - QString number = QString::fromLatin1("%1").arg(d, 0, f.toFormat(), f.precision+1); + QString number = QString::fromLatin1("%1").arg(d, 0, f.toFormat(), f.precision+2); return number; } From e215c22519e2d8337547960946d553a3182a8c21 Mon Sep 17 00:00:00 2001 From: Syres916 <46537884+Syres916@users.noreply.github.com> Date: Thu, 12 Aug 2021 11:13:29 +0100 Subject: [PATCH 31/41] [Base] Remove one unnecessary precision level --- src/Base/UnitsApi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Base/UnitsApi.cpp b/src/Base/UnitsApi.cpp index 7b607f153a..5bdff83351 100644 --- a/src/Base/UnitsApi.cpp +++ b/src/Base/UnitsApi.cpp @@ -168,7 +168,7 @@ QString UnitsApi::toNumber(const Base::Quantity& q, const QuantityFormat& f) QString UnitsApi::toNumber(double d, const QuantityFormat& f) { - QString number = QString::fromLatin1("%1").arg(d, 0, f.toFormat(), f.precision+2); + QString number = QString::fromLatin1("%1").arg(d, 0, f.toFormat(), f.precision+1); return number; } From 2a900b185014ce69d9a9e7c76a28dbb57ea38949 Mon Sep 17 00:00:00 2001 From: luzpaz Date: Fri, 13 Aug 2021 19:16:42 -0400 Subject: [PATCH 32/41] [PartDesign] Expose WizardShaft/ strings to translation (#4964) * [PartDesign] Expose WizardShaft/ strings to translation * Correct use of double quotes * [PD] Suggested changes in `Crowdin/pd-wizard-shaft` (#4) * [PartDesign] Use correct method name in `WizardShaft` Use `QtCore.QT_TRANSLATE_NOOP` instead of `QT_TRANSLATE_NOOP` because latter is not what is imported. * [PartDesign] Remove undesired translations in `WizardShaftTable.py` See comments by @chennes on PR#4964. Co-authored-by: Ajinkya Dahale --- src/Mod/PartDesign/WizardShaft/WizardShaft.py | 16 ++++++++++------ .../PartDesign/WizardShaft/WizardShaftTable.py | 18 +++++++++--------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/Mod/PartDesign/WizardShaft/WizardShaft.py b/src/Mod/PartDesign/WizardShaft/WizardShaft.py index 7dabcbaa9c..ae02afa5d7 100644 --- a/src/Mod/PartDesign/WizardShaft/WizardShaft.py +++ b/src/Mod/PartDesign/WizardShaft/WizardShaft.py @@ -182,9 +182,11 @@ class WizardShaftGui: def GetResources(self): IconPath = FreeCAD.ConfigGet("AppHomePath") + "Mod/PartDesign/WizardShaft/WizardShaft.svg" - MenuText = 'Shaft design wizard...' - ToolTip = 'Start the shaft design wizard' - return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip} + MenuText = QtCore.QT_TRANSLATE_NOOP("WizardShaft", "Shaft design wizard...") + ToolTip = QtCore.QT_TRANSLATE_NOOP("WizardShaft", "Start the shaft design wizard") + return {'Pixmap': IconPath, + 'MenuText': MenuText, + 'ToolTip': ToolTip} def IsActive(self): return FreeCAD.ActiveDocument != None @@ -205,9 +207,11 @@ class WizardShaftGuiCallback: def GetResources(self): IconPath = FreeCAD.ConfigGet("AppHomePath") + "Mod/PartDesign/WizardShaft/WizardShaft.svg" - MenuText = 'Shaft design wizard...' - ToolTip = 'Start the shaft design wizard' - return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip} + MenuText = QtCore.QT_TRANSLATE_NOOP("WizardShaft", "Shaft design wizard...") + ToolTip = QtCore.QT_TRANSLATE_NOOP("WizardShaft", "Start the shaft design wizard") + return {'Pixmap': IconPath, + 'MenuText': MenuText, + 'ToolTip': ToolTip} FreeCADGui.addCommand('PartDesign_WizardShaft', WizardShaftGui()) FreeCADGui.addCommand('PartDesign_WizardShaftCallBack', WizardShaftGuiCallback()) diff --git a/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py b/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py index 0ecb7a87ff..9b2139b76c 100644 --- a/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py +++ b/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py @@ -40,15 +40,15 @@ class WizardShaftTable: } rowDictReverse = {} headers = [ - "Length [mm]", - "Diameter [mm]", - "Inner diameter [mm]", - "Constraint type", - "Start edge type", - "Start edge size", - "End edge type", - "End edge size" - ] + QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "Length [mm]"), + QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "Diameter [mm]"), + QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "Inner diameter [mm]"), + QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "Constraint type"), + QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "Start edge type"), + QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "Start edge size"), + QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "End edge type"), + QtCore.QT_TRANSLATE_NOOP("WizardShaftTable", "End edge size") + ] def __init__(self, w, s): for key in iter(self.rowDict.keys()): From cfca2a790adcdb996a727911d91c7d2ccda2cf89 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Thu, 12 Aug 2021 22:26:57 -0500 Subject: [PATCH 33/41] Path: Allow parent job assignment Modifications focus on allowing the creation of operations to include direct provision of parent job. Path: Remove `useGui` implementation --- src/Mod/Path/PathScripts/PathAdaptive.py | 4 ++-- src/Mod/Path/PathScripts/PathCustom.py | 4 ++-- src/Mod/Path/PathScripts/PathDeburr.py | 5 ++--- src/Mod/Path/PathScripts/PathDrilling.py | 5 +++-- src/Mod/Path/PathScripts/PathEngrave.py | 8 ++++---- src/Mod/Path/PathScripts/PathHelix.py | 4 ++-- src/Mod/Path/PathScripts/PathJobGui.py | 2 +- src/Mod/Path/PathScripts/PathMillFace.py | 4 ++-- src/Mod/Path/PathScripts/PathOp.py | 11 +++++++---- src/Mod/Path/PathScripts/PathOpGui.py | 5 +++-- src/Mod/Path/PathScripts/PathPocket.py | 4 ++-- src/Mod/Path/PathScripts/PathPocketShape.py | 6 +++--- src/Mod/Path/PathScripts/PathProbe.py | 4 ++-- src/Mod/Path/PathScripts/PathProfile.py | 4 ++-- src/Mod/Path/PathScripts/PathProfileContour.py | 4 ++-- src/Mod/Path/PathScripts/PathProfileEdges.py | 4 ++-- src/Mod/Path/PathScripts/PathProfileFaces.py | 4 ++-- src/Mod/Path/PathScripts/PathSlot.py | 4 ++-- src/Mod/Path/PathScripts/PathSurface.py | 4 ++-- src/Mod/Path/PathScripts/PathThreadMilling.py | 4 ++-- src/Mod/Path/PathScripts/PathVcarve.py | 4 ++-- src/Mod/Path/PathScripts/PathWaterline.py | 4 ++-- 22 files changed, 53 insertions(+), 49 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathAdaptive.py b/src/Mod/Path/PathScripts/PathAdaptive.py index f7b96cf96e..4d16b1d68e 100644 --- a/src/Mod/Path/PathScripts/PathAdaptive.py +++ b/src/Mod/Path/PathScripts/PathAdaptive.py @@ -737,9 +737,9 @@ def SetupProperties(): return setup -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Adaptive operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = PathAdaptive(obj, name) + obj.Proxy = PathAdaptive(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathCustom.py b/src/Mod/Path/PathScripts/PathCustom.py index acfca878d2..73c4b4097e 100644 --- a/src/Mod/Path/PathScripts/PathCustom.py +++ b/src/Mod/Path/PathScripts/PathCustom.py @@ -70,9 +70,9 @@ def SetupProperties(): return setup -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Custom operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - proxy = ObjectCustom(obj, name) + obj.Proxy = ObjectCustom(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathDeburr.py b/src/Mod/Path/PathScripts/PathDeburr.py index 51bbb96207..fbfdf7cf02 100644 --- a/src/Mod/Path/PathScripts/PathDeburr.py +++ b/src/Mod/Path/PathScripts/PathDeburr.py @@ -291,10 +291,9 @@ def SetupProperties(): return setup -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Deburr operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - - obj.Proxy = ObjectDeburr(obj, name) + obj.Proxy = ObjectDeburr(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathDrilling.py b/src/Mod/Path/PathScripts/PathDrilling.py index 907749a904..fbcbce910a 100644 --- a/src/Mod/Path/PathScripts/PathDrilling.py +++ b/src/Mod/Path/PathScripts/PathDrilling.py @@ -159,12 +159,13 @@ def SetupProperties(): setup.append("RetractHeight") return setup -def Create(name, obj = None): + +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Drilling operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = ObjectDrilling(obj, name) + obj.Proxy = ObjectDrilling(obj, name, parentJob) if obj.Proxy: obj.Proxy.findAllHoles(obj) diff --git a/src/Mod/Path/PathScripts/PathEngrave.py b/src/Mod/Path/PathScripts/PathEngrave.py index 0edba1b07a..b60d2c977e 100644 --- a/src/Mod/Path/PathScripts/PathEngrave.py +++ b/src/Mod/Path/PathScripts/PathEngrave.py @@ -48,8 +48,8 @@ def translate(context, text, disambig=None): class ObjectEngrave(PathEngraveBase.ObjectOp): '''Proxy class for Engrave operation.''' - def __init__(self, obj, name): - super(ObjectEngrave, self).__init__(obj, name) + def __init__(self, obj, name, parentJob): + super(ObjectEngrave, self).__init__(obj, name, parentJob) self.wires = [] def opFeatures(self, obj): @@ -144,9 +144,9 @@ def SetupProperties(): return ["StartVertex"] -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns an Engrave operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = ObjectEngrave(obj, name) + obj.Proxy = ObjectEngrave(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathHelix.py b/src/Mod/Path/PathScripts/PathHelix.py index 59a729ab84..b74669f53f 100644 --- a/src/Mod/Path/PathScripts/PathHelix.py +++ b/src/Mod/Path/PathScripts/PathHelix.py @@ -214,11 +214,11 @@ def SetupProperties(): return setup -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Helix operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = ObjectHelix(obj, name) + obj.Proxy = ObjectHelix(obj, name, parentJob) if obj.Proxy: obj.Proxy.findAllHoles(obj) return obj diff --git a/src/Mod/Path/PathScripts/PathJobGui.py b/src/Mod/Path/PathScripts/PathJobGui.py index ce62fd0923..327b676b30 100644 --- a/src/Mod/Path/PathScripts/PathJobGui.py +++ b/src/Mod/Path/PathScripts/PathJobGui.py @@ -175,8 +175,8 @@ class ViewProvider: FreeCADGui.Control.closeDialog() FreeCADGui.Control.showDialog(self.taskPanel) self.taskPanel.setupUi(activate) - self.deleteOnReject = False self.showOriginAxis(True) + self.deleteOnReject = False def resetTaskPanel(self): self.showOriginAxis(False) diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py index db01c49379..f3c1d93511 100644 --- a/src/Mod/Path/PathScripts/PathMillFace.py +++ b/src/Mod/Path/PathScripts/PathMillFace.py @@ -304,9 +304,9 @@ def SetupProperties(): return setup -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Mill Facing operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = ObjectFace(obj, name) + obj.Proxy = ObjectFace(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathOp.py b/src/Mod/Path/PathScripts/PathOp.py index fa53a20093..d9537f99f7 100644 --- a/src/Mod/Path/PathScripts/PathOp.py +++ b/src/Mod/Path/PathScripts/PathOp.py @@ -119,7 +119,7 @@ class ObjectOp(object): obj.addProperty("App::PropertyDistance", "OpStockZMin", "Op Values", QtCore.QT_TRANSLATE_NOOP("PathOp", "Holds the min Z value of Stock")) obj.setEditorMode('OpStockZMin', 1) # read-only - def __init__(self, obj, name): + def __init__(self, obj, name, parentJob=None): PathLog.track() obj.addProperty("App::PropertyBool", "Active", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "Make False, to prevent operation from generating code")) @@ -190,6 +190,8 @@ class ObjectOp(object): self.initOperation(obj) if not hasattr(obj, 'DoNotSetDefaultValues') or not obj.DoNotSetDefaultValues: + if parentJob: + self.job = PathUtils.addToJob(obj, jobname=parentJob.Name) job = self.setDefaultValues(obj) if job: job.SetupSheet.Proxy.setOperationProperties(obj, name) @@ -322,7 +324,10 @@ class ObjectOp(object): def setDefaultValues(self, obj): '''setDefaultValues(obj) ... base implementation. Do not overwrite, overwrite opSetDefaultValues() instead.''' - job = PathUtils.addToJob(obj) + if self.job: + job = self.job + else: + job = PathUtils.addToJob(obj) obj.Active = True @@ -622,5 +627,3 @@ class ObjectOp(object): This function can safely be overwritten by subclasses.''' return True - - diff --git a/src/Mod/Path/PathScripts/PathOpGui.py b/src/Mod/Path/PathScripts/PathOpGui.py index 6fcdf86e54..d7aafb37c3 100644 --- a/src/Mod/Path/PathScripts/PathOpGui.py +++ b/src/Mod/Path/PathScripts/PathOpGui.py @@ -1280,12 +1280,12 @@ def Create(res): this function directly, but calls the Activated() function of the Command object that is created in each operations Gui implementation.''' FreeCAD.ActiveDocument.openTransaction("Create %s" % res.name) - obj = res.objFactory(res.name) + obj = res.objFactory(res.name, obj=None, parentJob=res.job) if obj.Proxy: obj.ViewObject.Proxy = ViewProvider(obj.ViewObject, res) obj.ViewObject.Visibility = False - FreeCAD.ActiveDocument.commitTransaction() + obj.ViewObject.Document.setEdit(obj.ViewObject, 0) return obj FreeCAD.ActiveDocument.abortTransaction() @@ -1329,6 +1329,7 @@ class CommandResources: self.menuText = menuText self.accelKey = accelKey self.toolTip = toolTip + self.job = None def SetupOperation(name, diff --git a/src/Mod/Path/PathScripts/PathPocket.py b/src/Mod/Path/PathScripts/PathPocket.py index 643cfdc06a..65249c6b20 100644 --- a/src/Mod/Path/PathScripts/PathPocket.py +++ b/src/Mod/Path/PathScripts/PathPocket.py @@ -717,9 +717,9 @@ def SetupProperties(): return PathPocketBase.SetupProperties() + ["HandleMultipleFeatures"] -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Pocket operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = ObjectPocket(obj, name) + obj.Proxy = ObjectPocket(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathPocketShape.py b/src/Mod/Path/PathScripts/PathPocketShape.py index 902fb21c44..8a52dbb6fc 100644 --- a/src/Mod/Path/PathScripts/PathPocketShape.py +++ b/src/Mod/Path/PathScripts/PathPocketShape.py @@ -242,11 +242,11 @@ def SetupProperties(): return setup -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Pocket operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject('Path::FeaturePython', name) - - obj.Proxy = ObjectPocket(obj, name) + obj.Proxy = ObjectPocket(obj, name, parentJob) + return obj return obj \ No newline at end of file diff --git a/src/Mod/Path/PathScripts/PathProbe.py b/src/Mod/Path/PathScripts/PathProbe.py index f555b00075..e2fc33e8f0 100644 --- a/src/Mod/Path/PathScripts/PathProbe.py +++ b/src/Mod/Path/PathScripts/PathProbe.py @@ -97,9 +97,9 @@ def SetupProperties(): return setup -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Probing operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - proxy = ObjectProbing(obj, name) + proxy = ObjectProbing(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathProfile.py b/src/Mod/Path/PathScripts/PathProfile.py index 39f65f9956..07901949f8 100644 --- a/src/Mod/Path/PathScripts/PathProfile.py +++ b/src/Mod/Path/PathScripts/PathProfile.py @@ -1301,9 +1301,9 @@ def SetupProperties(): return setup -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Profile based on faces operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = ObjectProfile(obj, name) + obj.Proxy = ObjectProfile(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathProfileContour.py b/src/Mod/Path/PathScripts/PathProfileContour.py index e743cdc8eb..ba02016b48 100644 --- a/src/Mod/Path/PathScripts/PathProfileContour.py +++ b/src/Mod/Path/PathScripts/PathProfileContour.py @@ -42,9 +42,9 @@ def SetupProperties(): return PathProfile.SetupProperties() -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Profile operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = ObjectContour(obj, name) + obj.Proxy = ObjectContour(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathProfileEdges.py b/src/Mod/Path/PathScripts/PathProfileEdges.py index 26a28e701a..a79a93e2b1 100644 --- a/src/Mod/Path/PathScripts/PathProfileEdges.py +++ b/src/Mod/Path/PathScripts/PathProfileEdges.py @@ -43,9 +43,9 @@ def SetupProperties(): return PathProfile.SetupProperties() -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Profile operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = ObjectProfile(obj, name) + obj.Proxy = ObjectProfile(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathProfileFaces.py b/src/Mod/Path/PathScripts/PathProfileFaces.py index 2896ab4390..f8f0b705dc 100644 --- a/src/Mod/Path/PathScripts/PathProfileFaces.py +++ b/src/Mod/Path/PathScripts/PathProfileFaces.py @@ -44,9 +44,9 @@ def SetupProperties(): return PathProfile.SetupProperties() -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Profile operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = ObjectProfile(obj, name) + obj.Proxy = ObjectProfile(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathSlot.py b/src/Mod/Path/PathScripts/PathSlot.py index 472a7fbe8f..9048b66a08 100644 --- a/src/Mod/Path/PathScripts/PathSlot.py +++ b/src/Mod/Path/PathScripts/PathSlot.py @@ -1767,9 +1767,9 @@ def SetupProperties(): return [tup[1] for tup in ObjectSlot.opPropertyDefinitions(False)] -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Slot operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = ObjectSlot(obj, name) + obj.Proxy = ObjectSlot(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index 015fdbb579..bf2107b34c 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -2124,9 +2124,9 @@ def SetupProperties(): return [tup[1] for tup in ObjectSurface.opPropertyDefinitions(False)] -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Surface operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = ObjectSurface(obj, name) + obj.Proxy = ObjectSurface(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathThreadMilling.py b/src/Mod/Path/PathScripts/PathThreadMilling.py index f7ca04b19a..0c5580d8eb 100644 --- a/src/Mod/Path/PathScripts/PathThreadMilling.py +++ b/src/Mod/Path/PathScripts/PathThreadMilling.py @@ -327,11 +327,11 @@ def SetupProperties(): return setup -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a thread milling operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = ObjectThreadMilling(obj, name) + obj.Proxy = ObjectThreadMilling(obj, name, parentJob) if obj.Proxy: obj.Proxy.findAllHoles(obj) return obj diff --git a/src/Mod/Path/PathScripts/PathVcarve.py b/src/Mod/Path/PathScripts/PathVcarve.py index 4b93ed1ad3..45609ebcf3 100644 --- a/src/Mod/Path/PathScripts/PathVcarve.py +++ b/src/Mod/Path/PathScripts/PathVcarve.py @@ -363,9 +363,9 @@ def SetupProperties(): return ["Discretize"] -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Vcarve operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - ObjectVcarve(obj, name) + obj.Proxy = ObjectVcarve(obj, name, parentJob) return obj diff --git a/src/Mod/Path/PathScripts/PathWaterline.py b/src/Mod/Path/PathScripts/PathWaterline.py index 764d4c723b..9ccc95bd68 100644 --- a/src/Mod/Path/PathScripts/PathWaterline.py +++ b/src/Mod/Path/PathScripts/PathWaterline.py @@ -1814,9 +1814,9 @@ def SetupProperties(): return [tup[1] for tup in ObjectWaterline.opPropertyDefinitions(False)] -def Create(name, obj=None): +def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Waterline operation.''' if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) - obj.Proxy = ObjectWaterline(obj, name) + obj.Proxy = ObjectWaterline(obj, name, parentJob) return obj From 30ea19a923cdc7be36bd319f6c1e1f3e8e013b67 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Sun, 9 May 2021 22:08:10 -0500 Subject: [PATCH 34/41] Path: Refactor `PathBoundary` algorithm to independent class Relocate code for PathBoundary algorithm into an independent class allowing for usage without the Dressup object creation. --- .../PathScripts/PathDressupPathBoundary.py | 228 ++++++++++-------- 1 file changed, 125 insertions(+), 103 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupPathBoundary.py b/src/Mod/Path/PathScripts/PathDressupPathBoundary.py index dc6d57f2f6..1bee910851 100644 --- a/src/Mod/Path/PathScripts/PathDressupPathBoundary.py +++ b/src/Mod/Path/PathScripts/PathDressupPathBoundary.py @@ -77,7 +77,29 @@ class DressupPathBoundary(object): obj.Stock = None return True - def boundaryCommands(self, obj, begin, end, verticalFeed): + def execute(self, obj): + pb = PathBoundary(obj.Base, obj.Stock.Shape, obj.Inside) + obj.Path = pb.execute() +# Eclass + + +class PathBoundary: + """class PathBoundary... + This class requires a base operation, boundary shape, and optional inside boolean (default is True). + The `execute()` method returns a Path object with path commands limited to cut paths inside or outside + the provided boundary shape. + """ + + def __init__(self, baseOp, boundaryShape, inside=True): + self.baseOp = baseOp + self.boundary = boundaryShape + self.inside = inside + self.safeHeight = None + self.clearanceHeight = None + self.strG1ZsafeHeight = None + self.strG0ZclearanceHeight = None + + def boundaryCommands(self, begin, end, verticalFeed): PathLog.track(_vstr(begin), _vstr(end)) if end and PathGeom.pointsCoincide(begin, end): return [] @@ -94,117 +116,117 @@ class DressupPathBoundary(object): cmds.append(Path.Command('G1', {'Z': end.z, 'F': verticalFeed})) return cmds - def execute(self, obj): - if not obj.Base or not obj.Base.isDerivedFrom('Path::Feature') or not obj.Base.Path: - return + def execute(self): + if not self.baseOp or not self.baseOp.isDerivedFrom('Path::Feature') or not self.baseOp.Path: + return None - tc = PathDressup.toolController(obj.Base) + if len(self.baseOp.Path.Commands) == 0: + PathLog.warning("No Path Commands for %s" % self.baseOp.Label) + return [] - if len(obj.Base.Path.Commands) > 0: - self.safeHeight = float(PathUtil.opProperty(obj.Base, 'SafeHeight')) - self.clearanceHeight = float(PathUtil.opProperty(obj.Base, 'ClearanceHeight')) - self.strG1ZsafeHeight = Path.Command('G1', {'Z': self.safeHeight, 'F': tc.VertFeed.Value}) - self.strG0ZclearanceHeight = Path.Command('G0', {'Z': self.clearanceHeight}) + tc = PathDressup.toolController(self.baseOp) - boundary = obj.Stock.Shape - cmd = obj.Base.Path.Commands[0] - pos = cmd.Placement.Base # bogus m/c position to create first edge - bogusX = True - bogusY = True - commands = [cmd] - lastExit = None - for cmd in obj.Base.Path.Commands[1:]: - if cmd.Name in PathGeom.CmdMoveAll: - if bogusX == True : - bogusX = ( 'X' not in cmd.Parameters ) - if bogusY : - bogusY = ( 'Y' not in cmd.Parameters ) - edge = PathGeom.edgeForCmd(cmd, pos) - if edge: - inside = edge.common(boundary).Edges - outside = edge.cut(boundary).Edges - if not obj.Inside: # UI "inside boundary" param - tmp = inside - inside = outside - outside = tmp - # it's really a shame that one cannot trust the sequence and/or - # orientation of edges - if 1 == len(inside) and 0 == len(outside): - PathLog.track(_vstr(pos), _vstr(lastExit), ' + ', cmd) - # cmd fully included by boundary - if lastExit: + self.safeHeight = float(PathUtil.opProperty(self.baseOp, 'SafeHeight')) + self.clearanceHeight = float(PathUtil.opProperty(self.baseOp, 'ClearanceHeight')) + self.strG1ZsafeHeight = Path.Command('G1', {'Z': self.safeHeight, 'F': tc.VertFeed.Value}) + self.strG0ZclearanceHeight = Path.Command('G0', {'Z': self.clearanceHeight}) + + cmd = self.baseOp.Path.Commands[0] + pos = cmd.Placement.Base # bogus m/c position to create first edge + bogusX = True + bogusY = True + commands = [cmd] + lastExit = None + for cmd in self.baseOp.Path.Commands[1:]: + if cmd.Name in PathGeom.CmdMoveAll: + if bogusX == True : + bogusX = ( 'X' not in cmd.Parameters ) + if bogusY : + bogusY = ( 'Y' not in cmd.Parameters ) + edge = PathGeom.edgeForCmd(cmd, pos) + if edge: + inside = edge.common(self.boundary).Edges + outside = edge.cut(self.boundary).Edges + if not self.inside: # UI "inside boundary" param + tmp = inside + inside = outside + outside = tmp + # it's really a shame that one cannot trust the sequence and/or + # orientation of edges + if 1 == len(inside) and 0 == len(outside): + PathLog.track(_vstr(pos), _vstr(lastExit), ' + ', cmd) + # cmd fully included by boundary + if lastExit: + if not ( bogusX or bogusY ) : # don't insert false paths based on bogus m/c position + commands.extend(self.boundaryCommands(lastExit, pos, tc.VertFeed.Value)) + lastExit = None + commands.append(cmd) + pos = PathGeom.commandEndPoint(cmd, pos) + elif 0 == len(inside) and 1 == len(outside): + PathLog.track(_vstr(pos), _vstr(lastExit), ' - ', cmd) + # cmd fully excluded by boundary + if not lastExit: + lastExit = pos + pos = PathGeom.commandEndPoint(cmd, pos) + else: + PathLog.track(_vstr(pos), _vstr(lastExit), len(inside), len(outside), cmd) + # cmd pierces boundary + while inside or outside: + ie = [e for e in inside if PathGeom.edgeConnectsTo(e, pos)] + PathLog.track(ie) + if ie: + e = ie[0] + LastPt = e.valueAt(e.LastParameter) + flip = PathGeom.pointsCoincide(pos, LastPt) + newPos = e.valueAt(e.FirstParameter) if flip else LastPt + # inside edges are taken at this point (see swap of inside/outside + # above - so we can just connect the dots ... + if lastExit: + if not ( bogusX or bogusY ) : commands.extend(self.boundaryCommands(lastExit, pos, tc.VertFeed.Value)) + lastExit = None + PathLog.track(e, flip) if not ( bogusX or bogusY ) : # don't insert false paths based on bogus m/c position - commands.extend(self.boundaryCommands(obj, lastExit, pos, tc.VertFeed.Value)) - lastExit = None - commands.append(cmd) - pos = PathGeom.commandEndPoint(cmd, pos) - elif 0 == len(inside) and 1 == len(outside): - PathLog.track(_vstr(pos), _vstr(lastExit), ' - ', cmd) - # cmd fully excluded by boundary - if not lastExit: - lastExit = pos - pos = PathGeom.commandEndPoint(cmd, pos) - else: - PathLog.track(_vstr(pos), _vstr(lastExit), len(inside), len(outside), cmd) - # cmd pierces boundary - while inside or outside: - ie = [e for e in inside if PathGeom.edgeConnectsTo(e, pos)] - PathLog.track(ie) - if ie: - e = ie[0] - LastPt = e.valueAt(e.LastParameter) - flip = PathGeom.pointsCoincide(pos, LastPt) - newPos = e.valueAt(e.FirstParameter) if flip else LastPt - # inside edges are taken at this point (see swap of inside/outside - # above - so we can just connect the dots ... - if lastExit: - if not ( bogusX or bogusY ) : commands.extend(self.boundaryCommands(obj, lastExit, pos, tc.VertFeed.Value)) - lastExit = None - PathLog.track(e, flip) - if not ( bogusX or bogusY ) : # don't insert false paths based on bogus m/c position - commands.extend(PathGeom.cmdsForEdge(e, flip, False, 50, tc.HorizFeed.Value, tc.VertFeed.Value)) - inside.remove(e) + commands.extend(PathGeom.cmdsForEdge(e, flip, False, 50, tc.HorizFeed.Value, tc.VertFeed.Value)) + inside.remove(e) + pos = newPos + lastExit = newPos + else: + oe = [e for e in outside if PathGeom.edgeConnectsTo(e, pos)] + PathLog.track(oe) + if oe: + e = oe[0] + ptL = e.valueAt(e.LastParameter) + flip = PathGeom.pointsCoincide(pos, ptL) + newPos = e.valueAt(e.FirstParameter) if flip else ptL + # outside edges are never taken at this point (see swap of + # inside/outside above) - so just move along ... + outside.remove(e) pos = newPos - lastExit = newPos else: - oe = [e for e in outside if PathGeom.edgeConnectsTo(e, pos)] - PathLog.track(oe) - if oe: - e = oe[0] - ptL = e.valueAt(e.LastParameter) - flip = PathGeom.pointsCoincide(pos, ptL) - newPos = e.valueAt(e.FirstParameter) if flip else ptL - # outside edges are never taken at this point (see swap of - # inside/outside above) - so just move along ... - outside.remove(e) - pos = newPos - else: - PathLog.error('huh?') - import Part - Part.show(Part.Vertex(pos), 'pos') - for e in inside: - Part.show(e, 'ei') - for e in outside: - Part.show(e, 'eo') - raise Exception('This is not supposed to happen') - # Eif + PathLog.error('huh?') + import Part + Part.show(Part.Vertex(pos), 'pos') + for e in inside: + Part.show(e, 'ei') + for e in outside: + Part.show(e, 'eo') + raise Exception('This is not supposed to happen') # Eif - # Ewhile - # Eif - # pos = PathGeom.commandEndPoint(cmd, pos) + # Eif + # Ewhile # Eif - else: - PathLog.track('no-move', cmd) - commands.append(cmd) - if lastExit: - commands.extend(self.boundaryCommands(obj, lastExit, None, tc.VertFeed.Value)) - lastExit = None - else: - PathLog.warning("No Path Commands for %s" % obj.Base.Label) - commands = [] - PathLog.track(commands) - obj.Path = Path.Path(commands) + # pos = PathGeom.commandEndPoint(cmd, pos) + # Eif + else: + PathLog.track('no-move', cmd) + commands.append(cmd) + if lastExit: + commands.extend(self.boundaryCommands(lastExit, None, tc.VertFeed.Value)) + lastExit = None + PathLog.track(commands) + return Path.Path(commands) +# Eclass def Create(base, name='DressupPathBoundary'): '''Create(base, name='DressupPathBoundary') ... creates a dressup limiting base's Path to a boundary.''' From 86b4c626a7b2c7667f0d5d439578a655b499a374 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Sun, 9 May 2021 22:09:22 -0500 Subject: [PATCH 35/41] Path: Add missing `translate()` function --- src/Mod/Path/PathScripts/PathDressupPathBoundary.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Mod/Path/PathScripts/PathDressupPathBoundary.py b/src/Mod/Path/PathScripts/PathDressupPathBoundary.py index 1bee910851..68eda29544 100644 --- a/src/Mod/Path/PathScripts/PathDressupPathBoundary.py +++ b/src/Mod/Path/PathScripts/PathDressupPathBoundary.py @@ -35,6 +35,11 @@ PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) # PathLog.trackModule(PathLog.thisModule()) +# Qt translation handling +def translate(context, text, disambig=None): + return QtCore.QCoreApplication.translate(context, text, disambig) + + def _vstr(v): if v: return "(%.2f, %.2f, %.2f)" % (v.x, v.y, v.z) From be1e394da9401f00ccf4a48a70363c1f12459ac3 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Mon, 17 May 2021 22:51:57 -0500 Subject: [PATCH 36/41] Path: Changes to allow for Adaptive op defaults at job level Add `SetupProperties()` function to Adaptive module. Connect `SetupProperties()` function to GUI command resources. Add string assignment for `App::PropertyPythonObject` property prototype --- src/Mod/Path/PathScripts/PathAdaptive.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Mod/Path/PathScripts/PathAdaptive.py b/src/Mod/Path/PathScripts/PathAdaptive.py index 4d16b1d68e..2c765c5d15 100644 --- a/src/Mod/Path/PathScripts/PathAdaptive.py +++ b/src/Mod/Path/PathScripts/PathAdaptive.py @@ -737,6 +737,16 @@ def SetupProperties(): return setup +def SetupProperties(): + setup = ["Side", "OperationType", "Tolerance", "StepOver", + "LiftDistance", "KeepToolDownRatio", "StockToLeave", + "ForceInsideOut", "FinishingProfile", "Stopped", + "StopProcessing", "UseHelixArcs", "AdaptiveInputState", + "AdaptiveOutputState", "HelixAngle", "HelixConeAngle", + "HelixDiameterLimit", "UseOutline"] + return setup + + def Create(name, obj=None, parentJob=None): '''Create(name) ... Creates and returns a Adaptive operation.''' if obj is None: From ff3adacd938dc7a092ff47b747c3c3ba2ae4debd Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Fri, 13 Aug 2021 18:58:29 -0500 Subject: [PATCH 37/41] Path: Auto-load the user-selected job for the simulator Add code to auto-load the user-selected job in the simulator so manual selection is not necessary with multi-job documents. --- src/Mod/Path/PathScripts/PathSimulatorGui.py | 44 +++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathSimulatorGui.py b/src/Mod/Path/PathScripts/PathSimulatorGui.py index 3a01944abc..9fc2344cbf 100644 --- a/src/Mod/Path/PathScripts/PathSimulatorGui.py +++ b/src/Mod/Path/PathScripts/PathSimulatorGui.py @@ -27,6 +27,7 @@ import PathScripts.PathDressup as PathDressup import PathScripts.PathGeom as PathGeom import PathScripts.PathLog as PathLog import PathScripts.PathUtil as PathUtil +import PathScripts.PathJob as PathJob import PathSimulator import math import os @@ -75,6 +76,7 @@ class PathSimulation: self.simperiod = 20 self.accuracy = 0.1 self.resetSimulation = False + self.jobs = [] def Connect(self, but, sig): QtCore.QObject.connect(but, QtCore.SIGNAL("clicked()"), sig) @@ -96,13 +98,9 @@ class PathSimulation: self.onSpeedBarChange() form.sliderAccuracy.valueChanged.connect(self.onAccuracyBarChange) self.onAccuracyBarChange() + self._populateJobSelection(form) form.comboJobs.currentIndexChanged.connect(self.onJobChange) - jobList = FreeCAD.ActiveDocument.findObjects("Path::FeaturePython", "Job.*") - form.comboJobs.clear() - self.jobs = [] - for j in jobList: - self.jobs.append(j) - form.comboJobs.addItem(j.ViewObject.Icon, j.Label) + self.onJobChange() FreeCADGui.Control.showDialog(self.taskForm) self.disableAnim = False self.isVoxel = True @@ -111,6 +109,40 @@ class PathSimulation: self.SimulateMill() self.initdone = True + def _populateJobSelection(self, form): + # Make Job selection combobox + setJobIdx = 0 + jobName = '' + jIdx = 0 + # Get list of Job objects in active document + jobList = FreeCAD.ActiveDocument.findObjects("Path::FeaturePython", "Job.*") + jCnt = len(jobList) + + # Check if user has selected a specific job for simulation + guiSelection = FreeCADGui.Selection.getSelectionEx() + if guiSelection: # Identify job selected by user + sel = guiSelection[0] + if hasattr(sel.Object, "Proxy") and isinstance(sel.Object.Proxy, PathJob.ObjectJob): + jobName = sel.Object.Name + FreeCADGui.Selection.clearSelection() + + # populate the job selection combobox + form.comboJobs.blockSignals(True) + form.comboJobs.clear() + form.comboJobs.blockSignals(False) + for j in jobList: + form.comboJobs.addItem(j.ViewObject.Icon, j.Label) + self.jobs.append(j) + if j.Name == jobName or jCnt == 1: + setJobIdx = jIdx + jIdx += 1 + + # Pre-select GUI-selected job in the combobox + if jobName or jCnt == 1: + form.comboJobs.setCurrentIndex(setJobIdx) + else: + form.comboJobs.setCurrentIndex(0) + def SetupSimulation(self): form = self.taskForm.form self.activeOps = [] From 1cdfd611fd85fc199652af053e34f32ae9671aa5 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Thu, 12 Aug 2021 22:50:24 -0500 Subject: [PATCH 38/41] Path: Downgrade module's debug level to INFO --- src/Mod/Path/PathScripts/PathSetupSheet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Path/PathScripts/PathSetupSheet.py b/src/Mod/Path/PathScripts/PathSetupSheet.py index 9cdfb0596c..79fbc339c8 100644 --- a/src/Mod/Path/PathScripts/PathSetupSheet.py +++ b/src/Mod/Path/PathScripts/PathSetupSheet.py @@ -34,7 +34,7 @@ __doc__ = "A container for all default values and job specific configuration val _RegisteredOps = {} -PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) +PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) # PathLog.trackModule(PathLog.thisModule()) From 2343c3cbe25f226ab4c7afce9a4f777a52ec15c1 Mon Sep 17 00:00:00 2001 From: Ajinkya Dahale Date: Sat, 14 Aug 2021 16:04:02 -0400 Subject: [PATCH 39/41] [OpenSCAD] Fixes cut color while importing CSG (#4920) * [OpenSCAD] Fixes cut color while importing CSG Fixes #4684. For some reason colors of "base" and "tool" need to be set in order for the cut to have colors as well, just like fuse and multifuse. Also tidied up the relevant code portions a little. Some testing might be needed. * [OpenSCAD] Color settings for other booleans in importCSG As per Keith's comments, this change extends recursive color setting to other boolean features viz. `Part::Common` and `Part::MultiCommon`. Also possible to add any other features needing the same treatment. * Update importCSG.py Remove unneeded parentheses and fix its/it's mixup Co-authored-by: Kurt Kremitzki --- src/Mod/OpenSCAD/importCSG.py | 46 +++++++++++++---------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/src/Mod/OpenSCAD/importCSG.py b/src/Mod/OpenSCAD/importCSG.py index 8b82d6d8b9..98197cec53 100644 --- a/src/Mod/OpenSCAD/importCSG.py +++ b/src/Mod/OpenSCAD/importCSG.py @@ -66,19 +66,22 @@ def shallHide(subject): return True return False -def setColorRecursively(obj,color,transp): - if(obj.TypeId=="Part::Fuse" or obj.TypeId=="Part::MultiFuse"): - for currentObject in obj.OutList: - if (currentObject.TypeId=="Part::Fuse" or currentObject.TypeId=="Part::MultiFuse"): - setColorRecursively(currentObject,color,transp) - else: - print("Fixing up colors for: "+str(currentObject.FullName)) - if(currentObject not in hassetcolor): - currentObject.ViewObject.ShapeColor=color - currentObject.ViewObject.Transparency=transp - setColorRecursively(currentObject,color,transp) - else: - setColorRecursively(currentObject,color,transp) +def setColorRecursively(obj, color, transp): + ''' + For some reason a part made by cutting or fusing other parts do not have a color + unless its constituents are also colored. This code sets colors for those + constituents unless already set elsewhere. + ''' + obj.ViewObject.ShapeColor = color + obj.ViewObject.Transparency = transp + # Add any other relevant features to this list + boolean_features = ["Part::Fuse", "Part::MultiFuse", "Part::Cut", + "Part::Common", "Part::MultiCommon"] + if obj.TypeId in boolean_features: + for currentObject in obj.OutList: + print(f"Fixing up colors for: {currentObject.FullName}") + if currentObject not in hassetcolor: + setColorRecursively(currentObject, color, transp) def fixVisibility(): for obj in FreeCAD.ActiveDocument.Objects: @@ -555,22 +558,7 @@ def p_color_action(p): if "Group" in obj.FullName: obj.ViewObject.Visibility=False alreadyhidden.append(obj) - if(obj.TypeId=="Part::Fuse" or obj.TypeId=="Part::MultiFuse"): - for currentObject in obj.OutList: - if (currentObject.TypeId=="Part::Fuse" or currentObject.TypeId=="Part::MultiFuse"): - setColorRecursively(currentObject,color,transp) - if(currentObject not in hassetcolor): - currentObject.ViewObject.ShapeColor=color - currentObject.ViewObject.Transparency=transp - setColorRecursively(currentObject,color,transp) - else: - setColorRecursively(currentObject,color,transp) - else: - obj.ViewObject.ShapeColor =color - obj.ViewObject.Transparency = transp - else: - obj.ViewObject.ShapeColor =color - obj.ViewObject.Transparency = transp + setColorRecursively(obj, color, transp) hassetcolor.append(obj) p[0] = p[6] From 09d9caf4387d0bed5d0162d8c3c3fe536adf5baf Mon Sep 17 00:00:00 2001 From: Tomas Pavlicek Date: Sat, 14 Aug 2021 10:05:00 +0200 Subject: [PATCH 40/41] TechDraw - Edit annotations via double-click --- src/Gui/Widgets.h | 2 +- src/Mod/TechDraw/Gui/QGIRichAnno.cpp | 34 ++++++++++++++ src/Mod/TechDraw/Gui/QGIRichAnno.h | 2 + src/Mod/TechDraw/Gui/QGIViewAnnotation.cpp | 52 ++++++++++++++++++++++ src/Mod/TechDraw/Gui/QGIViewAnnotation.h | 2 + src/Mod/TechDraw/Gui/mrichtextedit.cpp | 9 ++++ src/Mod/TechDraw/Gui/mrichtextedit.h | 1 + 7 files changed, 101 insertions(+), 1 deletion(-) diff --git a/src/Gui/Widgets.h b/src/Gui/Widgets.h index 3f868697cf..86baff6a51 100644 --- a/src/Gui/Widgets.h +++ b/src/Gui/Widgets.h @@ -373,7 +373,7 @@ private: // ---------------------------------------------------------------------- -class PropertyListEditor : public QPlainTextEdit +class GuiExport PropertyListEditor : public QPlainTextEdit { Q_OBJECT diff --git a/src/Mod/TechDraw/Gui/QGIRichAnno.cpp b/src/Mod/TechDraw/Gui/QGIRichAnno.cpp index 5920005bb4..d5e0c5286c 100644 --- a/src/Mod/TechDraw/Gui/QGIRichAnno.cpp +++ b/src/Mod/TechDraw/Gui/QGIRichAnno.cpp @@ -43,6 +43,7 @@ #include #include #include +#include # include @@ -80,6 +81,7 @@ #include "QGCustomRect.h" #include "QGIRichAnno.h" +#include "mrichtextedit.h" using namespace TechDraw; using namespace TechDrawGui; @@ -360,4 +362,36 @@ double QGIRichAnno::prefPointSize(void) return ptsSize; } +void QGIRichAnno::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) { + Q_UNUSED(event); + + TechDraw::DrawRichAnno *annotation = dynamic_cast(getViewObject()); + if (annotation == nullptr) { + return; + } + + QString text = QString::fromUtf8(annotation->AnnoText.getValue()); + + QDialog dialog(0); + dialog.setWindowTitle(QObject::tr("Rich text editor")); + dialog.setMinimumWidth(400); + dialog.setMinimumHeight(400); + + MRichTextEdit richEdit(&dialog, text); + QGridLayout gridLayout(&dialog); + gridLayout.addWidget(&richEdit, 0, 0, 1, 1); + + connect(&richEdit, SIGNAL(saveText(QString)), &dialog, SLOT(accept())); + connect(&richEdit, SIGNAL(editorFinished(void)), &dialog, SLOT(reject())); + + if (dialog.exec()) { + QString newText = richEdit.toHtml(); + if (newText != text) { + App::GetApplication().setActiveTransaction("Set Rich Annotation Text"); + annotation->AnnoText.setValue(newText.toStdString()); + App::GetApplication().closeActiveTransaction(); + } + } +} + #include diff --git a/src/Mod/TechDraw/Gui/QGIRichAnno.h b/src/Mod/TechDraw/Gui/QGIRichAnno.h index cc96b1a18e..552793cf9c 100644 --- a/src/Mod/TechDraw/Gui/QGIRichAnno.h +++ b/src/Mod/TechDraw/Gui/QGIRichAnno.h @@ -98,6 +98,8 @@ protected: double prefPointSize(void); QFont prefFont(void); + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override; + bool m_isExporting; QGCustomText* m_text; bool m_hasHover; diff --git a/src/Mod/TechDraw/Gui/QGIViewAnnotation.cpp b/src/Mod/TechDraw/Gui/QGIViewAnnotation.cpp index 5af8a8768f..7c88c270cc 100644 --- a/src/Mod/TechDraw/Gui/QGIViewAnnotation.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewAnnotation.cpp @@ -52,6 +52,8 @@ #include #include #include +#include + #include #include "Rez.h" @@ -189,4 +191,54 @@ void QGIViewAnnotation::rotateView(void) m_textItem->setRotation(-rot); } +void QGIViewAnnotation::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) +{ + Q_UNUSED(event); + TechDraw::DrawViewAnnotation *annotation = dynamic_cast(getViewObject()); + if (annotation == nullptr) { + return; + } + + const std::vector &values = annotation->Text.getValues(); + QString text; + if (values.size() > 0) { + text = QString::fromUtf8(Base::Tools::escapedUnicodeToUtf8(values[0]).c_str()); + + for (unsigned int i = 1; i < values.size(); ++i) { + text += QChar::fromLatin1('\n'); + text += QString::fromUtf8(Base::Tools::escapedUnicodeToUtf8(values[i]).c_str()); + } + } + + QDialog dialog(0); + dialog.setWindowTitle(tr("Text")); + + Gui::PropertyListEditor editor(&dialog); + editor.setPlainText(text); + + QDialogButtonBox buttonBox(&dialog); + buttonBox.setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + QVBoxLayout boxLayout(&dialog); + boxLayout.addWidget(&editor); + boxLayout.addWidget(&buttonBox); + + connect(&buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept())); + connect(&buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject())); + if (dialog.exec() == QDialog::Accepted) { + QString newText = editor.toPlainText(); + if (newText != text) { + QStringList list = newText.split(QChar::fromLatin1('\n')); + std::vector newValues; + + for (int i = 0; i < list.size(); ++i) { + newValues.push_back(Base::Tools::escapedUnicodeFromUtf8(list[i].toStdString().c_str())); + } + + App::GetApplication().setActiveTransaction("Set Annotation Text"); + annotation->Text.setValues(newValues); + App::GetApplication().closeActiveTransaction(); + } + } +} diff --git a/src/Mod/TechDraw/Gui/QGIViewAnnotation.h b/src/Mod/TechDraw/Gui/QGIViewAnnotation.h index d45846f017..0552d99125 100644 --- a/src/Mod/TechDraw/Gui/QGIViewAnnotation.h +++ b/src/Mod/TechDraw/Gui/QGIViewAnnotation.h @@ -56,6 +56,8 @@ protected: void drawAnnotation(); QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override; + QGCustomText *m_textItem; QColor m_colNormal; QColor m_colSel; diff --git a/src/Mod/TechDraw/Gui/mrichtextedit.cpp b/src/Mod/TechDraw/Gui/mrichtextedit.cpp index 630ae0f6d4..7cc23e368c 100644 --- a/src/Mod/TechDraw/Gui/mrichtextedit.cpp +++ b/src/Mod/TechDraw/Gui/mrichtextedit.cpp @@ -317,6 +317,15 @@ void MRichTextEdit::focusInEvent(QFocusEvent *) { f_textedit->setFocus(Qt::TabFocusReason); } +void MRichTextEdit::keyPressEvent(QKeyEvent *event) { + if (event->key() == Qt::Key_Return && event->modifiers() == Qt::ControlModifier) { + onSave(); + return; + } + + QWidget::keyPressEvent(event); +} + void MRichTextEdit::textUnderline() { QTextCharFormat fmt; diff --git a/src/Mod/TechDraw/Gui/mrichtextedit.h b/src/Mod/TechDraw/Gui/mrichtextedit.h index 4e5a31cc13..28a8bf5859 100644 --- a/src/Mod/TechDraw/Gui/mrichtextedit.h +++ b/src/Mod/TechDraw/Gui/mrichtextedit.h @@ -94,6 +94,7 @@ Q_SIGNALS: void list(bool checked, QTextListFormat::Style style); void indent(int delta); void focusInEvent(QFocusEvent *event); + void keyPressEvent(QKeyEvent *event); bool hasMultipleSizes(void); void addFontSize(QString fs); From 108437e37b19ec9c8c59c68f1daf3d7e4d41afdd Mon Sep 17 00:00:00 2001 From: Christophe Grellier Date: Sun, 15 Aug 2021 11:16:01 +0200 Subject: [PATCH 41/41] Update BSplineSurfacePyImp.cpp Typo in setVKnot() --- src/Mod/Part/App/BSplineSurfacePyImp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Part/App/BSplineSurfacePyImp.cpp b/src/Mod/Part/App/BSplineSurfacePyImp.cpp index 9b368f476f..6c0bc66281 100644 --- a/src/Mod/Part/App/BSplineSurfacePyImp.cpp +++ b/src/Mod/Part/App/BSplineSurfacePyImp.cpp @@ -447,10 +447,10 @@ PyObject* BSplineSurfacePy::setVKnot(PyObject *args) Handle(Geom_BSplineSurface) surf = Handle(Geom_BSplineSurface)::DownCast (getGeometryPtr()->handle()); if (M == -1) { - surf->SetUKnot(Index, K); + surf->SetVKnot(Index, K); } else { - surf->SetUKnot(Index, K, M); + surf->SetVKnot(Index, K, M); } Py_Return;