diff --git a/src/Mod/Arch/ArchCutPlane.py b/src/Mod/Arch/ArchCutPlane.py
index fc4c16ba6a..fdf903ad19 100644
--- a/src/Mod/Arch/ArchCutPlane.py
+++ b/src/Mod/Arch/ArchCutPlane.py
@@ -31,7 +31,7 @@ if FreeCAD.GuiUp:
from draftutils.translate import translate
else:
# \cond
- def translate(ctxt,txt):
+ def translate(ctxt, txt):
return txt
# \endcond
@@ -45,11 +45,67 @@ __url__ = "https://www.freecad.org"
#
# This module handles the Cut Plane object
-def getPlanWithLine(line):
- """Function to make a plane along Normal plan"""
+# _getShapes(FreeCADGui.Selection.getSelectionEx("", 0))
+def _getShapes(sels):
+ """Check and process the user selection.
+ Returns a tuple: (baseObj, baseShp, cutterShp).
+ baseShp and cutterShp are in the global coordinate system, cutterShp is a planar face.
+ If the selection is not valid one or more items in the tuple will be `None`.
+ """
+ if not sels:
+ return None, None, None
+ objs = []
+ needSubEle = False
+ for sel in sels:
+ for sub in sel.SubElementNames if sel.SubElementNames else [""]:
+ objs.append(Part.getShape(sel.Object, sub, needSubElement=needSubEle, retType=1))
+ needSubEle = True
+ if len(objs) != 2:
+ return None, None, None
+ baseShp, _, baseObj = objs[0]
+ cutterShp, _, _ = objs[1]
+ if baseShp.isNull():
+ return baseObj, None, None
+ if cutterShp.isNull():
+ return baseObj, baseShp, None
+ if cutterShp.ShapeType == "Edge":
+ if isinstance(cutterShp.Curve, Part.Line):
+ cutterShp = _extrudeEdge(cutterShp)
+ else:
+ try:
+ cutterShp = Part.Face(Part.Wire(cutterShp))
+ except Part.OCCError:
+ pass
+ elif cutterShp.ShapeType == "Wire":
+ if len(cutterShp.Edges) == 1 and isinstance(cutterShp.Edges[0].Curve, Part.Line):
+ cutterShp = _extrudeEdge(cutterShp.Edges[0])
+ else:
+ try:
+ cutterShp = Part.Face(cutterShp)
+ except Part.OCCError:
+ pass
+ if not cutterShp.Faces and cutterShp.Vertexes:
+ plane = cutterShp.findPlane()
+ if plane is not None:
+ # Directly creating a face from the plane results in an almost
+ # endless face that ArchCommands.getCutVolume() cannot handle.
+ # We therefore create a small triangular face.
+ pt_main = cutterShp.Vertexes[0].Point
+ mtx = plane.Rotation.toMatrix()
+ pt_u = mtx.col(0) + pt_main
+ pt_v = mtx.col(1) + pt_main
+ cutterShp = Part.Face(Part.makePolygon([pt_main, pt_u, pt_v, pt_main]))
+ # _extrudeEdge can create a face with a zero area (if the edge is parallel to the WP normal):
+ if not cutterShp.Faces \
+ or cutterShp.Faces[0].Area < 1e-6 \
+ or cutterShp.findPlane() is None:
+ return baseObj, baseShp, None
+ return baseObj, baseShp, cutterShp.Faces[0]
+
+def _extrudeEdge(edge):
+ """Exrude an edge along the WP normal"""
import WorkingPlane
- w = WorkingPlane.get_working_plane().axis
- return line.extrude(w)
+ return edge.extrude(WorkingPlane.get_working_plane().axis)
def cutComponentwithPlane(baseObj, cutterShp=None, side=0):
"""cut an object with a plane defined by a face.
@@ -72,14 +128,7 @@ def cutComponentwithPlane(baseObj, cutterShp=None, side=0):
if isinstance(baseObj, list) \
and len(baseObj) >= 1 \
and baseObj[0].isDerivedFrom("Gui::SelectionObject"):
- objs = []
- needSubEle = False
- for sel in baseObj:
- for sub in sel.SubElementNames if sel.SubElementNames else [""]:
- objs.append(Part.getShape(sel.Object, sub, needSubElement=needSubEle, retType=1))
- needSubEle = True
- baseShp, _, baseObj = objs[0]
- cutterShp, _, _ = objs[1]
+ baseObj, baseShp, cutterShp = _getShapes(baseObj)
baseParent = baseObj.getParentGeoFeatureGroup()
else:
baseShp = baseObj.Shape
@@ -88,12 +137,12 @@ def cutComponentwithPlane(baseObj, cutterShp=None, side=0):
baseShp = baseShp.transformGeometry(baseParent.getGlobalPlacement().toMatrix())
if cutterShp.ShapeType != "Face":
- cutterShp = getPlanWithLine(cutterShp)
+ cutterShp = _extrudeEdge(cutterShp)
cutVolume = ArchCommands.getCutVolume(cutterShp, baseShp)
cutVolume = cutVolume[2] if side == 0 else cutVolume[1]
if cutVolume:
- obj = FreeCAD.ActiveDocument.addObject("Part::Feature","CutVolume")
+ obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "CutVolume")
if baseParent is not None:
cutVolume.Placement = baseParent.getGlobalPlacement().inverse()
obj.Shape = Part.Compound([cutVolume])
@@ -103,67 +152,41 @@ def cutComponentwithPlane(baseObj, cutterShp=None, side=0):
ArchCommands.removeComponents(obj, baseObj) # Also changes the obj colors.
else:
Draft.format_object(obj, baseObj)
- cutObj = FreeCAD.ActiveDocument.addObject("Part::Cut","CutPlane")
+ cutObj = FreeCAD.ActiveDocument.addObject("Part::Cut", "CutPlane")
if baseParent is not None:
baseParent.addObject(cutObj)
cutObj.Base = baseObj
cutObj.Tool = obj
-class _CommandCutLine:
- "the Arch CutPlane command definition"
- def GetResources(self):
- return {"Pixmap": "Arch_CutLine",
- "MenuText": QtCore.QT_TRANSLATE_NOOP("Arch_CutLine", "Cut with line"),
- "ToolTip": QtCore.QT_TRANSLATE_NOOP("Arch_CutLine", "Cut an object with a line")}
-
- def IsActive(self):
- return len(FreeCADGui.Selection.getSelection()) > 1
-
- def Activated(self):
- sels = FreeCADGui.Selection.getSelectionEx()
- if len(sels) != 2:
- FreeCAD.Console.PrintError("You must select exactly two objects, the shape to be cut and a line\n")
- return
- if not sels[1].SubObjects:
- FreeCAD.Console.PrintError("You must select a line from the second object (cut line), not the whole object\n")
- return
- panel=_CutPlaneTaskPanel(linecut=True)
- FreeCADGui.Control.showDialog(panel)
-
class _CommandCutPlane:
"the Arch CutPlane command definition"
def GetResources(self):
return {"Pixmap": "Arch_CutPlane",
- "MenuText": QtCore.QT_TRANSLATE_NOOP("Arch_CutPlane","Cut with plane"),
- "ToolTip": QtCore.QT_TRANSLATE_NOOP("Arch_CutPlane","Cut an object with a plane")}
+ "MenuText": QtCore.QT_TRANSLATE_NOOP("Arch_CutPlane", "Cut with plane"),
+ "ToolTip": QtCore.QT_TRANSLATE_NOOP("Arch_CutPlane", "Cut an object with a plane")}
def IsActive(self):
return len(FreeCADGui.Selection.getSelection()) > 1
def Activated(self):
- sels = FreeCADGui.Selection.getSelectionEx()
- if len(sels) != 2:
- FreeCAD.Console.PrintError("You must select exactly two objects, the shape to be cut and the cut plane\n")
+ baseObj, baseShp, cutterShp = _getShapes(FreeCADGui.Selection.getSelectionEx("", 0))
+ if baseObj is None:
+ FreeCAD.Console.PrintError(
+ translate("Arch", "Select two objects, an object to be cut and an object defining a cutting plane, in that order\n")
+ )
return
- if not sels[1].SubObjects:
- FreeCAD.Console.PrintError("You must select a face from the second object (cut plane), not the whole object\n")
+ if baseShp is None:
+ FreeCAD.Console.PrintError(translate("Arch", "The first object does not have a shape\n"))
+ return
+ if cutterShp is None:
+ FreeCAD.Console.PrintError(translate("Arch", "The second object does not define a plane\n"))
return
panel=_CutPlaneTaskPanel()
FreeCADGui.Control.showDialog(panel)
class _CutPlaneTaskPanel:
- def __init__(self,linecut=False):
- sels = FreeCADGui.Selection.getSelectionEx("", 0)
- shapes = []
- needSubEle = False
- for sel in sels:
- for sub in sel.SubElementNames if sel.SubElementNames else [""]:
- shapes.append(Part.getShape(sel.Object, sub, needSubElement=needSubEle, retType=0))
- needSubEle = True
- self.base = shapes[0]
- self.plan = shapes[1]
- if linecut:
- self.plan = getPlanWithLine(self.plan)
+ def __init__(self):
+ _, self.base, self.cutter = _getShapes(FreeCADGui.Selection.getSelectionEx("", 0))
self.previewObj = FreeCAD.ActiveDocument.addObject("Part::Feature", "PreviewCutVolume")
self.previewObj.ViewObject.ShapeColor = (1.00, 0.00, 0.00)
@@ -180,7 +203,7 @@ class _CutPlaneTaskPanel:
self.combobox = QtGui.QComboBox()
self.combobox.setCurrentIndex(0)
self.grid.addWidget(self.combobox, 2, 1)
- QtCore.QObject.connect(self.combobox,QtCore.SIGNAL("currentIndexChanged(int)"),self.previewCutVolume)
+ QtCore.QObject.connect(self.combobox,QtCore.SIGNAL("currentIndexChanged(int)"), self.previewCutVolume)
self.retranslateUi(self.form)
self.previewCutVolume(self.combobox.currentIndex())
@@ -190,16 +213,12 @@ class _CutPlaneTaskPanel:
def accept(self):
FreeCAD.ActiveDocument.removeObject(self.previewObj.Name)
side = self.combobox.currentIndex()
- sels = FreeCADGui.Selection.getSelectionEx()
- if len(sels) > 1 and sels[1].SubObjects:
- FreeCAD.ActiveDocument.openTransaction(translate("Arch","Cutting"))
- FreeCADGui.addModule("Arch")
- FreeCADGui.doCommand("sels = FreeCADGui.Selection.getSelectionEx('', 0)")
- FreeCADGui.doCommand("Arch.cutComponentwithPlane(sels, side=" + str(side) + ")")
- FreeCAD.ActiveDocument.commitTransaction()
- FreeCAD.ActiveDocument.recompute()
- return True
- FreeCAD.Console.PrintError("Wrong selection\n")
+ FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Cutting"))
+ FreeCADGui.addModule("Arch")
+ FreeCADGui.doCommand("sels = FreeCADGui.Selection.getSelectionEx('', 0)")
+ FreeCADGui.doCommand("Arch.cutComponentwithPlane(sels, side=" + str(side) + ")")
+ FreeCAD.ActiveDocument.commitTransaction()
+ FreeCAD.ActiveDocument.recompute()
return True
def reject(self):
@@ -211,7 +230,7 @@ class _CutPlaneTaskPanel:
return int(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel)
def previewCutVolume(self, i):
- cutVolume = ArchCommands.getCutVolume(self.plan,self.base)
+ cutVolume = ArchCommands.getCutVolume(self.cutter, self.base)
if i == 1:
cutVolume = cutVolume[1]
else:
@@ -220,12 +239,11 @@ class _CutPlaneTaskPanel:
self.previewObj.Shape = cutVolume
def retranslateUi(self, TaskPanel):
- TaskPanel.setWindowTitle(QtGui.QApplication.translate("Arch", "Cut Plane", None))
- self.title.setText(QtGui.QApplication.translate("Arch", "Cut Plane options", None))
- self.infoText.setText(QtGui.QApplication.translate("Arch", "Which side to cut", None))
- self.combobox.addItems([QtGui.QApplication.translate("Arch", "Behind", None),
- QtGui.QApplication.translate("Arch", "Front", None)])
+ TaskPanel.setWindowTitle(translate("Arch", "Cut Plane"))
+ self.title.setText(translate("Arch", "Cut Plane options"))
+ self.infoText.setText(translate("Arch", "Which side to cut"))
+ self.combobox.addItems([translate("Arch", "Behind"), translate("Arch", "Front")])
if FreeCAD.GuiUp:
- FreeCADGui.addCommand('Arch_CutPlane',_CommandCutPlane())
- FreeCADGui.addCommand('Arch_CutLine', _CommandCutLine())
+ FreeCADGui.addCommand("Arch_CutPlane", _CommandCutPlane())
+
diff --git a/src/Mod/Arch/InitGui.py b/src/Mod/Arch/InitGui.py
index 464bf2bb8e..7e036fd1cb 100644
--- a/src/Mod/Arch/InitGui.py
+++ b/src/Mod/Arch/InitGui.py
@@ -111,7 +111,6 @@ class ArchWorkbench(FreeCADGui.Workbench):
list(pipe_group.GetCommands(pipe_group))),
("Arch_PipeTools", ),
"Arch_CutPlane",
- "Arch_CutLine",
"Arch_Add",
"Arch_Remove",
"Arch_Survey"]
diff --git a/src/Mod/Arch/Resources/Arch.qrc b/src/Mod/Arch/Resources/Arch.qrc
index 551e61015a..a4124cc8a7 100644
--- a/src/Mod/Arch/Resources/Arch.qrc
+++ b/src/Mod/Arch/Resources/Arch.qrc
@@ -20,7 +20,6 @@
icons/Arch_Component_Tree.svg
icons/Arch_CurtainWall.svg
icons/Arch_CurtainWall_Tree.svg
- icons/Arch_CutLine.svg
icons/Arch_CutPlane.svg
icons/Arch_Equipment.svg
icons/Arch_Equipment_Clone.svg
diff --git a/src/Mod/Arch/Resources/icons/Arch_CutLine.svg b/src/Mod/Arch/Resources/icons/Arch_CutLine.svg
deleted file mode 100644
index 8a6c8afffa..0000000000
--- a/src/Mod/Arch/Resources/icons/Arch_CutLine.svg
+++ /dev/null
@@ -1,418 +0,0 @@
-
-
-
-