From ae42541baa7d0c21857384e966eaafee7bd2327c Mon Sep 17 00:00:00 2001 From: Patrick F Date: Sat, 13 Feb 2021 00:33:25 +0100 Subject: [PATCH 01/12] [PATH] Added basic support for arcs in deburr op --- src/Mod/Path/PathScripts/PathDeburr.py | 56 ++++++++++++++++++++--- src/Mod/Path/PathScripts/PathDeburrGui.py | 14 +++--- src/Mod/Path/PathScripts/PathOpTools.py | 22 +++++++++ src/Mod/Path/PathScripts/PathSelection.py | 3 +- 4 files changed, 80 insertions(+), 15 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDeburr.py b/src/Mod/Path/PathScripts/PathDeburr.py index ba530dc366..c540811765 100644 --- a/src/Mod/Path/PathScripts/PathDeburr.py +++ b/src/Mod/Path/PathScripts/PathDeburr.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # *************************************************************************** # * Copyright (c) 2018 sliptonic * -# * Copyright (c) 2020 Schildkroet * +# * Copyright (c) 2020-2021 Schildkroet * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * @@ -93,7 +93,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): '''Proxy class for Deburr operation.''' def opFeatures(self, obj): - return PathOp.FeatureTool | PathOp.FeatureHeights | PathOp.FeatureStepDown | PathOp.FeatureBaseEdges | PathOp.FeatureBaseFaces | PathOp.FeatureCoolant + return PathOp.FeatureTool | PathOp.FeatureHeights | PathOp.FeatureStepDown | PathOp.FeatureBaseEdges | PathOp.FeatureBaseFaces | PathOp.FeatureCoolant | PathOp.FeatureBaseGeometry def initOperation(self, obj): PathLog.track(obj.Label) @@ -139,14 +139,59 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): for base, subs in obj.Base: edges = [] basewires = [] + max_h = -99999 + for f in subs: sub = base.Shape.getElement(f) - if type(sub) == Part.Edge: + + if type(sub) == Part.Edge: # Edge edges.append(sub) + + elif type(sub) == Part.Face and sub.normalAt(0, 0) != FreeCAD.Vector(0, 0, 1): # Angled face + # Find z value of upper edge + for edge in sub.Edges: + for p0 in edge.Vertexes: + if p0.Point.z > max_h: + max_h = p0.Point.z + + # Search for lower edge and raise it to height of upper edge + for edge in sub.Edges: + if Part.Circle == type(edge.Curve): # Edge is a circle + if edge.Vertexes[0].Point.z < max_h: + + if edge.Closed: # Circle + v = FreeCAD.Vector(edge.Curve.Center.x, edge.Curve.Center.y, max_h) + new_edge = Part.makeCircle(edge.Curve.Radius, v, FreeCAD.Vector(0, 0, 1)) + edges.append(new_edge) + break + else: # Arc + if edge.Vertexes[0].Point.z == edge.Vertexes[1].Point.z: + l1 = math.sqrt((edge.Vertexes[0].Point.x - edge.Curve.Center.x)**2 + (edge.Vertexes[0].Point.y - edge.Curve.Center.y)**2) + l2 = math.sqrt((edge.Vertexes[1].Point.x - edge.Curve.Center.x)**2 + (edge.Vertexes[1].Point.y - edge.Curve.Center.y)**2) + + start_angle = math.acos((edge.Vertexes[0].Point.x - edge.Curve.Center.x) / l1) + end_angle = math.acos((edge.Vertexes[1].Point.x - edge.Curve.Center.x) / l2) + + if edge.Vertexes[0].Point.y < edge.Curve.Center.y: + start_angle *= -1 + if edge.Vertexes[1].Point.y < edge.Curve.Center.y: + end_angle *= -1 + + edge = Part.ArcOfCircle(Part.Circle(edge.Curve.Center, FreeCAD.Vector(0,0,1), edge.Curve.Radius), start_angle, end_angle).toShape() + break + + else: # Line + if edge.Vertexes[0].Point.z == edge.Vertexes[1].Point.z and edge.Vertexes[0].Point.z < max_h: + new_edge = Part.Edge(Part.LineSegment(FreeCAD.Vector(edge.Vertexes[0].Point.x, edge.Vertexes[0].Point.y, max_h), FreeCAD.Vector(edge.Vertexes[1].Point.x, edge.Vertexes[1].Point.y, max_h))) + edges.append(new_edge) + + elif sub.Wires: basewires.extend(sub.Wires) - else: + + else: # Flat face basewires.append(Part.Wire(sub.Edges)) + self.edges = edges # pylint: disable=attribute-defined-outside-init for edgelist in Part.sortEdges(edges): basewires.append(Part.Wire(edgelist)) @@ -160,9 +205,6 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): if wire: wires.append(wire) - # # Save Outside or Inside - # obj.Side = side[0] - # Set direction of op forward = (obj.Direction == 'CW') diff --git a/src/Mod/Path/PathScripts/PathDeburrGui.py b/src/Mod/Path/PathScripts/PathDeburrGui.py index d98642f4dd..1d4e0d8bf3 100644 --- a/src/Mod/Path/PathScripts/PathDeburrGui.py +++ b/src/Mod/Path/PathScripts/PathDeburrGui.py @@ -55,14 +55,14 @@ class TaskPanelBaseGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): return super(TaskPanelBaseGeometryPage, self) def addBaseGeometry(self, selection): - for sel in selection: - if sel.HasSubObjects: + #for sel in selection: + #if sel.HasSubObjects: # selectively add some elements of the drawing to the Base - for sub in sel.SubObjects: - if isinstance(sub, Part.Face): - if sub.normalAt(0, 0) != FreeCAD.Vector(0, 0, 1): - PathLog.info(translate("Path", "Ignoring non-horizontal Face")) - return + #for sub in sel.SubObjects: + # if isinstance(sub, Part.Face): + # if sub.normalAt(0, 0) != FreeCAD.Vector(0, 0, 1): + # PathLog.info(translate("Path", "Ignoring non-horizontal Face")) + # return self.super().addBaseGeometry(selection) diff --git a/src/Mod/Path/PathScripts/PathOpTools.py b/src/Mod/Path/PathScripts/PathOpTools.py index 5bdb3a9ba4..862710bca5 100644 --- a/src/Mod/Path/PathScripts/PathOpTools.py +++ b/src/Mod/Path/PathScripts/PathOpTools.py @@ -166,6 +166,28 @@ def offsetWire(wire, base, offset, forward):#, Side = None): edge = Part.makeCircle(curve.Radius - offset, curve.Center, FreeCAD.Vector(0, 0, -z)) w = Part.Wire([edge]) return w + + if Part.Circle == type(curve) and not wire.isClosed(): + # Process arc segment + z = -1 if forward else 1 + l1 = math.sqrt((edge.Vertexes[0].Point.x - curve.Center.x)**2 + (edge.Vertexes[0].Point.y - curve.Center.y)**2) + l2 = math.sqrt((edge.Vertexes[1].Point.x - curve.Center.x)**2 + (edge.Vertexes[1].Point.y - curve.Center.y)**2) + + start_angle = math.acos((edge.Vertexes[0].Point.x - curve.Center.x) / l1) + end_angle = math.acos((edge.Vertexes[1].Point.x - curve.Center.x) / l2) + + if edge.Vertexes[0].Point.y < curve.Center.y: + start_angle *= -1 + if edge.Vertexes[1].Point.y < curve.Center.y: + end_angle *= -1 + + if base.isInside(edge.Vertexes[0].Point, offset/2, True): + offset *= -1 + + edge = Part.ArcOfCircle(Part.Circle(curve.Center, FreeCAD.Vector(0,0,1), curve.Radius+offset), start_angle, end_angle).toShape() + + return Part.Wire([edge]) + if Part.Line == type(curve) or Part.LineSegment == type(curve): # offsetting a single edge doesn't work because there is an infinite # possible planes into which the edge could be offset diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py index 055bfb1757..8f0725407b 100644 --- a/src/Mod/Path/PathScripts/PathSelection.py +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # *************************************************************************** # * Copyright (c) 2015 Dan Falck * +# * Copyright (c) 2021 Schildkroet * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * @@ -115,7 +116,7 @@ class CHAMFERGate(PathBaseGate): subShape = shape.getElement(sub) if subShape.ShapeType == 'Edge': return True - elif (subShape.ShapeType == 'Face' and subShape.normalAt(0, 0) == FreeCAD.Vector(0, 0, 1)): + elif (subShape.ShapeType == 'Face'): return True return False From 7128095115ebca50ec3c5048aeba648c3d9c6eb7 Mon Sep 17 00:00:00 2001 From: Patrick F Date: Sat, 13 Feb 2021 15:41:37 +0100 Subject: [PATCH 02/12] [PATH] Improved deburr --- src/Mod/Path/PathScripts/PathDeburr.py | 50 ++++++++++++++++++++--- src/Mod/Path/PathScripts/PathDeburrGui.py | 9 ---- src/Mod/Path/PathScripts/PathOpTools.py | 16 +++++--- 3 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDeburr.py b/src/Mod/Path/PathScripts/PathDeburr.py index c540811765..2dac9b36e3 100644 --- a/src/Mod/Path/PathScripts/PathDeburr.py +++ b/src/Mod/Path/PathScripts/PathDeburr.py @@ -86,7 +86,7 @@ def toolDepthAndOffset(width, extraDepth, tool, printInfo): extraOffset = -width if angle == 180 else (extraDepth / tan) offset = toolOffset + extraOffset - return (depth, offset, suppressInfo) + return (depth, offset, extraOffset, suppressInfo) class ObjectDeburr(PathEngraveBase.ObjectOp): @@ -123,7 +123,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): if not hasattr(self, 'printInfo'): self.printInfo = True try: - (depth, offset, suppressInfo) = toolDepthAndOffset(obj.Width.Value, obj.ExtraDepth.Value, self.tool, self.printInfo) + (depth, offset, extraOffset, suppressInfo) = toolDepthAndOffset(obj.Width.Value, obj.ExtraDepth.Value, self.tool, self.printInfo) self.printInfo = not suppressInfo except ValueError as e: msg = "{} \n No path will be generated".format(e) @@ -136,10 +136,13 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): self.basewires = [] # pylint: disable=attribute-defined-outside-init self.adjusted_basewires = [] # pylint: disable=attribute-defined-outside-init wires = [] + for base, subs in obj.Base: edges = [] basewires = [] max_h = -99999 + radius_top = 0 + radius_bottom = 0 for f in subs: sub = base.Shape.getElement(f) @@ -148,11 +151,24 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): edges.append(sub) elif type(sub) == Part.Face and sub.normalAt(0, 0) != FreeCAD.Vector(0, 0, 1): # Angled face + # If an angled face is selected, the lower edge is projected to the height of the upper edge, + # to simulate an edge + # Find z value of upper edge for edge in sub.Edges: for p0 in edge.Vertexes: if p0.Point.z > max_h: max_h = p0.Point.z + + # Find biggest radius for top/bottom + for edge in sub.Edges: + if Part.Circle == type(edge.Curve): + if edge.Vertexes[0].Point.z == max_h: + if edge.Curve.Radius > radius_top: + radius_top = edge.Curve.Radius + else: + if edge.Curve.Radius > radius_bottom: + radius_bottom = edge.Curve.Radius # Search for lower edge and raise it to height of upper edge for edge in sub.Edges: @@ -160,24 +176,44 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): if edge.Vertexes[0].Point.z < max_h: if edge.Closed: # Circle - v = FreeCAD.Vector(edge.Curve.Center.x, edge.Curve.Center.y, max_h) - new_edge = Part.makeCircle(edge.Curve.Radius, v, FreeCAD.Vector(0, 0, 1)) + # New center + center = FreeCAD.Vector(edge.Curve.Center.x, edge.Curve.Center.y, max_h) + new_edge = Part.makeCircle(edge.Curve.Radius, center, FreeCAD.Vector(0, 0, 1)) edges.append(new_edge) + + # Modify offset for inner angled faces + if radius_bottom < radius_top: + offset -= 2 * extraOffset + break + else: # Arc if edge.Vertexes[0].Point.z == edge.Vertexes[1].Point.z: + # Arc vertexes are on same layer l1 = math.sqrt((edge.Vertexes[0].Point.x - edge.Curve.Center.x)**2 + (edge.Vertexes[0].Point.y - edge.Curve.Center.y)**2) l2 = math.sqrt((edge.Vertexes[1].Point.x - edge.Curve.Center.x)**2 + (edge.Vertexes[1].Point.y - edge.Curve.Center.y)**2) + # New center + center = FreeCAD.Vector(edge.Curve.Center.x, edge.Curve.Center.y, max_h) + + # Calculate angles based on x-axis (0 - PI/2) start_angle = math.acos((edge.Vertexes[0].Point.x - edge.Curve.Center.x) / l1) end_angle = math.acos((edge.Vertexes[1].Point.x - edge.Curve.Center.x) / l2) + # Angles are based on x-axis (Mirrored on x-axis) -> negative y value means negative angle if edge.Vertexes[0].Point.y < edge.Curve.Center.y: start_angle *= -1 if edge.Vertexes[1].Point.y < edge.Curve.Center.y: end_angle *= -1 - edge = Part.ArcOfCircle(Part.Circle(edge.Curve.Center, FreeCAD.Vector(0,0,1), edge.Curve.Radius), start_angle, end_angle).toShape() + # Create new arc + new_edge = Part.ArcOfCircle(Part.Circle(center, FreeCAD.Vector(0,0,1), edge.Curve.Radius), start_angle, end_angle).toShape() + edges.append(new_edge) + + # Modify offset for inner angled faces + if radius_bottom < radius_top: + offset -= 2 * extraOffset + break else: # Line @@ -201,7 +237,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): for w in basewires: self.adjusted_basewires.append(w) - wire = PathOpTools.offsetWire(w, base.Shape, offset, True) #, obj.Side) + wire = PathOpTools.offsetWire(w, base.Shape, offset, True) if wire: wires.append(wire) @@ -214,6 +250,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): while z + obj.StepDown.Value < depth: z = z + obj.StepDown.Value zValues.append(z) + zValues.append(depth) PathLog.track(obj.Label, depth, zValues) @@ -250,5 +287,6 @@ def Create(name, obj=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) return obj diff --git a/src/Mod/Path/PathScripts/PathDeburrGui.py b/src/Mod/Path/PathScripts/PathDeburrGui.py index 1d4e0d8bf3..76e5f405e6 100644 --- a/src/Mod/Path/PathScripts/PathDeburrGui.py +++ b/src/Mod/Path/PathScripts/PathDeburrGui.py @@ -55,15 +55,6 @@ class TaskPanelBaseGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): return super(TaskPanelBaseGeometryPage, self) def addBaseGeometry(self, selection): - #for sel in selection: - #if sel.HasSubObjects: - # selectively add some elements of the drawing to the Base - #for sub in sel.SubObjects: - # if isinstance(sub, Part.Face): - # if sub.normalAt(0, 0) != FreeCAD.Vector(0, 0, 1): - # PathLog.info(translate("Path", "Ignoring non-horizontal Face")) - # return - self.super().addBaseGeometry(selection) diff --git a/src/Mod/Path/PathScripts/PathOpTools.py b/src/Mod/Path/PathScripts/PathOpTools.py index 862710bca5..de7c2e19fb 100644 --- a/src/Mod/Path/PathScripts/PathOpTools.py +++ b/src/Mod/Path/PathScripts/PathOpTools.py @@ -143,7 +143,7 @@ def orientWire(w, forward=True): PathLog.track('orientWire - ok') return wire -def offsetWire(wire, base, offset, forward):#, Side = None): +def offsetWire(wire, base, offset, forward): '''offsetWire(wire, base, offset, forward) ... offsets the wire away from base and orients the wire accordingly. The function tries to avoid most of the pitfalls of Part.makeOffset2D which is possible because all offsetting happens in the XY plane. @@ -158,14 +158,14 @@ def offsetWire(wire, base, offset, forward):#, Side = None): # https://www.freecadweb.org/wiki/Part%20Offset2D # it's easy to construct them manually though z = -1 if forward else 1 - edge = Part.makeCircle(curve.Radius + offset, curve.Center, FreeCAD.Vector(0, 0, z)) - if base.isInside(edge.Vertexes[0].Point, offset/2, True): + new_edge = Part.makeCircle(curve.Radius + offset, curve.Center, FreeCAD.Vector(0, 0, z)) + if base.isInside(new_edge.Vertexes[0].Point, offset/2, True): if offset > curve.Radius or PathGeom.isRoughly(offset, curve.Radius): # offsetting a hole by its own radius (or more) makes the hole vanish return None - edge = Part.makeCircle(curve.Radius - offset, curve.Center, FreeCAD.Vector(0, 0, -z)) - w = Part.Wire([edge]) - return w + new_edge = Part.makeCircle(curve.Radius - offset, curve.Center, FreeCAD.Vector(0, 0, -z)) + + return Part.Wire([new_edge]) if Part.Circle == type(curve) and not wire.isClosed(): # Process arc segment @@ -173,17 +173,21 @@ def offsetWire(wire, base, offset, forward):#, Side = None): l1 = math.sqrt((edge.Vertexes[0].Point.x - curve.Center.x)**2 + (edge.Vertexes[0].Point.y - curve.Center.y)**2) l2 = math.sqrt((edge.Vertexes[1].Point.x - curve.Center.x)**2 + (edge.Vertexes[1].Point.y - curve.Center.y)**2) + # Calculate angles based on x-axis (0 - PI/2) start_angle = math.acos((edge.Vertexes[0].Point.x - curve.Center.x) / l1) end_angle = math.acos((edge.Vertexes[1].Point.x - curve.Center.x) / l2) + # Angles are based on x-axis (Mirrored on x-axis) -> negative y value means negative angle if edge.Vertexes[0].Point.y < curve.Center.y: start_angle *= -1 if edge.Vertexes[1].Point.y < curve.Center.y: end_angle *= -1 + # Inside / Outside if base.isInside(edge.Vertexes[0].Point, offset/2, True): offset *= -1 + # Create new arc edge = Part.ArcOfCircle(Part.Circle(curve.Center, FreeCAD.Vector(0,0,1), curve.Radius+offset), start_angle, end_angle).toShape() return Part.Wire([edge]) From f79075110a59cce1a5aa462ba0c44599024b7fcb Mon Sep 17 00:00:00 2001 From: Patrick F Date: Sat, 13 Feb 2021 00:33:25 +0100 Subject: [PATCH 03/12] [PATH] Added basic support for arcs in deburr op --- src/Mod/Path/PathScripts/PathDeburr.py | 56 ++++++++++++++++++++--- src/Mod/Path/PathScripts/PathDeburrGui.py | 14 +++--- src/Mod/Path/PathScripts/PathOpTools.py | 22 +++++++++ src/Mod/Path/PathScripts/PathSelection.py | 3 +- 4 files changed, 80 insertions(+), 15 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDeburr.py b/src/Mod/Path/PathScripts/PathDeburr.py index ba530dc366..c540811765 100644 --- a/src/Mod/Path/PathScripts/PathDeburr.py +++ b/src/Mod/Path/PathScripts/PathDeburr.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # *************************************************************************** # * Copyright (c) 2018 sliptonic * -# * Copyright (c) 2020 Schildkroet * +# * Copyright (c) 2020-2021 Schildkroet * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * @@ -93,7 +93,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): '''Proxy class for Deburr operation.''' def opFeatures(self, obj): - return PathOp.FeatureTool | PathOp.FeatureHeights | PathOp.FeatureStepDown | PathOp.FeatureBaseEdges | PathOp.FeatureBaseFaces | PathOp.FeatureCoolant + return PathOp.FeatureTool | PathOp.FeatureHeights | PathOp.FeatureStepDown | PathOp.FeatureBaseEdges | PathOp.FeatureBaseFaces | PathOp.FeatureCoolant | PathOp.FeatureBaseGeometry def initOperation(self, obj): PathLog.track(obj.Label) @@ -139,14 +139,59 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): for base, subs in obj.Base: edges = [] basewires = [] + max_h = -99999 + for f in subs: sub = base.Shape.getElement(f) - if type(sub) == Part.Edge: + + if type(sub) == Part.Edge: # Edge edges.append(sub) + + elif type(sub) == Part.Face and sub.normalAt(0, 0) != FreeCAD.Vector(0, 0, 1): # Angled face + # Find z value of upper edge + for edge in sub.Edges: + for p0 in edge.Vertexes: + if p0.Point.z > max_h: + max_h = p0.Point.z + + # Search for lower edge and raise it to height of upper edge + for edge in sub.Edges: + if Part.Circle == type(edge.Curve): # Edge is a circle + if edge.Vertexes[0].Point.z < max_h: + + if edge.Closed: # Circle + v = FreeCAD.Vector(edge.Curve.Center.x, edge.Curve.Center.y, max_h) + new_edge = Part.makeCircle(edge.Curve.Radius, v, FreeCAD.Vector(0, 0, 1)) + edges.append(new_edge) + break + else: # Arc + if edge.Vertexes[0].Point.z == edge.Vertexes[1].Point.z: + l1 = math.sqrt((edge.Vertexes[0].Point.x - edge.Curve.Center.x)**2 + (edge.Vertexes[0].Point.y - edge.Curve.Center.y)**2) + l2 = math.sqrt((edge.Vertexes[1].Point.x - edge.Curve.Center.x)**2 + (edge.Vertexes[1].Point.y - edge.Curve.Center.y)**2) + + start_angle = math.acos((edge.Vertexes[0].Point.x - edge.Curve.Center.x) / l1) + end_angle = math.acos((edge.Vertexes[1].Point.x - edge.Curve.Center.x) / l2) + + if edge.Vertexes[0].Point.y < edge.Curve.Center.y: + start_angle *= -1 + if edge.Vertexes[1].Point.y < edge.Curve.Center.y: + end_angle *= -1 + + edge = Part.ArcOfCircle(Part.Circle(edge.Curve.Center, FreeCAD.Vector(0,0,1), edge.Curve.Radius), start_angle, end_angle).toShape() + break + + else: # Line + if edge.Vertexes[0].Point.z == edge.Vertexes[1].Point.z and edge.Vertexes[0].Point.z < max_h: + new_edge = Part.Edge(Part.LineSegment(FreeCAD.Vector(edge.Vertexes[0].Point.x, edge.Vertexes[0].Point.y, max_h), FreeCAD.Vector(edge.Vertexes[1].Point.x, edge.Vertexes[1].Point.y, max_h))) + edges.append(new_edge) + + elif sub.Wires: basewires.extend(sub.Wires) - else: + + else: # Flat face basewires.append(Part.Wire(sub.Edges)) + self.edges = edges # pylint: disable=attribute-defined-outside-init for edgelist in Part.sortEdges(edges): basewires.append(Part.Wire(edgelist)) @@ -160,9 +205,6 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): if wire: wires.append(wire) - # # Save Outside or Inside - # obj.Side = side[0] - # Set direction of op forward = (obj.Direction == 'CW') diff --git a/src/Mod/Path/PathScripts/PathDeburrGui.py b/src/Mod/Path/PathScripts/PathDeburrGui.py index d98642f4dd..1d4e0d8bf3 100644 --- a/src/Mod/Path/PathScripts/PathDeburrGui.py +++ b/src/Mod/Path/PathScripts/PathDeburrGui.py @@ -55,14 +55,14 @@ class TaskPanelBaseGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): return super(TaskPanelBaseGeometryPage, self) def addBaseGeometry(self, selection): - for sel in selection: - if sel.HasSubObjects: + #for sel in selection: + #if sel.HasSubObjects: # selectively add some elements of the drawing to the Base - for sub in sel.SubObjects: - if isinstance(sub, Part.Face): - if sub.normalAt(0, 0) != FreeCAD.Vector(0, 0, 1): - PathLog.info(translate("Path", "Ignoring non-horizontal Face")) - return + #for sub in sel.SubObjects: + # if isinstance(sub, Part.Face): + # if sub.normalAt(0, 0) != FreeCAD.Vector(0, 0, 1): + # PathLog.info(translate("Path", "Ignoring non-horizontal Face")) + # return self.super().addBaseGeometry(selection) diff --git a/src/Mod/Path/PathScripts/PathOpTools.py b/src/Mod/Path/PathScripts/PathOpTools.py index 5bdb3a9ba4..862710bca5 100644 --- a/src/Mod/Path/PathScripts/PathOpTools.py +++ b/src/Mod/Path/PathScripts/PathOpTools.py @@ -166,6 +166,28 @@ def offsetWire(wire, base, offset, forward):#, Side = None): edge = Part.makeCircle(curve.Radius - offset, curve.Center, FreeCAD.Vector(0, 0, -z)) w = Part.Wire([edge]) return w + + if Part.Circle == type(curve) and not wire.isClosed(): + # Process arc segment + z = -1 if forward else 1 + l1 = math.sqrt((edge.Vertexes[0].Point.x - curve.Center.x)**2 + (edge.Vertexes[0].Point.y - curve.Center.y)**2) + l2 = math.sqrt((edge.Vertexes[1].Point.x - curve.Center.x)**2 + (edge.Vertexes[1].Point.y - curve.Center.y)**2) + + start_angle = math.acos((edge.Vertexes[0].Point.x - curve.Center.x) / l1) + end_angle = math.acos((edge.Vertexes[1].Point.x - curve.Center.x) / l2) + + if edge.Vertexes[0].Point.y < curve.Center.y: + start_angle *= -1 + if edge.Vertexes[1].Point.y < curve.Center.y: + end_angle *= -1 + + if base.isInside(edge.Vertexes[0].Point, offset/2, True): + offset *= -1 + + edge = Part.ArcOfCircle(Part.Circle(curve.Center, FreeCAD.Vector(0,0,1), curve.Radius+offset), start_angle, end_angle).toShape() + + return Part.Wire([edge]) + if Part.Line == type(curve) or Part.LineSegment == type(curve): # offsetting a single edge doesn't work because there is an infinite # possible planes into which the edge could be offset diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py index 055bfb1757..8f0725407b 100644 --- a/src/Mod/Path/PathScripts/PathSelection.py +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # *************************************************************************** # * Copyright (c) 2015 Dan Falck * +# * Copyright (c) 2021 Schildkroet * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * @@ -115,7 +116,7 @@ class CHAMFERGate(PathBaseGate): subShape = shape.getElement(sub) if subShape.ShapeType == 'Edge': return True - elif (subShape.ShapeType == 'Face' and subShape.normalAt(0, 0) == FreeCAD.Vector(0, 0, 1)): + elif (subShape.ShapeType == 'Face'): return True return False From c821b09c818d08c7eed2389eb96a4099c5603392 Mon Sep 17 00:00:00 2001 From: Patrick F Date: Sat, 13 Feb 2021 15:41:37 +0100 Subject: [PATCH 04/12] [PATH] Improved deburr --- src/Mod/Path/PathScripts/PathDeburr.py | 50 ++++++++++++++++++++--- src/Mod/Path/PathScripts/PathDeburrGui.py | 9 ---- src/Mod/Path/PathScripts/PathOpTools.py | 16 +++++--- 3 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDeburr.py b/src/Mod/Path/PathScripts/PathDeburr.py index c540811765..2dac9b36e3 100644 --- a/src/Mod/Path/PathScripts/PathDeburr.py +++ b/src/Mod/Path/PathScripts/PathDeburr.py @@ -86,7 +86,7 @@ def toolDepthAndOffset(width, extraDepth, tool, printInfo): extraOffset = -width if angle == 180 else (extraDepth / tan) offset = toolOffset + extraOffset - return (depth, offset, suppressInfo) + return (depth, offset, extraOffset, suppressInfo) class ObjectDeburr(PathEngraveBase.ObjectOp): @@ -123,7 +123,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): if not hasattr(self, 'printInfo'): self.printInfo = True try: - (depth, offset, suppressInfo) = toolDepthAndOffset(obj.Width.Value, obj.ExtraDepth.Value, self.tool, self.printInfo) + (depth, offset, extraOffset, suppressInfo) = toolDepthAndOffset(obj.Width.Value, obj.ExtraDepth.Value, self.tool, self.printInfo) self.printInfo = not suppressInfo except ValueError as e: msg = "{} \n No path will be generated".format(e) @@ -136,10 +136,13 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): self.basewires = [] # pylint: disable=attribute-defined-outside-init self.adjusted_basewires = [] # pylint: disable=attribute-defined-outside-init wires = [] + for base, subs in obj.Base: edges = [] basewires = [] max_h = -99999 + radius_top = 0 + radius_bottom = 0 for f in subs: sub = base.Shape.getElement(f) @@ -148,11 +151,24 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): edges.append(sub) elif type(sub) == Part.Face and sub.normalAt(0, 0) != FreeCAD.Vector(0, 0, 1): # Angled face + # If an angled face is selected, the lower edge is projected to the height of the upper edge, + # to simulate an edge + # Find z value of upper edge for edge in sub.Edges: for p0 in edge.Vertexes: if p0.Point.z > max_h: max_h = p0.Point.z + + # Find biggest radius for top/bottom + for edge in sub.Edges: + if Part.Circle == type(edge.Curve): + if edge.Vertexes[0].Point.z == max_h: + if edge.Curve.Radius > radius_top: + radius_top = edge.Curve.Radius + else: + if edge.Curve.Radius > radius_bottom: + radius_bottom = edge.Curve.Radius # Search for lower edge and raise it to height of upper edge for edge in sub.Edges: @@ -160,24 +176,44 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): if edge.Vertexes[0].Point.z < max_h: if edge.Closed: # Circle - v = FreeCAD.Vector(edge.Curve.Center.x, edge.Curve.Center.y, max_h) - new_edge = Part.makeCircle(edge.Curve.Radius, v, FreeCAD.Vector(0, 0, 1)) + # New center + center = FreeCAD.Vector(edge.Curve.Center.x, edge.Curve.Center.y, max_h) + new_edge = Part.makeCircle(edge.Curve.Radius, center, FreeCAD.Vector(0, 0, 1)) edges.append(new_edge) + + # Modify offset for inner angled faces + if radius_bottom < radius_top: + offset -= 2 * extraOffset + break + else: # Arc if edge.Vertexes[0].Point.z == edge.Vertexes[1].Point.z: + # Arc vertexes are on same layer l1 = math.sqrt((edge.Vertexes[0].Point.x - edge.Curve.Center.x)**2 + (edge.Vertexes[0].Point.y - edge.Curve.Center.y)**2) l2 = math.sqrt((edge.Vertexes[1].Point.x - edge.Curve.Center.x)**2 + (edge.Vertexes[1].Point.y - edge.Curve.Center.y)**2) + # New center + center = FreeCAD.Vector(edge.Curve.Center.x, edge.Curve.Center.y, max_h) + + # Calculate angles based on x-axis (0 - PI/2) start_angle = math.acos((edge.Vertexes[0].Point.x - edge.Curve.Center.x) / l1) end_angle = math.acos((edge.Vertexes[1].Point.x - edge.Curve.Center.x) / l2) + # Angles are based on x-axis (Mirrored on x-axis) -> negative y value means negative angle if edge.Vertexes[0].Point.y < edge.Curve.Center.y: start_angle *= -1 if edge.Vertexes[1].Point.y < edge.Curve.Center.y: end_angle *= -1 - edge = Part.ArcOfCircle(Part.Circle(edge.Curve.Center, FreeCAD.Vector(0,0,1), edge.Curve.Radius), start_angle, end_angle).toShape() + # Create new arc + new_edge = Part.ArcOfCircle(Part.Circle(center, FreeCAD.Vector(0,0,1), edge.Curve.Radius), start_angle, end_angle).toShape() + edges.append(new_edge) + + # Modify offset for inner angled faces + if radius_bottom < radius_top: + offset -= 2 * extraOffset + break else: # Line @@ -201,7 +237,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): for w in basewires: self.adjusted_basewires.append(w) - wire = PathOpTools.offsetWire(w, base.Shape, offset, True) #, obj.Side) + wire = PathOpTools.offsetWire(w, base.Shape, offset, True) if wire: wires.append(wire) @@ -214,6 +250,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): while z + obj.StepDown.Value < depth: z = z + obj.StepDown.Value zValues.append(z) + zValues.append(depth) PathLog.track(obj.Label, depth, zValues) @@ -250,5 +287,6 @@ def Create(name, obj=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) return obj diff --git a/src/Mod/Path/PathScripts/PathDeburrGui.py b/src/Mod/Path/PathScripts/PathDeburrGui.py index 1d4e0d8bf3..76e5f405e6 100644 --- a/src/Mod/Path/PathScripts/PathDeburrGui.py +++ b/src/Mod/Path/PathScripts/PathDeburrGui.py @@ -55,15 +55,6 @@ class TaskPanelBaseGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): return super(TaskPanelBaseGeometryPage, self) def addBaseGeometry(self, selection): - #for sel in selection: - #if sel.HasSubObjects: - # selectively add some elements of the drawing to the Base - #for sub in sel.SubObjects: - # if isinstance(sub, Part.Face): - # if sub.normalAt(0, 0) != FreeCAD.Vector(0, 0, 1): - # PathLog.info(translate("Path", "Ignoring non-horizontal Face")) - # return - self.super().addBaseGeometry(selection) diff --git a/src/Mod/Path/PathScripts/PathOpTools.py b/src/Mod/Path/PathScripts/PathOpTools.py index 862710bca5..de7c2e19fb 100644 --- a/src/Mod/Path/PathScripts/PathOpTools.py +++ b/src/Mod/Path/PathScripts/PathOpTools.py @@ -143,7 +143,7 @@ def orientWire(w, forward=True): PathLog.track('orientWire - ok') return wire -def offsetWire(wire, base, offset, forward):#, Side = None): +def offsetWire(wire, base, offset, forward): '''offsetWire(wire, base, offset, forward) ... offsets the wire away from base and orients the wire accordingly. The function tries to avoid most of the pitfalls of Part.makeOffset2D which is possible because all offsetting happens in the XY plane. @@ -158,14 +158,14 @@ def offsetWire(wire, base, offset, forward):#, Side = None): # https://www.freecadweb.org/wiki/Part%20Offset2D # it's easy to construct them manually though z = -1 if forward else 1 - edge = Part.makeCircle(curve.Radius + offset, curve.Center, FreeCAD.Vector(0, 0, z)) - if base.isInside(edge.Vertexes[0].Point, offset/2, True): + new_edge = Part.makeCircle(curve.Radius + offset, curve.Center, FreeCAD.Vector(0, 0, z)) + if base.isInside(new_edge.Vertexes[0].Point, offset/2, True): if offset > curve.Radius or PathGeom.isRoughly(offset, curve.Radius): # offsetting a hole by its own radius (or more) makes the hole vanish return None - edge = Part.makeCircle(curve.Radius - offset, curve.Center, FreeCAD.Vector(0, 0, -z)) - w = Part.Wire([edge]) - return w + new_edge = Part.makeCircle(curve.Radius - offset, curve.Center, FreeCAD.Vector(0, 0, -z)) + + return Part.Wire([new_edge]) if Part.Circle == type(curve) and not wire.isClosed(): # Process arc segment @@ -173,17 +173,21 @@ def offsetWire(wire, base, offset, forward):#, Side = None): l1 = math.sqrt((edge.Vertexes[0].Point.x - curve.Center.x)**2 + (edge.Vertexes[0].Point.y - curve.Center.y)**2) l2 = math.sqrt((edge.Vertexes[1].Point.x - curve.Center.x)**2 + (edge.Vertexes[1].Point.y - curve.Center.y)**2) + # Calculate angles based on x-axis (0 - PI/2) start_angle = math.acos((edge.Vertexes[0].Point.x - curve.Center.x) / l1) end_angle = math.acos((edge.Vertexes[1].Point.x - curve.Center.x) / l2) + # Angles are based on x-axis (Mirrored on x-axis) -> negative y value means negative angle if edge.Vertexes[0].Point.y < curve.Center.y: start_angle *= -1 if edge.Vertexes[1].Point.y < curve.Center.y: end_angle *= -1 + # Inside / Outside if base.isInside(edge.Vertexes[0].Point, offset/2, True): offset *= -1 + # Create new arc edge = Part.ArcOfCircle(Part.Circle(curve.Center, FreeCAD.Vector(0,0,1), curve.Radius+offset), start_angle, end_angle).toShape() return Part.Wire([edge]) From 974c78b0904740c157dc6fc2cb6234317a02977f Mon Sep 17 00:00:00 2001 From: Patrick F Date: Sat, 13 Feb 2021 17:32:54 +0100 Subject: [PATCH 05/12] [PATH] Fixed leadinout problems with full circles and side detection --- src/Mod/Path/PathScripts/PathDeburr.py | 10 +++++++++- src/Mod/Path/PathScripts/PathDressupLeadInOut.py | 5 +++++ src/Mod/Path/PathScripts/PathGeom.py | 7 +++++-- src/Mod/Path/PathScripts/PathOpTools.py | 16 +++++++++++----- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDeburr.py b/src/Mod/Path/PathScripts/PathDeburr.py index 2dac9b36e3..fb5e969155 100644 --- a/src/Mod/Path/PathScripts/PathDeburr.py +++ b/src/Mod/Path/PathScripts/PathDeburr.py @@ -234,16 +234,24 @@ class ObjectDeburr(PathEngraveBase.ObjectOp): self.basewires.extend(basewires) + # Set default side + side = ["Outside"] for w in basewires: self.adjusted_basewires.append(w) - wire = PathOpTools.offsetWire(w, base.Shape, offset, True) + wire = PathOpTools.offsetWire(w, base.Shape, offset, True, side) if wire: wires.append(wire) # Set direction of op forward = (obj.Direction == 'CW') + # Set value of side + obj.Side = side[0] + # Check side extra for angled faces + if radius_top > radius_bottom: + obj.Side = "Inside" + zValues = [] z = 0 if obj.StepDown.Value != 0: diff --git a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py index 64fc18e1ac..137cf71ee7 100644 --- a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py +++ b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py @@ -113,6 +113,7 @@ class ObjectDressup: def getDirectionOfPath(self, obj): op = PathDressup.baseOp(obj.Base) + if hasattr(op, 'Side') and op.Side == 'Outside': if hasattr(op, 'Direction') and op.Direction == 'CW': return 'left' @@ -131,12 +132,16 @@ class ObjectDressup: return '' def normalize(self, Vector): + vx = 0 + vy = 0 + x = Vector.x y = Vector.y length = math.sqrt(x*x + y*y) if((math.fabs(length)) > 0.0000000000001): vx = round(x / length, 3) vy = round(y / length, 3) + return FreeCAD.Vector(vx, vy, 0) def invert(self, Vector): diff --git a/src/Mod/Path/PathScripts/PathGeom.py b/src/Mod/Path/PathScripts/PathGeom.py index 5c70b63701..21585435d3 100644 --- a/src/Mod/Path/PathScripts/PathGeom.py +++ b/src/Mod/Path/PathScripts/PathGeom.py @@ -350,8 +350,11 @@ def edgeForCmd(cmd, startPoint): PathLog.debug("StartPoint:{}".format(startPoint)) PathLog.debug("MidPoint:{}".format(midPoint)) PathLog.debug("EndPoint:{}".format(endPoint)) - - return Part.Edge(Part.Arc(startPoint, midPoint, endPoint)) + + if pointsCoincide(startPoint, endPoint, 0.001): + return Part.makeCircle(R, center, FreeCAD.Vector(0, 0, 1)) + else: + return Part.Edge(Part.Arc(startPoint, midPoint, endPoint)) # It's a Helix #print('angle: A=%.2f B=%.2f' % (getAngle(A)/math.pi, getAngle(B)/math.pi)) diff --git a/src/Mod/Path/PathScripts/PathOpTools.py b/src/Mod/Path/PathScripts/PathOpTools.py index de7c2e19fb..83cdb5c3e8 100644 --- a/src/Mod/Path/PathScripts/PathOpTools.py +++ b/src/Mod/Path/PathScripts/PathOpTools.py @@ -143,7 +143,7 @@ def orientWire(w, forward=True): PathLog.track('orientWire - ok') return wire -def offsetWire(wire, base, offset, forward): +def offsetWire(wire, base, offset, forward, Side = None): '''offsetWire(wire, base, offset, forward) ... offsets the wire away from base and orients the wire accordingly. The function tries to avoid most of the pitfalls of Part.makeOffset2D which is possible because all offsetting happens in the XY plane. @@ -163,6 +163,9 @@ def offsetWire(wire, base, offset, forward): if offset > curve.Radius or PathGeom.isRoughly(offset, curve.Radius): # offsetting a hole by its own radius (or more) makes the hole vanish return None + if Side: + Side[0] = "Inside" + print("inside") new_edge = Part.makeCircle(curve.Radius - offset, curve.Center, FreeCAD.Vector(0, 0, -z)) return Part.Wire([new_edge]) @@ -186,6 +189,9 @@ def offsetWire(wire, base, offset, forward): # Inside / Outside if base.isInside(edge.Vertexes[0].Point, offset/2, True): offset *= -1 + if Side: + print("inside") + Side[0] = "Inside" # Create new arc edge = Part.ArcOfCircle(Part.Circle(curve.Center, FreeCAD.Vector(0,0,1), curve.Radius+offset), start_angle, end_angle).toShape() @@ -223,12 +229,12 @@ def offsetWire(wire, base, offset, forward): if wire.isClosed(): if not base.isInside(owire.Edges[0].Vertexes[0].Point, offset/2, True): PathLog.track('closed - outside') - # if Side: - # Side[0] = "Outside" + if Side: + Side[0] = "Outside" return orientWire(owire, forward) PathLog.track('closed - inside') - # if Side: - # Side[0] = "Inside" + if Side: + Side[0] = "Inside" try: owire = wire.makeOffset2D(-offset) except Exception: # pylint: disable=broad-except From 25fa7c9be176a3b9111f377104a71dac845c0ee3 Mon Sep 17 00:00:00 2001 From: Patrick F Date: Sat, 13 Feb 2021 17:37:24 +0100 Subject: [PATCH 06/12] [PATH] Updated copyright --- src/Mod/Path/PathScripts/PathDressupLeadInOut.py | 4 ++-- src/Mod/Path/PathScripts/PathGeom.py | 1 + src/Mod/Path/PathScripts/PathOpTools.py | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py index 137cf71ee7..03f24a277f 100644 --- a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py +++ b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # *************************************************************************** # * Copyright (c) 2017 LTS under LGPL * -# * Copyright (c) 2020 Schildkroet * +# * Copyright (c) 2020-2021 Schildkroet * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * @@ -113,7 +113,7 @@ class ObjectDressup: def getDirectionOfPath(self, obj): op = PathDressup.baseOp(obj.Base) - + if hasattr(op, 'Side') and op.Side == 'Outside': if hasattr(op, 'Direction') and op.Direction == 'CW': return 'left' diff --git a/src/Mod/Path/PathScripts/PathGeom.py b/src/Mod/Path/PathScripts/PathGeom.py index 21585435d3..d4054f48a8 100644 --- a/src/Mod/Path/PathScripts/PathGeom.py +++ b/src/Mod/Path/PathScripts/PathGeom.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # *************************************************************************** # * Copyright (c) 2016 sliptonic * +# * Copyright (c) 2021 Schildkroet * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * diff --git a/src/Mod/Path/PathScripts/PathOpTools.py b/src/Mod/Path/PathScripts/PathOpTools.py index 83cdb5c3e8..f9e885a1f2 100644 --- a/src/Mod/Path/PathScripts/PathOpTools.py +++ b/src/Mod/Path/PathScripts/PathOpTools.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # *************************************************************************** # * Copyright (c) 2018 sliptonic * +# * Copyright (c) 2021 Schildkroet * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * From dec5b2185879358fc7499f141efbd5314f4a1c67 Mon Sep 17 00:00:00 2001 From: Patrick F Date: Sat, 13 Feb 2021 22:02:10 +0100 Subject: [PATCH 07/12] [PATH] Fixed tests --- src/Mod/Path/PathTests/TestPathDeburr.py | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Mod/Path/PathTests/TestPathDeburr.py b/src/Mod/Path/PathTests/TestPathDeburr.py index 901851143b..2dcf446018 100644 --- a/src/Mod/Path/PathTests/TestPathDeburr.py +++ b/src/Mod/Path/PathTests/TestPathDeburr.py @@ -38,7 +38,7 @@ class TestPathDeburr(PathTestUtils.PathTestBase): tool.FlatRadius = 0 tool.CuttingEdgeAngle = 180 - (depth, offset, info) = PathDeburr.toolDepthAndOffset(1, 0.01, tool, True) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0.01, tool, True) self.assertRoughly(0.01, depth) self.assertRoughly(9, offset) self.assertFalse(info) @@ -46,7 +46,7 @@ class TestPathDeburr(PathTestUtils.PathTestBase): # legacy tools - no problem, same result tool.CuttingEdgeAngle = 0 - (depth, offset, info) = PathDeburr.toolDepthAndOffset(1, 0.01, tool, True) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0.01, tool, True) self.assertRoughly(0.01, depth) self.assertRoughly(9, offset) self.assertFalse(info) @@ -57,12 +57,12 @@ class TestPathDeburr(PathTestUtils.PathTestBase): tool.FlatRadius = 0 tool.CuttingEdgeAngle = 90 - (depth, offset, info) = PathDeburr.toolDepthAndOffset(1, 0, tool, True) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0, tool, True) self.assertRoughly(1, depth) self.assertRoughly(0, offset) self.assertFalse(info) - (depth, offset, info) = PathDeburr.toolDepthAndOffset(1, 0.2, tool, True) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0.2, tool, True) self.assertRoughly(1.2, depth) self.assertRoughly(0.2, offset) self.assertFalse(info) @@ -73,12 +73,12 @@ class TestPathDeburr(PathTestUtils.PathTestBase): tool.FlatRadius = 0.3 tool.CuttingEdgeAngle = 90 - (depth, offset, info) = PathDeburr.toolDepthAndOffset(1, 0, tool, True) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0, tool, True) self.assertRoughly(1, depth) self.assertRoughly(0.3, offset) self.assertFalse(info) - (depth, offset, info) = PathDeburr.toolDepthAndOffset(2, 0.2, tool, True) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(2, 0.2, tool, True) self.assertRoughly(2.2, depth) self.assertRoughly(0.5, offset) self.assertFalse(info) @@ -91,12 +91,12 @@ class TestPathDeburr(PathTestUtils.PathTestBase): td = 1.73205 - (depth, offset, info) = PathDeburr.toolDepthAndOffset(1, 0, tool, True) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0, tool, True) self.assertRoughly(td, depth) self.assertRoughly(10, offset) self.assertFalse(info) - (depth, offset, info) = PathDeburr.toolDepthAndOffset(3, 1, tool, True) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(3, 1, tool, True) self.assertRoughly(td * 3 + 1, depth) self.assertRoughly(10 + td, offset) self.assertFalse(info) @@ -109,15 +109,15 @@ class TestPathDeburr(PathTestUtils.PathTestBase): self.Diameter = dia tool = FakeEndmill(10) - (depth, offset, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, True) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, True) self.assertRoughly(0.1, depth) self.assertRoughly(4, offset) self.assertTrue(info) - (depth, offset, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, not info) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, not info) self.assertRoughly(0.1, depth) self.assertRoughly(4, offset) self.assertTrue(info) - (depth, offset, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, not info) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, not info) self.assertRoughly(0.1, depth) self.assertRoughly(4, offset) self.assertTrue(info) @@ -131,15 +131,15 @@ class TestPathDeburr(PathTestUtils.PathTestBase): self.CuttingEdgeAngle = angle tool = FakePointyBit(10, 90) - (depth, offset, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, True) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, True) self.assertRoughly(1.1, depth) self.assertRoughly(0.1, offset) self.assertTrue(info) - (depth, offset, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, not info) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, not info) self.assertRoughly(1.1, depth) self.assertRoughly(0.1, offset) self.assertTrue(info) - (depth, offset, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, not info) + (depth, offset, __, info) = PathDeburr.toolDepthAndOffset(1, 0.1, tool, not info) self.assertRoughly(1.1, depth) self.assertRoughly(0.1, offset) self.assertTrue(info) From 6974ef8b9eb5eff9302aef68bef34b7c57ed19ea Mon Sep 17 00:00:00 2001 From: Patrick F Date: Sun, 21 Feb 2021 02:56:14 +0100 Subject: [PATCH 08/12] [PATH] offsetWire bugfixes --- src/Mod/Path/PathScripts/PathOpTools.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathOpTools.py b/src/Mod/Path/PathScripts/PathOpTools.py index f9e885a1f2..4f66953b02 100644 --- a/src/Mod/Path/PathScripts/PathOpTools.py +++ b/src/Mod/Path/PathScripts/PathOpTools.py @@ -186,16 +186,23 @@ def offsetWire(wire, base, offset, forward, Side = None): start_angle *= -1 if edge.Vertexes[1].Point.y < curve.Center.y: end_angle *= -1 + + if (edge.Vertexes[0].Point.x > curve.Center.x or edge.Vertexes[1].Point.x > curve.Center.x) and curve.AngleXU < 0: + tmp = start_angle + start_angle = end_angle + end_angle = tmp # Inside / Outside if base.isInside(edge.Vertexes[0].Point, offset/2, True): offset *= -1 if Side: - print("inside") Side[0] = "Inside" # Create new arc - edge = Part.ArcOfCircle(Part.Circle(curve.Center, FreeCAD.Vector(0,0,1), curve.Radius+offset), start_angle, end_angle).toShape() + if curve.AngleXU > 0: + edge = Part.ArcOfCircle(Part.Circle(curve.Center, FreeCAD.Vector(0,0,1), curve.Radius+offset), start_angle, end_angle).toShape() + else: + edge = Part.ArcOfCircle(Part.Circle(curve.Center, FreeCAD.Vector(0,0,1), curve.Radius-offset), start_angle, end_angle).toShape() return Part.Wire([edge]) From 166f5e7a4b76c6a1de7e9fe2ddcbde169339c34b Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Mon, 19 Jul 2021 07:49:04 +0200 Subject: [PATCH 09/12] FEM: material task panel, raise max possible young's modul value --- src/Mod/Fem/Gui/Resources/ui/Material.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Fem/Gui/Resources/ui/Material.ui b/src/Mod/Fem/Gui/Resources/ui/Material.ui index 818a4f2634..6c5ab1d64c 100755 --- a/src/Mod/Fem/Gui/Resources/ui/Material.ui +++ b/src/Mod/Fem/Gui/Resources/ui/Material.ui @@ -246,7 +246,7 @@ 2.000000000000000 - 2000000000.000000000000000 + 90000000000.000000000000000 Pa From a98ebfde6e71c03d76e96f6541e0ca5b9167bb61 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 20 Jul 2021 09:42:17 +0200 Subject: [PATCH 10/12] FEM: mesh set getter, use ccx independent variable names --- src/Mod/Fem/femmesh/meshsetsgetter.py | 230 +++++++++++++------------- 1 file changed, 115 insertions(+), 115 deletions(-) diff --git a/src/Mod/Fem/femmesh/meshsetsgetter.py b/src/Mod/Fem/femmesh/meshsetsgetter.py index a0834f5d71..466d83dcfb 100644 --- a/src/Mod/Fem/femmesh/meshsetsgetter.py +++ b/src/Mod/Fem/femmesh/meshsetsgetter.py @@ -638,7 +638,7 @@ class MeshSetsGetter(): if self.member.geos_beamsection: # we will need to split the beam even for one beamobj # because no beam in z-direction can be used in ccx without a special adjustment - # thus they need an own ccx_elset + # thus they need an own matgeoset self.get_element_rotation1D_elements() # get the element ids for face and edge elements and write them into the objects @@ -726,15 +726,15 @@ class MeshSetsGetter(): {"short": beamrot_data["ShortName"]}, {"short": "D" + str(i)} ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["beamsection_obj"] = beamsec_obj + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + matgeoset["beamsection_obj"] = beamsec_obj # normal for this direction - ccx_elset["beam_normal"] = beamdirection["normal"] - self.mat_geo_sets.append(ccx_elset) + matgeoset["beam_normal"] = beamdirection["normal"] + self.mat_geo_sets.append(matgeoset) def get_mat_geo_sets_single_mat_multiple_beam(self): mat_obj = self.member.mats_linear[0]["Object"] @@ -753,15 +753,15 @@ class MeshSetsGetter(): {"short": beamrot_data["ShortName"]}, {"short": "D" + str(i)} ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["beamsection_obj"] = beamsec_obj + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + matgeoset["beamsection_obj"] = beamsec_obj # normal for this direction - ccx_elset["beam_normal"] = beamdirection["normal"] - self.mat_geo_sets.append(ccx_elset) + matgeoset["beam_normal"] = beamdirection["normal"] + self.mat_geo_sets.append(matgeoset) def get_mat_geo_sets_multiple_mat_single_beam(self): beamsec_obj = self.member.geos_beamsection[0]["Object"] @@ -779,15 +779,15 @@ class MeshSetsGetter(): {"short": beamrot_data["ShortName"]}, {"short": "D" + str(i)} ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["beamsection_obj"] = beamsec_obj + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + matgeoset["beamsection_obj"] = beamsec_obj # normal for this direction - ccx_elset["beam_normal"] = beamdirection["normal"] - self.mat_geo_sets.append(ccx_elset) + matgeoset["beam_normal"] = beamdirection["normal"] + self.mat_geo_sets.append(matgeoset) def get_mat_geo_sets_multiple_mat_multiple_beam(self): beamrot_data = self.member.geos_beamrotation[0] @@ -810,15 +810,15 @@ class MeshSetsGetter(): {"short": beamrot_data["ShortName"]}, {"short": "D" + str(i)} ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["beamsection_obj"] = beamsec_obj + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + matgeoset["beamsection_obj"] = beamsec_obj # normal for this direction - ccx_elset["beam_normal"] = beamdirection["normal"] - self.mat_geo_sets.append(ccx_elset) + matgeoset["beam_normal"] = beamdirection["normal"] + self.mat_geo_sets.append(matgeoset) # fluid def get_mat_geo_sets_single_mat_single_fluid(self): @@ -826,13 +826,13 @@ class MeshSetsGetter(): fluidsec_obj = self.member.geos_fluidsection[0]["Object"] elset_data = self.ccx_eedges names = [{"short": "M0"}, {"short": "F0"}] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["fluidsection_obj"] = fluidsec_obj - self.mat_geo_sets.append(ccx_elset) + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + matgeoset["fluidsection_obj"] = fluidsec_obj + self.mat_geo_sets.append(matgeoset) def get_mat_geo_sets_single_mat_multiple_fluid(self): mat_obj = self.member.mats_linear[0]["Object"] @@ -840,13 +840,13 @@ class MeshSetsGetter(): fluidsec_obj = fluidsec_data["Object"] elset_data = fluidsec_data["FEMElements"] names = [{"short": "M0"}, {"short": fluidsec_data["ShortName"]}] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["fluidsection_obj"] = fluidsec_obj - self.mat_geo_sets.append(ccx_elset) + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + matgeoset["fluidsection_obj"] = fluidsec_obj + self.mat_geo_sets.append(matgeoset) def get_mat_geo_sets_multiple_mat_single_fluid(self): fluidsec_obj = self.member.geos_fluidsection[0]["Object"] @@ -854,13 +854,13 @@ class MeshSetsGetter(): mat_obj = mat_data["Object"] elset_data = mat_data["FEMElements"] names = [{"short": mat_data["ShortName"]}, {"short": "F0"}] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["fluidsection_obj"] = fluidsec_obj - self.mat_geo_sets.append(ccx_elset) + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + matgeoset["fluidsection_obj"] = fluidsec_obj + self.mat_geo_sets.append(matgeoset) def get_mat_geo_sets_multiple_mat_multiple_fluid(self): for fluidsec_data in self.member.geos_fluidsection: @@ -876,13 +876,13 @@ class MeshSetsGetter(): {"short": mat_data["ShortName"]}, {"short": fluidsec_data["ShortName"]} ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_short(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["fluidsection_obj"] = fluidsec_obj - self.mat_geo_sets.append(ccx_elset) + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + matgeoset["fluidsection_obj"] = fluidsec_obj + self.mat_geo_sets.append(matgeoset) # shell def get_mat_geo_sets_single_mat_single_shell(self): @@ -893,13 +893,13 @@ class MeshSetsGetter(): {"long": mat_obj.Name, "short": "M0"}, {"long": shellth_obj.Name, "short": "S0"} ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["shellthickness_obj"] = shellth_obj - self.mat_geo_sets.append(ccx_elset) + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + matgeoset["shellthickness_obj"] = shellth_obj + self.mat_geo_sets.append(matgeoset) def get_mat_geo_sets_single_mat_multiple_shell(self): mat_obj = self.member.mats_linear[0]["Object"] @@ -910,13 +910,13 @@ class MeshSetsGetter(): {"long": mat_obj.Name, "short": "M0"}, {"long": shellth_obj.Name, "short": shellth_data["ShortName"]} ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["shellthickness_obj"] = shellth_obj - self.mat_geo_sets.append(ccx_elset) + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + matgeoset["shellthickness_obj"] = shellth_obj + self.mat_geo_sets.append(matgeoset) def get_mat_geo_sets_multiple_mat_single_shell(self): shellth_obj = self.member.geos_shellthickness[0]["Object"] @@ -927,13 +927,13 @@ class MeshSetsGetter(): {"long": mat_obj.Name, "short": mat_data["ShortName"]}, {"long": shellth_obj.Name, "short": "S0"} ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["shellthickness_obj"] = shellth_obj - self.mat_geo_sets.append(ccx_elset) + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + matgeoset["shellthickness_obj"] = shellth_obj + self.mat_geo_sets.append(matgeoset) def get_mat_geo_sets_multiple_mat_multiple_shell(self): for shellth_data in self.member.geos_shellthickness: @@ -949,13 +949,13 @@ class MeshSetsGetter(): {"long": mat_obj.Name, "short": mat_data["ShortName"]}, {"long": shellth_obj.Name, "short": shellth_data["ShortName"]} ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - ccx_elset["shellthickness_obj"] = shellth_obj - self.mat_geo_sets.append(ccx_elset) + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + matgeoset["shellthickness_obj"] = shellth_obj + self.mat_geo_sets.append(matgeoset) # solid def get_mat_geo_sets_single_mat_solid(self): @@ -965,12 +965,12 @@ class MeshSetsGetter(): {"long": mat_obj.Name, "short": "M0"}, {"long": "Solid", "short": "Solid"} ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - self.mat_geo_sets.append(ccx_elset) + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + self.mat_geo_sets.append(matgeoset) print(self.mat_geo_sets) def get_mat_geo_sets_multiple_mat_solid(self): @@ -981,12 +981,12 @@ class MeshSetsGetter(): {"long": mat_obj.Name, "short": mat_data["ShortName"]}, {"long": "Solid", "short": "Solid"} ] - ccx_elset = {} - ccx_elset["ccx_elset"] = elset_data - ccx_elset["ccx_elset_name"] = get_ccx_elset_name_standard(names) - ccx_elset["mat_obj_name"] = mat_obj.Name - ccx_elset["ccx_mat_name"] = mat_obj.Material["Name"] - self.mat_geo_sets.append(ccx_elset) + matgeoset = {} + matgeoset["ccx_elset"] = elset_data + matgeoset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + matgeoset["mat_obj_name"] = mat_obj.Name + matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] + self.mat_geo_sets.append(matgeoset) # ************************************************************************************************ @@ -1005,38 +1005,38 @@ class MeshSetsGetter(): def get_ccx_elset_name_standard(names): # standard max length = 80 - ccx_elset_name = "" + elset_name = "" for name in names: - ccx_elset_name += name["long"] - if len(ccx_elset_name) < 81: - return ccx_elset_name + elset_name += name["long"] + if len(elset_name) < 81: + return elset_name else: - ccx_elset_name = "" + elset_name = "" for name in names: - ccx_elset_name += name["short"] - if len(ccx_elset_name) < 81: - return ccx_elset_name + elset_name += name["short"] + if len(elset_name) < 81: + return elset_name else: error = ( "FEM: Trouble in ccx input file, because an " "elset name is longer than 80 character! {}\n" - .format(ccx_elset_name) + .format(elset_name) ) raise Exception(error) def get_ccx_elset_name_short(names): # restricted max length = 20 (beam elsets) - ccx_elset_name = "" + elset_name = "" for name in names: - ccx_elset_name += name["short"] - if len(ccx_elset_name) < 21: - return ccx_elset_name + elset_name += name["short"] + if len(elset_name) < 21: + return elset_name else: error = ( "FEM: Trouble in ccx input file, because an" "beam elset name is longer than 20 characters! {}\n" - .format(ccx_elset_name) + .format(elset_name) ) raise Exception(error) From f4275adfedfe0b500c38f400f6424e0be64d1d2b Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 20 Jul 2021 09:45:38 +0200 Subject: [PATCH 11/12] FEM: mesh set getter, use ccx independent method names --- src/Mod/Fem/femmesh/meshsetsgetter.py | 41 ++++++++++++++------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/Mod/Fem/femmesh/meshsetsgetter.py b/src/Mod/Fem/femmesh/meshsetsgetter.py index 466d83dcfb..8cc97b6487 100644 --- a/src/Mod/Fem/femmesh/meshsetsgetter.py +++ b/src/Mod/Fem/femmesh/meshsetsgetter.py @@ -728,7 +728,7 @@ class MeshSetsGetter(): ] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["ccx_elset_name"] = get_elset_name_short(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] matgeoset["beamsection_obj"] = beamsec_obj @@ -755,7 +755,7 @@ class MeshSetsGetter(): ] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["ccx_elset_name"] = get_elset_name_short(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] matgeoset["beamsection_obj"] = beamsec_obj @@ -781,7 +781,7 @@ class MeshSetsGetter(): ] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["ccx_elset_name"] = get_elset_name_short(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] matgeoset["beamsection_obj"] = beamsec_obj @@ -812,7 +812,7 @@ class MeshSetsGetter(): ] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["ccx_elset_name"] = get_elset_name_short(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] matgeoset["beamsection_obj"] = beamsec_obj @@ -828,7 +828,7 @@ class MeshSetsGetter(): names = [{"short": "M0"}, {"short": "F0"}] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["ccx_elset_name"] = get_elset_name_short(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] matgeoset["fluidsection_obj"] = fluidsec_obj @@ -842,7 +842,7 @@ class MeshSetsGetter(): names = [{"short": "M0"}, {"short": fluidsec_data["ShortName"]}] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["ccx_elset_name"] = get_elset_name_short(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] matgeoset["fluidsection_obj"] = fluidsec_obj @@ -856,7 +856,7 @@ class MeshSetsGetter(): names = [{"short": mat_data["ShortName"]}, {"short": "F0"}] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["ccx_elset_name"] = get_elset_name_short(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] matgeoset["fluidsection_obj"] = fluidsec_obj @@ -878,7 +878,7 @@ class MeshSetsGetter(): ] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_short(names) + matgeoset["ccx_elset_name"] = get_elset_name_short(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] matgeoset["fluidsection_obj"] = fluidsec_obj @@ -895,7 +895,7 @@ class MeshSetsGetter(): ] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + matgeoset["ccx_elset_name"] = get_elset_name_standard(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] matgeoset["shellthickness_obj"] = shellth_obj @@ -912,7 +912,7 @@ class MeshSetsGetter(): ] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + matgeoset["ccx_elset_name"] = get_elset_name_standard(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] matgeoset["shellthickness_obj"] = shellth_obj @@ -929,7 +929,7 @@ class MeshSetsGetter(): ] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + matgeoset["ccx_elset_name"] = get_elset_name_standard(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] matgeoset["shellthickness_obj"] = shellth_obj @@ -951,7 +951,7 @@ class MeshSetsGetter(): ] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + matgeoset["ccx_elset_name"] = get_elset_name_standard(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] matgeoset["shellthickness_obj"] = shellth_obj @@ -967,7 +967,7 @@ class MeshSetsGetter(): ] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + matgeoset["ccx_elset_name"] = get_elset_name_standard(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] self.mat_geo_sets.append(matgeoset) @@ -983,7 +983,7 @@ class MeshSetsGetter(): ] matgeoset = {} matgeoset["ccx_elset"] = elset_data - matgeoset["ccx_elset_name"] = get_ccx_elset_name_standard(names) + matgeoset["ccx_elset_name"] = get_elset_name_standard(names) matgeoset["mat_obj_name"] = mat_obj.Name matgeoset["ccx_mat_name"] = mat_obj.Material["Name"] self.mat_geo_sets.append(matgeoset) @@ -1003,7 +1003,7 @@ class MeshSetsGetter(): # TODO write comment into input file to elset ids and elset attributes -def get_ccx_elset_name_standard(names): +def get_elset_name_standard(names): # standard max length = 80 elset_name = "" for name in names: @@ -1018,15 +1018,16 @@ def get_ccx_elset_name_standard(names): return elset_name else: error = ( - "FEM: Trouble in ccx input file, because an " + "FEM: Trouble in elset name, because an " "elset name is longer than 80 character! {}\n" .format(elset_name) ) raise Exception(error) -def get_ccx_elset_name_short(names): - # restricted max length = 20 (beam elsets) +def get_elset_name_short(names): + # restricted max length = 20 (elsets) + # in CalculiX solver input this is needed for beam elsets elset_name = "" for name in names: elset_name += name["short"] @@ -1034,8 +1035,8 @@ def get_ccx_elset_name_short(names): return elset_name else: error = ( - "FEM: Trouble in ccx input file, because an" - "beam elset name is longer than 20 characters! {}\n" + "FEM: Trouble in elset name, because an" + "short elset name is longer than 20 characters! {}\n" .format(elset_name) ) raise Exception(error) From 31fd5be0c48fa020aba9b283db38519004ce04ca Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 20 Jul 2021 09:53:22 +0200 Subject: [PATCH 12/12] FEM: calculix writer, use generic elset variable names --- .../calculix/write_constraint_fluidsection.py | 28 +++++++++---------- .../calculix/write_femelement_geometry.py | 22 +++++++-------- .../calculix/write_femelement_matgeosets.py | 10 +++---- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/Mod/Fem/femsolver/calculix/write_constraint_fluidsection.py b/src/Mod/Fem/femsolver/calculix/write_constraint_fluidsection.py index 1c07af5e5e..b5819bcbd7 100644 --- a/src/Mod/Fem/femsolver/calculix/write_constraint_fluidsection.py +++ b/src/Mod/Fem/femsolver/calculix/write_constraint_fluidsection.py @@ -48,7 +48,7 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter): # Fluid sections: # fluidsection Liquid inlet outlet objs requires special element definition - # to fill ccxwriter.FluidInletoutlet_ele list the ccx_elset are needed + # to fill ccxwriter.FluidInletoutlet_ele list the mat_geo_sets are needed # thus this has to be after the creation of mat_geo_sets # different pipe cross sections will generate mat_geo_sets @@ -58,14 +58,14 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter): "{}_inout_nodes.txt".format(ccxwriter.mesh_name) ) - def get_fluidsection_inoutlet_obj_if_setdata(ccx_elset): + def get_fluidsection_inoutlet_obj_if_setdata(matgeoset): if ( - ccx_elset["ccx_elset"] + matgeoset["ccx_elset"] # use six to be sure to be Python 2.7 and 3.x compatible - and not isinstance(ccx_elset["ccx_elset"], six.string_types) - and "fluidsection_obj" in ccx_elset # fluid mesh + and not isinstance(matgeoset["ccx_elset"], six.string_types) + and "fluidsection_obj" in matgeoset # fluid mesh ): - fluidsec_obj = ccx_elset["fluidsection_obj"] + fluidsec_obj = matgeoset["fluidsection_obj"] if ( fluidsec_obj.SectionType == "Liquid" and ( @@ -77,12 +77,12 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter): return None def is_fluidsection_inoutlet_setnames_possible(mat_geo_sets): - for ccx_elset in mat_geo_sets: + for matgeoset in mat_geo_sets: if ( - ccx_elset["ccx_elset"] - and "fluidsection_obj" in ccx_elset # fluid mesh + matgeoset["ccx_elset"] + and "fluidsection_obj" in matgeoset # fluid mesh ): - fluidsec_obj = ccx_elset["fluidsection_obj"] + fluidsec_obj = matgeoset["fluidsection_obj"] if ( fluidsec_obj.SectionType == "Liquid" and ( @@ -95,13 +95,13 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter): # collect elementIDs for fluidsection Liquid inlet outlet objs # if they have element data (happens if not "eall") - for ccx_elset in ccxwriter.mat_geo_sets: - fluidsec_obj = get_fluidsection_inoutlet_obj_if_setdata(ccx_elset) + for matgeoset in ccxwriter.mat_geo_sets: + fluidsec_obj = get_fluidsection_inoutlet_obj_if_setdata(matgeoset) if fluidsec_obj is None: continue elsetchanged = False counter = 0 - for elid in ccx_elset["ccx_elset"]: + for elid in matgeoset["ccx_elset"]: counter = counter + 1 if (elsetchanged is False) \ and (fluidsec_obj.LiquidSectionType == "PIPE INLET"): @@ -111,7 +111,7 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter): ) elsetchanged = True elif (fluidsec_obj.LiquidSectionType == "PIPE OUTLET") \ - and (counter == len(ccx_elset["ccx_elset"])): + and (counter == len(matgeoset["ccx_elset"])): # 3rd index is to track which line nr the element is defined ccxwriter.FluidInletoutlet_ele.append( [str(elid), fluidsec_obj.LiquidSectionType, 0] diff --git a/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py b/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py index e38b044ee1..5a695d9974 100644 --- a/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py +++ b/src/Mod/Fem/femsolver/calculix/write_femelement_geometry.py @@ -32,14 +32,14 @@ def write_femelement_geometry(f, ccxwriter): f.write("\n{}\n".format(59 * "*")) f.write("** Sections\n") - for ccx_elset in ccxwriter.mat_geo_sets: - if ccx_elset["ccx_elset"]: - elsetdef = "ELSET={}, ".format(ccx_elset["ccx_elset_name"]) - material = "MATERIAL={}".format(ccx_elset["mat_obj_name"]) + for matgeoset in ccxwriter.mat_geo_sets: + if matgeoset["ccx_elset"]: + elsetdef = "ELSET={}, ".format(matgeoset["ccx_elset_name"]) + material = "MATERIAL={}".format(matgeoset["mat_obj_name"]) - if "beamsection_obj"in ccx_elset: # beam mesh - beamsec_obj = ccx_elset["beamsection_obj"] - normal = ccx_elset["beam_normal"] + if "beamsection_obj"in matgeoset: # beam mesh + beamsec_obj = matgeoset["beamsection_obj"] + normal = matgeoset["beam_normal"] if beamsec_obj.SectionType == "Rectangular": height = beamsec_obj.RectHeight.getValueAs("mm").Value width = beamsec_obj.RectWidth.getValueAs("mm").Value @@ -79,8 +79,8 @@ def write_femelement_geometry(f, ccxwriter): f.write(section_def) f.write(section_geo) f.write(section_nor) - elif "fluidsection_obj"in ccx_elset: # fluid mesh - fluidsec_obj = ccx_elset["fluidsection_obj"] + elif "fluidsection_obj"in matgeoset: # fluid mesh + fluidsec_obj = matgeoset["fluidsection_obj"] if fluidsec_obj.SectionType == "Liquid": section_type = fluidsec_obj.LiquidSectionType if (section_type == "PIPE INLET") or (section_type == "PIPE OUTLET"): @@ -101,8 +101,8 @@ def write_femelement_geometry(f, ccxwriter): """ f.write(section_def) f.write(section_geo) - elif "shellthickness_obj"in ccx_elset: # shell mesh - shellth_obj = ccx_elset["shellthickness_obj"] + elif "shellthickness_obj"in matgeoset: # shell mesh + shellth_obj = matgeoset["shellthickness_obj"] section_def = "*SHELL SECTION, {}{}\n".format(elsetdef, material) thickness = shellth_obj.Thickness.getValueAs("mm").Value section_geo = "{:.13G}\n".format(thickness) diff --git a/src/Mod/Fem/femsolver/calculix/write_femelement_matgeosets.py b/src/Mod/Fem/femsolver/calculix/write_femelement_matgeosets.py index 01f0f6720f..daa27e6749 100644 --- a/src/Mod/Fem/femsolver/calculix/write_femelement_matgeosets.py +++ b/src/Mod/Fem/femsolver/calculix/write_femelement_matgeosets.py @@ -35,13 +35,13 @@ def write_femelement_matgeosets(f, ccxwriter): f.write("\n{}\n".format(59 * "*")) f.write("** Element sets for materials and FEM element type (solid, shell, beam, fluid)\n") - for ccx_elset in ccxwriter.mat_geo_sets: + for matgeoset in ccxwriter.mat_geo_sets: - f.write("*ELSET,ELSET={}\n".format(ccx_elset["ccx_elset_name"])) + f.write("*ELSET,ELSET={}\n".format(matgeoset["ccx_elset_name"])) # use six to be sure to be Python 2.7 and 3.x compatible - if isinstance(ccx_elset["ccx_elset"], six.string_types): - f.write("{}\n".format(ccx_elset["ccx_elset"])) + if isinstance(matgeoset["ccx_elset"], six.string_types): + f.write("{}\n".format(matgeoset["ccx_elset"])) else: - for elid in ccx_elset["ccx_elset"]: + for elid in matgeoset["ccx_elset"]: f.write(str(elid) + ",\n")