Merge pull request #5442 from sliptonic/bug/translationAreaOp
[PATH] Bug/translation cleanup (surface, waterline, propertybag, area ops)
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>420</width>
|
||||
<width>498</width>
|
||||
<height>541</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -82,7 +82,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Additional offset to the selected bounding box along the X axis."</p></body></html></string>
|
||||
<string><html><head/><body><p>Additional offset to the selected bounding box along the X axis."</p></body></html></string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
@@ -92,7 +92,7 @@
|
||||
<item>
|
||||
<widget class="Gui::InputField" name="boundBoxExtraOffsetY">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Additional offset to the selected bounding box along the Y axis."</p></body></html></string>
|
||||
<string><html><head/><body><p>Additional offset to the selected bounding box along the Y axis."</p></body></html></string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
@@ -133,16 +133,6 @@
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Select the overall boundary for the operation.</p></body></html></string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Stock</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BaseBoundBox</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="1">
|
||||
@@ -160,26 +150,6 @@
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Profile the edges of the selection.</p></body></html></string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Only</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>First</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Last</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
@@ -194,36 +164,6 @@
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Set the geometric clearing pattern to use for the operation.</p></body></html></string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Circular</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>CircularZigZag</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Line</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Offset</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Spiral</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ZigZag</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
@@ -231,16 +171,6 @@
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Planar: Flat, 3D surface scan. Rotational: 4th-axis rotational scan.</p></body></html></string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Planar</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Rotational</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
@@ -262,16 +192,6 @@
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Complete the operation in a single pass at depth, or mulitiple passes to final depth.</p></body></html></string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Single-pass</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Multi-pass</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
@@ -293,16 +213,6 @@
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Dropcutter lines are created parallel to this axis.</p></body></html></string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>X</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Y</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1" colspan="2">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>350</width>
|
||||
<width>389</width>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -53,87 +53,80 @@
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="algorithmSelect_label">
|
||||
<property name="text">
|
||||
<string>Algorithm</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="algorithmSelect">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Select the algorithm to use: OCL Dropcutter*, or Experimental (Not OCL based).</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="boundBoxSelect_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>BoundBox</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="boundBoxSelect">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>8</pointsize>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Select the overall boundary for the operation.</p></body></html></string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Stock</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BaseBoundBox</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="3">
|
||||
<widget class="QLineEdit" name="boundaryAdjustment">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Positive values push the cutter toward, or beyond, the boundary. Negative values retract the cutter away from the boundary.</p></body></html></string>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="layerMode_label">
|
||||
<property name="text">
|
||||
<string>Layer Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="layerMode">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>8</pointsize>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Complete the operation in a single pass at depth, or mulitiple passes to final depth.</p></body></html></string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Single-pass</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Multi-pass</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QComboBox" name="algorithmSelect">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Select the algorithm to use: OCL Dropcutter*, or Experimental (Not OCL based).</p></body></html></string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>OCL Dropcutter</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Experimental</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="3">
|
||||
<widget class="QCheckBox" name="optimizeEnabled">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enable optimization of linear paths (co-linear points). Removes unnecessary co-linear points from G-Code output.</p></body></html></string>
|
||||
</property>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="cutPattern_label">
|
||||
<property name="text">
|
||||
<string>Optimize Linear Paths</string>
|
||||
<string>Cut Pattern</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="cutPattern">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Set the geometric clearing pattern to use for the operation.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -150,54 +143,24 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QComboBox" name="cutPattern">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>8</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<item row="4" column="1">
|
||||
<widget class="Gui::InputField" name="boundaryAdjustment">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Set the geometric clearing pattern to use for the operation.</p></body></html></string>
|
||||
<string><html><head/><body><p>Set the Z-axis depth offset from the target surface.</p></body></html></string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Circular</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>CircularZigZag</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Line</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Offset</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Spiral</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ZigZag</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="3">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="stepOver_label">
|
||||
<property name="text">
|
||||
<string>Step over</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QSpinBox" name="stepOver">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
@@ -216,35 +179,15 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="layerMode_label">
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="sampleInterval_label">
|
||||
<property name="text">
|
||||
<string>Layer Mode</string>
|
||||
<string>Sample interval</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="boundBoxSelect_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>BoundBox</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="stepOver_label">
|
||||
<property name="text">
|
||||
<string>Step over</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="3">
|
||||
<widget class="Gui::InputField" name="sampleInterval" native="true">
|
||||
<item row="6" column="1">
|
||||
<widget class="Gui::InputField" name="sampleInterval">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Set the sampling resolution. Smaller values quickly increase processing time.</p></body></html></string>
|
||||
</property>
|
||||
@@ -253,24 +196,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="cutPattern_label">
|
||||
<property name="text">
|
||||
<string>Cut Pattern</string>
|
||||
<item row="7" column="1">
|
||||
<widget class="QCheckBox" name="optimizeEnabled">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enable optimization of linear paths (co-linear points). Removes unnecessary co-linear points from G-Code output.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="sampleInterval_label">
|
||||
<property name="text">
|
||||
<string>Sample interval</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="algorithmSelect_label">
|
||||
<property name="text">
|
||||
<string>Algorithm</string>
|
||||
<string>Optimize Linear Paths</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -306,7 +238,6 @@
|
||||
<tabstop>boundBoxSelect</tabstop>
|
||||
<tabstop>layerMode</tabstop>
|
||||
<tabstop>cutPattern</tabstop>
|
||||
<tabstop>boundaryAdjustment</tabstop>
|
||||
<tabstop>stepOver</tabstop>
|
||||
<tabstop>sampleInterval</tabstop>
|
||||
<tabstop>optimizeEnabled</tabstop>
|
||||
|
||||
@@ -20,22 +20,20 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import Path
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import math
|
||||
from PySide import QtCore
|
||||
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
Draft = LazyLoader('Draft', globals(), 'Draft')
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
Draft = LazyLoader("Draft", globals(), "Draft")
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
|
||||
__title__ = "Base class for PathArea based operations."
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
@@ -44,95 +42,112 @@ __doc__ = "Base class and properties for Path.Area based operations."
|
||||
__contributors__ = "russ4262 (Russell Johnson)"
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class ObjectOp(PathOp.ObjectOp):
|
||||
'''Base class for all Path.Area based operations.
|
||||
"""Base class for all Path.Area based operations.
|
||||
Provides standard features including debugging properties AreaParams,
|
||||
PathParams and removalshape, all hidden.
|
||||
The main reason for existence is to implement the standard interface
|
||||
to Path.Area so subclasses only have to provide the shapes for the
|
||||
operations.'''
|
||||
operations."""
|
||||
|
||||
def opFeatures(self, obj):
|
||||
'''opFeatures(obj) ... returns the base features supported by all Path.Area based operations.
|
||||
"""opFeatures(obj) ... returns the base features supported by all Path.Area based operations.
|
||||
The standard feature list is OR'ed with the return value of areaOpFeatures().
|
||||
Do not overwrite, implement areaOpFeatures(obj) instead.'''
|
||||
return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureStepDown \
|
||||
| PathOp.FeatureHeights | PathOp.FeatureStartPoint \
|
||||
| self.areaOpFeatures(obj) | PathOp.FeatureCoolant
|
||||
Do not overwrite, implement areaOpFeatures(obj) instead."""
|
||||
return (
|
||||
PathOp.FeatureTool
|
||||
| PathOp.FeatureDepths
|
||||
| PathOp.FeatureStepDown
|
||||
| PathOp.FeatureHeights
|
||||
| PathOp.FeatureStartPoint
|
||||
| self.areaOpFeatures(obj)
|
||||
| PathOp.FeatureCoolant
|
||||
)
|
||||
|
||||
def areaOpFeatures(self, obj):
|
||||
'''areaOpFeatures(obj) ... overwrite to add operation specific features.
|
||||
Can safely be overwritten by subclasses.'''
|
||||
"""areaOpFeatures(obj) ... overwrite to add operation specific features.
|
||||
Can safely be overwritten by subclasses."""
|
||||
# pylint: disable=unused-argument
|
||||
return 0
|
||||
|
||||
def initOperation(self, obj):
|
||||
'''initOperation(obj) ... sets up standard Path.Area properties and calls initAreaOp().
|
||||
Do not overwrite, overwrite initAreaOp(obj) instead.'''
|
||||
"""initOperation(obj) ... sets up standard Path.Area properties and calls initAreaOp().
|
||||
Do not overwrite, overwrite initAreaOp(obj) instead."""
|
||||
PathLog.track()
|
||||
|
||||
# Debugging
|
||||
obj.addProperty("App::PropertyString", "AreaParams", "Path")
|
||||
obj.setEditorMode('AreaParams', 2) # hide
|
||||
obj.setEditorMode("AreaParams", 2) # hide
|
||||
obj.addProperty("App::PropertyString", "PathParams", "Path")
|
||||
obj.setEditorMode('PathParams', 2) # hide
|
||||
obj.setEditorMode("PathParams", 2) # hide
|
||||
obj.addProperty("Part::PropertyPartShape", "removalshape", "Path")
|
||||
obj.setEditorMode('removalshape', 2) # hide
|
||||
obj.setEditorMode("removalshape", 2) # hide
|
||||
|
||||
obj.addProperty("App::PropertyBool", "SplitArcs", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Split Arcs into discrete segments"))
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"SplitArcs",
|
||||
"Path",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Split Arcs into discrete segments"),
|
||||
)
|
||||
|
||||
# obj.Proxy = self
|
||||
|
||||
self.initAreaOp(obj)
|
||||
|
||||
def initAreaOp(self, obj):
|
||||
'''initAreaOp(obj) ... overwrite if the receiver class needs initialisation.
|
||||
Can safely be overwritten by subclasses.'''
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
"""initAreaOp(obj) ... overwrite if the receiver class needs initialisation.
|
||||
Can safely be overwritten by subclasses."""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def areaOpShapeForDepths(self, obj, job):
|
||||
'''areaOpShapeForDepths(obj) ... returns the shape used to make an initial calculation for the depths being used.
|
||||
The default implementation returns the job's Base.Shape'''
|
||||
"""areaOpShapeForDepths(obj) ... returns the shape used to make an initial calculation for the depths being used.
|
||||
The default implementation returns the job's Base.Shape"""
|
||||
if job:
|
||||
if job.Stock:
|
||||
PathLog.debug("job=%s base=%s shape=%s" % (job, job.Stock, job.Stock.Shape))
|
||||
PathLog.debug(
|
||||
"job=%s base=%s shape=%s" % (job, job.Stock, job.Stock.Shape)
|
||||
)
|
||||
return job.Stock.Shape
|
||||
else:
|
||||
PathLog.warning(translate("PathAreaOp", "job %s has no Base.") % job.Label)
|
||||
PathLog.warning(
|
||||
translate("PathAreaOp", "job %s has no Base.") % job.Label
|
||||
)
|
||||
else:
|
||||
PathLog.warning(translate("PathAreaOp", "no job for op %s found.") % obj.Label)
|
||||
PathLog.warning(
|
||||
translate("PathAreaOp", "no job for op %s found.") % obj.Label
|
||||
)
|
||||
return None
|
||||
|
||||
def areaOpOnChanged(self, obj, prop):
|
||||
'''areaOpOnChanged(obj, porp) ... overwrite to process operation specific changes to properties.
|
||||
Can safely be overwritten by subclasses.'''
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
"""areaOpOnChanged(obj, porp) ... overwrite to process operation specific changes to properties.
|
||||
Can safely be overwritten by subclasses."""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def opOnChanged(self, obj, prop):
|
||||
'''opOnChanged(obj, prop) ... base implementation of the notification framework - do not overwrite.
|
||||
"""opOnChanged(obj, prop) ... base implementation of the notification framework - do not overwrite.
|
||||
The base implementation takes a stab at determining Heights and Depths if the operations's Base
|
||||
changes.
|
||||
Do not overwrite, overwrite areaOpOnChanged(obj, prop) instead.'''
|
||||
Do not overwrite, overwrite areaOpOnChanged(obj, prop) instead."""
|
||||
# PathLog.track(obj.Label, prop)
|
||||
if prop in ['AreaParams', 'PathParams', 'removalshape']:
|
||||
if prop in ["AreaParams", "PathParams", "removalshape"]:
|
||||
obj.setEditorMode(prop, 2)
|
||||
|
||||
if prop == 'Base' and len(obj.Base) == 1:
|
||||
if prop == "Base" and len(obj.Base) == 1:
|
||||
(base, sub) = obj.Base[0]
|
||||
bb = base.Shape.BoundBox # parent boundbox
|
||||
subobj = base.Shape.getElement(sub[0])
|
||||
fbb = subobj.BoundBox # feature boundbox
|
||||
|
||||
if hasattr(obj, 'Side'):
|
||||
if hasattr(obj, "Side"):
|
||||
if bb.XLength == fbb.XLength and bb.YLength == fbb.YLength:
|
||||
obj.Side = "Outside"
|
||||
else:
|
||||
@@ -141,29 +156,34 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
self.areaOpOnChanged(obj, prop)
|
||||
|
||||
def opOnDocumentRestored(self, obj):
|
||||
for prop in ['AreaParams', 'PathParams', 'removalshape']:
|
||||
for prop in ["AreaParams", "PathParams", "removalshape"]:
|
||||
if hasattr(obj, prop):
|
||||
obj.setEditorMode(prop, 2)
|
||||
if not hasattr(obj, 'SplitArcs'):
|
||||
obj.addProperty("App::PropertyBool", "SplitArcs", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Split Arcs into discrete segments"))
|
||||
if not hasattr(obj, "SplitArcs"):
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"SplitArcs",
|
||||
"Path",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Split Arcs into discrete segments"),
|
||||
)
|
||||
|
||||
self.areaOpOnDocumentRestored(obj)
|
||||
|
||||
def areaOpOnDocumentRestored(self, obj):
|
||||
'''areaOpOnDocumentRestored(obj) ... overwrite to fully restore receiver'''
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
"""areaOpOnDocumentRestored(obj) ... overwrite to fully restore receiver"""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def opSetDefaultValues(self, obj, job):
|
||||
'''opSetDefaultValues(obj) ... base implementation, do not overwrite.
|
||||
"""opSetDefaultValues(obj) ... base implementation, do not overwrite.
|
||||
The base implementation sets the depths and heights based on the
|
||||
areaOpShapeForDepths() return value.
|
||||
Do not overwrite, overwrite areaOpSetDefaultValues(obj, job) instead.'''
|
||||
Do not overwrite, overwrite areaOpSetDefaultValues(obj, job) instead."""
|
||||
PathLog.debug("opSetDefaultValues(%s, %s)" % (obj.Label, job.Label))
|
||||
|
||||
if PathOp.FeatureDepths & self.opFeatures(obj):
|
||||
try:
|
||||
shape = self.areaOpShapeForDepths(obj, job)
|
||||
except Exception as ee: # pylint: disable=broad-except
|
||||
except Exception as ee: # pylint: disable=broad-except
|
||||
PathLog.error(ee)
|
||||
shape = None
|
||||
|
||||
@@ -182,136 +202,158 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
obj.OpStartDepth.Value = startDepth
|
||||
obj.OpFinalDepth.Value = finalDepth
|
||||
|
||||
PathLog.debug("Default OpDepths are Start: {}, and Final: {}".format(obj.OpStartDepth.Value, obj.OpFinalDepth.Value))
|
||||
PathLog.debug("Default Depths are Start: {}, and Final: {}".format(startDepth, finalDepth))
|
||||
PathLog.debug(
|
||||
"Default OpDepths are Start: {}, and Final: {}".format(
|
||||
obj.OpStartDepth.Value, obj.OpFinalDepth.Value
|
||||
)
|
||||
)
|
||||
PathLog.debug(
|
||||
"Default Depths are Start: {}, and Final: {}".format(
|
||||
startDepth, finalDepth
|
||||
)
|
||||
)
|
||||
|
||||
self.areaOpSetDefaultValues(obj, job)
|
||||
|
||||
def areaOpSetDefaultValues(self, obj, job):
|
||||
'''areaOpSetDefaultValues(obj, job) ... overwrite to set initial values of operation specific properties.
|
||||
Can safely be overwritten by subclasses.'''
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
"""areaOpSetDefaultValues(obj, job) ... overwrite to set initial values of operation specific properties.
|
||||
Can safely be overwritten by subclasses."""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def _buildPathArea(self, obj, baseobject, isHole, start, getsim):
|
||||
'''_buildPathArea(obj, baseobject, isHole, start, getsim) ... internal function.'''
|
||||
"""_buildPathArea(obj, baseobject, isHole, start, getsim) ... internal function."""
|
||||
# pylint: disable=unused-argument
|
||||
PathLog.track()
|
||||
area = Path.Area()
|
||||
area.setPlane(PathUtils.makeWorkplane(baseobject))
|
||||
area.add(baseobject)
|
||||
|
||||
areaParams = self.areaOpAreaParams(obj, isHole) # pylint: disable=assignment-from-no-return
|
||||
areaParams = self.areaOpAreaParams(
|
||||
obj, isHole
|
||||
) # pylint: disable=assignment-from-no-return
|
||||
|
||||
heights = [i for i in self.depthparams]
|
||||
PathLog.debug('depths: {}'.format(heights))
|
||||
PathLog.debug("depths: {}".format(heights))
|
||||
area.setParams(**areaParams)
|
||||
obj.AreaParams = str(area.getParams())
|
||||
|
||||
PathLog.debug("Area with params: {}".format(area.getParams()))
|
||||
|
||||
sections = area.makeSections(mode=0, project=self.areaOpUseProjection(obj), heights=heights)
|
||||
sections = area.makeSections(
|
||||
mode=0, project=self.areaOpUseProjection(obj), heights=heights
|
||||
)
|
||||
PathLog.debug("sections = %s" % sections)
|
||||
shapelist = [sec.getShape() for sec in sections]
|
||||
PathLog.debug("shapelist = %s" % shapelist)
|
||||
|
||||
pathParams = self.areaOpPathParams(obj, isHole) # pylint: disable=assignment-from-no-return
|
||||
pathParams['shapes'] = shapelist
|
||||
pathParams['feedrate'] = self.horizFeed
|
||||
pathParams['feedrate_v'] = self.vertFeed
|
||||
pathParams['verbose'] = True
|
||||
pathParams['resume_height'] = obj.SafeHeight.Value
|
||||
pathParams['retraction'] = obj.ClearanceHeight.Value
|
||||
pathParams['return_end'] = True
|
||||
pathParams = self.areaOpPathParams(
|
||||
obj, isHole
|
||||
) # pylint: disable=assignment-from-no-return
|
||||
pathParams["shapes"] = shapelist
|
||||
pathParams["feedrate"] = self.horizFeed
|
||||
pathParams["feedrate_v"] = self.vertFeed
|
||||
pathParams["verbose"] = True
|
||||
pathParams["resume_height"] = obj.SafeHeight.Value
|
||||
pathParams["retraction"] = obj.ClearanceHeight.Value
|
||||
pathParams["return_end"] = True
|
||||
# Note that emitting preambles between moves breaks some dressups and prevents path optimization on some controllers
|
||||
pathParams['preamble'] = False
|
||||
pathParams["preamble"] = False
|
||||
|
||||
if not self.areaOpRetractTool(obj):
|
||||
pathParams['threshold'] = 2.001 * self.radius
|
||||
pathParams["threshold"] = 2.001 * self.radius
|
||||
|
||||
if self.endVector is not None:
|
||||
pathParams['start'] = self.endVector
|
||||
pathParams["start"] = self.endVector
|
||||
elif PathOp.FeatureStartPoint & self.opFeatures(obj) and obj.UseStartPoint:
|
||||
pathParams['start'] = obj.StartPoint
|
||||
pathParams["start"] = obj.StartPoint
|
||||
|
||||
obj.PathParams = str({key: value for key, value in pathParams.items() if key != 'shapes'})
|
||||
obj.PathParams = str(
|
||||
{key: value for key, value in pathParams.items() if key != "shapes"}
|
||||
)
|
||||
PathLog.debug("Path with params: {}".format(obj.PathParams))
|
||||
|
||||
(pp, end_vector) = Path.fromShapes(**pathParams)
|
||||
PathLog.debug('pp: {}, end vector: {}'.format(pp, end_vector))
|
||||
self.endVector = end_vector # pylint: disable=attribute-defined-outside-init
|
||||
PathLog.debug("pp: {}, end vector: {}".format(pp, end_vector))
|
||||
self.endVector = end_vector # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
simobj = None
|
||||
if getsim:
|
||||
areaParams['Thicken'] = True
|
||||
areaParams['ToolRadius'] = self.radius - self.radius * .005
|
||||
areaParams["Thicken"] = True
|
||||
areaParams["ToolRadius"] = self.radius - self.radius * 0.005
|
||||
area.setParams(**areaParams)
|
||||
sec = area.makeSections(mode=0, project=False, heights=heights)[-1].getShape()
|
||||
sec = area.makeSections(mode=0, project=False, heights=heights)[
|
||||
-1
|
||||
].getShape()
|
||||
simobj = sec.extrude(FreeCAD.Vector(0, 0, baseobject.BoundBox.ZMax))
|
||||
|
||||
return pp, simobj
|
||||
|
||||
def _buildProfileOpenEdges(self, obj, edgeList, isHole, start, getsim):
|
||||
'''_buildPathArea(obj, edgeList, isHole, start, getsim) ... internal function.'''
|
||||
"""_buildPathArea(obj, edgeList, isHole, start, getsim) ... internal function."""
|
||||
# pylint: disable=unused-argument
|
||||
PathLog.track()
|
||||
|
||||
paths = []
|
||||
heights = [i for i in self.depthparams]
|
||||
PathLog.debug('depths: {}'.format(heights))
|
||||
PathLog.debug("depths: {}".format(heights))
|
||||
for i in range(0, len(heights)):
|
||||
for baseShape in edgeList:
|
||||
hWire = Part.Wire(Part.__sortEdges__(baseShape.Edges))
|
||||
hWire.translate(FreeCAD.Vector(0, 0, heights[i] - hWire.BoundBox.ZMin))
|
||||
|
||||
pathParams = {} # pylint: disable=assignment-from-no-return
|
||||
pathParams['shapes'] = [hWire]
|
||||
pathParams['feedrate'] = self.horizFeed
|
||||
pathParams['feedrate_v'] = self.vertFeed
|
||||
pathParams['verbose'] = True
|
||||
pathParams['resume_height'] = obj.SafeHeight.Value
|
||||
pathParams['retraction'] = obj.ClearanceHeight.Value
|
||||
pathParams['return_end'] = True
|
||||
pathParams = {} # pylint: disable=assignment-from-no-return
|
||||
pathParams["shapes"] = [hWire]
|
||||
pathParams["feedrate"] = self.horizFeed
|
||||
pathParams["feedrate_v"] = self.vertFeed
|
||||
pathParams["verbose"] = True
|
||||
pathParams["resume_height"] = obj.SafeHeight.Value
|
||||
pathParams["retraction"] = obj.ClearanceHeight.Value
|
||||
pathParams["return_end"] = True
|
||||
# Note that emitting preambles between moves breaks some dressups and prevents path optimization on some controllers
|
||||
pathParams['preamble'] = False
|
||||
pathParams["preamble"] = False
|
||||
|
||||
if self.endVector is None:
|
||||
V = hWire.Wires[0].Vertexes
|
||||
lv = len(V) - 1
|
||||
pathParams['start'] = FreeCAD.Vector(V[0].X, V[0].Y, V[0].Z)
|
||||
if obj.Direction == 'CCW':
|
||||
pathParams['start'] = FreeCAD.Vector(V[lv].X, V[lv].Y, V[lv].Z)
|
||||
pathParams["start"] = FreeCAD.Vector(V[0].X, V[0].Y, V[0].Z)
|
||||
if obj.Direction == "CCW":
|
||||
pathParams["start"] = FreeCAD.Vector(V[lv].X, V[lv].Y, V[lv].Z)
|
||||
else:
|
||||
pathParams['start'] = self.endVector
|
||||
pathParams["start"] = self.endVector
|
||||
|
||||
obj.PathParams = str({key: value for key, value in pathParams.items() if key != 'shapes'})
|
||||
obj.PathParams = str(
|
||||
{key: value for key, value in pathParams.items() if key != "shapes"}
|
||||
)
|
||||
PathLog.debug("Path with params: {}".format(obj.PathParams))
|
||||
|
||||
(pp, end_vector) = Path.fromShapes(**pathParams)
|
||||
paths.extend(pp.Commands)
|
||||
PathLog.debug('pp: {}, end vector: {}'.format(pp, end_vector))
|
||||
PathLog.debug("pp: {}, end vector: {}".format(pp, end_vector))
|
||||
|
||||
self.endVector = end_vector
|
||||
simobj = None
|
||||
|
||||
return paths, simobj
|
||||
|
||||
def opExecute(self, obj, getsim=False): # pylint: disable=arguments-differ
|
||||
'''opExecute(obj, getsim=False) ... implementation of Path.Area ops.
|
||||
def opExecute(self, obj, getsim=False): # pylint: disable=arguments-differ
|
||||
"""opExecute(obj, getsim=False) ... implementation of Path.Area ops.
|
||||
determines the parameters for _buildPathArea().
|
||||
Do not overwrite, implement
|
||||
areaOpAreaParams(obj, isHole) ... op specific area param dictionary
|
||||
areaOpPathParams(obj, isHole) ... op specific path param dictionary
|
||||
areaOpShapes(obj) ... the shape for path area to process
|
||||
areaOpUseProjection(obj) ... return true if operation can use projection
|
||||
instead.'''
|
||||
instead."""
|
||||
PathLog.track()
|
||||
|
||||
# Instantiate class variables for operation reference
|
||||
self.endVector = None # pylint: disable=attribute-defined-outside-init
|
||||
self.endVector = None # pylint: disable=attribute-defined-outside-init
|
||||
self.leadIn = 2.0 # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
# Initiate depthparams and calculate operation heights for operation
|
||||
self.depthparams = self._customDepthParams(obj, obj.StartDepth.Value, obj.FinalDepth.Value)
|
||||
self.depthparams = self._customDepthParams(
|
||||
obj, obj.StartDepth.Value, obj.FinalDepth.Value
|
||||
)
|
||||
|
||||
# Set start point
|
||||
if PathOp.FeatureStartPoint & self.opFeatures(obj) and obj.UseStartPoint:
|
||||
@@ -319,7 +361,7 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
else:
|
||||
start = None
|
||||
|
||||
aOS = self.areaOpShapes(obj) # pylint: disable=assignment-from-no-return
|
||||
aOS = self.areaOpShapes(obj) # pylint: disable=assignment-from-no-return
|
||||
|
||||
# Adjust tuples length received from other PathWB tools/operations
|
||||
shapes = []
|
||||
@@ -327,7 +369,7 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
if len(shp) == 2:
|
||||
(fc, iH) = shp
|
||||
# fc, iH, sub or description
|
||||
tup = fc, iH, 'otherOp'
|
||||
tup = fc, iH, "otherOp"
|
||||
shapes.append(tup)
|
||||
else:
|
||||
shapes.append(shp)
|
||||
@@ -335,38 +377,47 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
if len(shapes) > 1:
|
||||
locations = []
|
||||
for s in shapes:
|
||||
if s[2] == 'OpenEdge':
|
||||
if s[2] == "OpenEdge":
|
||||
shp = Part.makeCompound(s[0])
|
||||
else:
|
||||
shp = s[0]
|
||||
locations.append({
|
||||
'x': shp.BoundBox.XMax,
|
||||
'y': shp.BoundBox.YMax,
|
||||
'shape': s
|
||||
})
|
||||
locations.append(
|
||||
{"x": shp.BoundBox.XMax, "y": shp.BoundBox.YMax, "shape": s}
|
||||
)
|
||||
|
||||
locations = PathUtils.sort_locations(locations, ['x', 'y'])
|
||||
locations = PathUtils.sort_locations(locations, ["x", "y"])
|
||||
|
||||
shapes = [j['shape'] for j in locations]
|
||||
shapes = [j["shape"] for j in locations]
|
||||
|
||||
sims = []
|
||||
for shape, isHole, sub in shapes:
|
||||
profileEdgesIsOpen = False
|
||||
|
||||
if sub == 'OpenEdge':
|
||||
if sub == "OpenEdge":
|
||||
profileEdgesIsOpen = True
|
||||
if PathOp.FeatureStartPoint & self.opFeatures(obj) and obj.UseStartPoint:
|
||||
if (
|
||||
PathOp.FeatureStartPoint & self.opFeatures(obj)
|
||||
and obj.UseStartPoint
|
||||
):
|
||||
osp = obj.StartPoint
|
||||
self.commandlist.append(Path.Command('G0', {'X': osp.x, 'Y': osp.y, 'F': self.horizRapid}))
|
||||
self.commandlist.append(
|
||||
Path.Command(
|
||||
"G0", {"X": osp.x, "Y": osp.y, "F": self.horizRapid}
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
if profileEdgesIsOpen:
|
||||
(pp, sim) = self._buildProfileOpenEdges(obj, shape, isHole, start, getsim)
|
||||
(pp, sim) = self._buildProfileOpenEdges(
|
||||
obj, shape, isHole, start, getsim
|
||||
)
|
||||
else:
|
||||
(pp, sim) = self._buildPathArea(obj, shape, isHole, start, getsim)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
FreeCAD.Console.PrintError(e)
|
||||
FreeCAD.Console.PrintError("Something unexpected happened. Check project and tool config.")
|
||||
FreeCAD.Console.PrintError(
|
||||
"Something unexpected happened. Check project and tool config."
|
||||
)
|
||||
else:
|
||||
if profileEdgesIsOpen:
|
||||
ppCmds = pp
|
||||
@@ -378,41 +429,49 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
sims.append(sim)
|
||||
# Eif
|
||||
|
||||
if self.areaOpRetractTool(obj) and self.endVector is not None and len(self.commandlist) > 1:
|
||||
if (
|
||||
self.areaOpRetractTool(obj)
|
||||
and self.endVector is not None
|
||||
and len(self.commandlist) > 1
|
||||
):
|
||||
self.endVector[2] = obj.ClearanceHeight.Value
|
||||
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
|
||||
self.commandlist.append(
|
||||
Path.Command(
|
||||
"G0", {"Z": obj.ClearanceHeight.Value, "F": self.vertRapid}
|
||||
)
|
||||
)
|
||||
|
||||
PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n")
|
||||
return sims
|
||||
|
||||
def areaOpRetractTool(self, obj):
|
||||
'''areaOpRetractTool(obj) ... return False to keep the tool at current level between shapes. Default is True.'''
|
||||
"""areaOpRetractTool(obj) ... return False to keep the tool at current level between shapes. Default is True."""
|
||||
# pylint: disable=unused-argument
|
||||
return True
|
||||
|
||||
def areaOpAreaParams(self, obj, isHole):
|
||||
'''areaOpAreaParams(obj, isHole) ... return operation specific area parameters in a dictionary.
|
||||
"""areaOpAreaParams(obj, isHole) ... return operation specific area parameters in a dictionary.
|
||||
Note that the resulting parameters are stored in the property AreaParams.
|
||||
Must be overwritten by subclasses.'''
|
||||
Must be overwritten by subclasses."""
|
||||
# pylint: disable=unused-argument
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def areaOpPathParams(self, obj, isHole):
|
||||
'''areaOpPathParams(obj, isHole) ... return operation specific path parameters in a dictionary.
|
||||
"""areaOpPathParams(obj, isHole) ... return operation specific path parameters in a dictionary.
|
||||
Note that the resulting parameters are stored in the property PathParams.
|
||||
Must be overwritten by subclasses.'''
|
||||
Must be overwritten by subclasses."""
|
||||
# pylint: disable=unused-argument
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def areaOpShapes(self, obj):
|
||||
'''areaOpShapes(obj) ... return all shapes to be processed by Path.Area for this op.
|
||||
Must be overwritten by subclasses.'''
|
||||
"""areaOpShapes(obj) ... return all shapes to be processed by Path.Area for this op.
|
||||
Must be overwritten by subclasses."""
|
||||
# pylint: disable=unused-argument
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def areaOpUseProjection(self, obj):
|
||||
'''areaOpUseProcjection(obj) ... return True if the operation can use procjection, defaults to False.
|
||||
Can safely be overwritten by subclasses.'''
|
||||
"""areaOpUseProcjection(obj) ... return True if the operation can use procjection, defaults to False.
|
||||
Can safely be overwritten by subclasses."""
|
||||
# pylint: disable=unused-argument
|
||||
return False
|
||||
|
||||
@@ -426,10 +485,14 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
step_down=obj.StepDown.Value,
|
||||
z_finish_step=finish_step,
|
||||
final_depth=finDep,
|
||||
user_depths=None)
|
||||
user_depths=None,
|
||||
)
|
||||
return cdp
|
||||
|
||||
|
||||
# Eclass
|
||||
|
||||
|
||||
def SetupProperties():
|
||||
setup = []
|
||||
return setup
|
||||
|
||||
@@ -20,15 +20,12 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
|
||||
# import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.drillableLib as drillableLib
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
|
||||
@@ -43,9 +40,7 @@ __url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Base class an implementation for operations on circular holes."
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
if False:
|
||||
@@ -54,6 +49,7 @@ if False:
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
class ObjectOp(PathOp.ObjectOp):
|
||||
"""Base class for proxy objects of all operations on circular holes."""
|
||||
|
||||
@@ -81,7 +77,7 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
"App::PropertyStringList",
|
||||
"Disabled",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP("Path", "List of disabled features"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "List of disabled features"),
|
||||
)
|
||||
self.initCircularHoleOperation(obj)
|
||||
|
||||
|
||||
@@ -39,6 +39,24 @@ else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
def populateCombobox(form, enumTups, comboBoxesPropertyMap):
|
||||
"""fillComboboxes(form, comboBoxesPropertyMap) ... populate comboboxes with translated enumerations
|
||||
** comboBoxesPropertyMap will be unnecessary if UI files use strict combobox naming protocol.
|
||||
Args:
|
||||
form = UI form
|
||||
enumTups = list of (translated_text, data_string) tuples
|
||||
comboBoxesPropertyMap = list of (translated_text, data_string) tuples
|
||||
"""
|
||||
PathLog.track(enumTups)
|
||||
|
||||
# Load appropriate enumerations in each combobox
|
||||
for cb, prop in comboBoxesPropertyMap:
|
||||
box = getattr(form, cb) # Get the combobox
|
||||
box.clear() # clear the combobox
|
||||
for text, data in enumTups[prop]: # load enumerations
|
||||
box.addItem(text, data)
|
||||
|
||||
|
||||
def updateInputField(obj, prop, widget, onBeforeChange=None):
|
||||
"""updateInputField(obj, prop, widget) ... update obj's property prop with the value of widget.
|
||||
The property's value is only assigned if the new value differs from the current value.
|
||||
@@ -50,6 +68,7 @@ def updateInputField(obj, prop, widget, onBeforeChange=None):
|
||||
"""
|
||||
PathLog.track()
|
||||
value = widget.property("rawValue")
|
||||
PathLog.track("value: {}".format(value))
|
||||
attr = PathUtil.getProperty(obj, prop)
|
||||
attrValue = attr.Value if hasattr(attr, "Value") else attr
|
||||
|
||||
|
||||
@@ -107,6 +107,8 @@ def _log(level, module_line_func, msg):
|
||||
|
||||
def debug(msg):
|
||||
"""(message)"""
|
||||
module, line, func = _caller()
|
||||
msg = "({}) - {}".format(line, msg)
|
||||
return _log(Level.DEBUG, _caller(), msg)
|
||||
def info(msg):
|
||||
"""(message)"""
|
||||
|
||||
@@ -20,17 +20,17 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import time
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
import FreeCAD
|
||||
from PathScripts.PathUtils import waiting_effects
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import Path
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import time
|
||||
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
@@ -42,13 +42,13 @@ __author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Base class and properties implementation for all Path operations."
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule()
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
FeatureTool = 0x0001 # ToolController
|
||||
@@ -105,7 +105,7 @@ class ObjectOp(object):
|
||||
"App::PropertyLinkSubListGlobal",
|
||||
"Base",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "The base geometry for this operation"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "The base geometry for this operation"),
|
||||
)
|
||||
|
||||
def addOpValues(self, obj, values):
|
||||
@@ -114,8 +114,8 @@ class ObjectOp(object):
|
||||
"App::PropertyDistance",
|
||||
"OpStartDepth",
|
||||
"Op Values",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Holds the calculated value for the StartDepth"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Holds the calculated value for the StartDepth"
|
||||
),
|
||||
)
|
||||
obj.setEditorMode("OpStartDepth", 1) # read-only
|
||||
@@ -124,8 +124,8 @@ class ObjectOp(object):
|
||||
"App::PropertyDistance",
|
||||
"OpFinalDepth",
|
||||
"Op Values",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Holds the calculated value for the FinalDepth"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Holds the calculated value for the FinalDepth"
|
||||
),
|
||||
)
|
||||
obj.setEditorMode("OpFinalDepth", 1) # read-only
|
||||
@@ -134,7 +134,7 @@ class ObjectOp(object):
|
||||
"App::PropertyDistance",
|
||||
"OpToolDiameter",
|
||||
"Op Values",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Holds the diameter of the tool"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Holds the diameter of the tool"),
|
||||
)
|
||||
obj.setEditorMode("OpToolDiameter", 1) # read-only
|
||||
if "stockz" in values:
|
||||
@@ -142,14 +142,14 @@ class ObjectOp(object):
|
||||
"App::PropertyDistance",
|
||||
"OpStockZMax",
|
||||
"Op Values",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Holds the max Z value of Stock"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Holds the max Z value of Stock"),
|
||||
)
|
||||
obj.setEditorMode("OpStockZMax", 1) # read-only
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"OpStockZMin",
|
||||
"Op Values",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Holds the min Z value of Stock"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Holds the min Z value of Stock"),
|
||||
)
|
||||
obj.setEditorMode("OpStockZMin", 1) # read-only
|
||||
|
||||
@@ -160,29 +160,29 @@ class ObjectOp(object):
|
||||
"App::PropertyBool",
|
||||
"Active",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Make False, to prevent operation from generating code"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Make False, to prevent operation from generating code"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"Comment",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "An optional comment for this Operation"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "An optional comment for this Operation"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"UserLabel",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "User Assigned Label"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "User Assigned Label"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"CycleTime",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Operations Cycle Time Estimation"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Operations Cycle Time Estimation"),
|
||||
)
|
||||
obj.setEditorMode("CycleTime", 1) # read-only
|
||||
|
||||
@@ -196,7 +196,7 @@ class ObjectOp(object):
|
||||
"App::PropertyVectorList",
|
||||
"Locations",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Base locations for this operation"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Base locations for this operation"),
|
||||
)
|
||||
|
||||
if FeatureTool & features:
|
||||
@@ -204,8 +204,8 @@ class ObjectOp(object):
|
||||
"App::PropertyLink",
|
||||
"ToolController",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"The tool controller that will be used to calculate the path",
|
||||
),
|
||||
)
|
||||
@@ -213,10 +213,10 @@ class ObjectOp(object):
|
||||
|
||||
if FeatureCoolant & features:
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"App::PropertyEnumeration",
|
||||
"CoolantMode",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Coolant mode for this operation"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Coolant mode for this operation"),
|
||||
)
|
||||
|
||||
if FeatureDepths & features:
|
||||
@@ -224,16 +224,16 @@ class ObjectOp(object):
|
||||
"App::PropertyDistance",
|
||||
"StartDepth",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Starting Depth of Tool- first cut depth in Z"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Starting Depth of Tool- first cut depth in Z"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"FinalDepth",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Final Depth of Tool- lowest value in Z"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Final Depth of Tool- lowest value in Z"
|
||||
),
|
||||
)
|
||||
if FeatureNoFinalDepth & features:
|
||||
@@ -245,8 +245,9 @@ class ObjectOp(object):
|
||||
"App::PropertyDistance",
|
||||
"StartDepth",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Starting Depth internal use only for derived values"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Starting Depth internal use only for derived values",
|
||||
),
|
||||
)
|
||||
obj.setEditorMode("StartDepth", 1) # read-only
|
||||
@@ -258,7 +259,7 @@ class ObjectOp(object):
|
||||
"App::PropertyDistance",
|
||||
"StepDown",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Incremental Step Down of Tool"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Incremental Step Down of Tool"),
|
||||
)
|
||||
|
||||
if FeatureFinishDepth & features:
|
||||
@@ -266,8 +267,8 @@ class ObjectOp(object):
|
||||
"App::PropertyDistance",
|
||||
"FinishDepth",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Maximum material removed on final pass."
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Maximum material removed on final pass."
|
||||
),
|
||||
)
|
||||
|
||||
@@ -276,16 +277,17 @@ class ObjectOp(object):
|
||||
"App::PropertyDistance",
|
||||
"ClearanceHeight",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "The height needed to clear clamps and obstructions"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"The height needed to clear clamps and obstructions",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"SafeHeight",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Rapid Safety Height between locations."
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Rapid Safety Height between locations."
|
||||
),
|
||||
)
|
||||
|
||||
@@ -294,14 +296,14 @@ class ObjectOp(object):
|
||||
"App::PropertyVectorDistance",
|
||||
"StartPoint",
|
||||
"Start Point",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "The start point of this path"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "The start point of this path"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"UseStartPoint",
|
||||
"Start Point",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Make True, if specifying a Start Point"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Make True, if specifying a Start Point"
|
||||
),
|
||||
)
|
||||
|
||||
@@ -310,19 +312,24 @@ class ObjectOp(object):
|
||||
"App::PropertyDistance",
|
||||
"MinDiameter",
|
||||
"Diameter",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Lower limit of the turning diameter"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Lower limit of the turning diameter"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"MaxDiameter",
|
||||
"Diameter",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Upper limit of the turning diameter."
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Upper limit of the turning diameter."
|
||||
),
|
||||
)
|
||||
|
||||
for n in self.opPropertyEnumerations():
|
||||
PathLog.debug("n[0]: {} n[1]: {}".format(n[0], n[1]))
|
||||
if hasattr(obj, n[0]):
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
# members being set later
|
||||
self.commandlist = None
|
||||
self.horizFeed = None
|
||||
@@ -347,6 +354,39 @@ class ObjectOp(object):
|
||||
obj.recompute()
|
||||
obj.Proxy = self
|
||||
|
||||
@classmethod
|
||||
def opPropertyEnumerations(self, dataType="data"):
|
||||
"""propertyEnumerations(dataType="data")... return property enumeration lists of specified dataType.
|
||||
Args:
|
||||
dataType = 'data', 'raw', 'translated'
|
||||
Notes:
|
||||
'data' is list of internal string literals used in code
|
||||
'raw' is list of (translated_text, data_string) tuples
|
||||
'translated' is list of translated string literals
|
||||
"""
|
||||
|
||||
enums = {
|
||||
"CoolantMode": [
|
||||
(translate("Path_Operation", "None"), "None"),
|
||||
(translate("Path_Operation", "Flood"), "Flood"),
|
||||
(translate("Path_Operation", "Mist"), "Mist"),
|
||||
],
|
||||
}
|
||||
|
||||
if dataType == "raw":
|
||||
return enums
|
||||
|
||||
data = list()
|
||||
idx = 0 if dataType == "translated" else 1
|
||||
|
||||
PathLog.debug(enums)
|
||||
|
||||
for k, v in enumerate(enums):
|
||||
data.append((v, [tup[idx] for tup in enums[v]]))
|
||||
PathLog.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
def setEditorModes(self, obj, features):
|
||||
"""Editor modes are not preserved during document store/restore, set editor modes for all properties"""
|
||||
|
||||
@@ -359,6 +399,7 @@ class ObjectOp(object):
|
||||
obj.setEditorMode("OpFinalDepth", 2)
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
PathLog.track()
|
||||
features = self.opFeatures(obj)
|
||||
if (
|
||||
FeatureBaseGeometry & features
|
||||
@@ -375,13 +416,28 @@ class ObjectOp(object):
|
||||
if FeatureTool & features and not hasattr(obj, "OpToolDiameter"):
|
||||
self.addOpValues(obj, ["tooldia"])
|
||||
|
||||
if FeatureCoolant & features and not hasattr(obj, "CoolantMode"):
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"CoolantMode",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Coolant option for this operation"),
|
||||
)
|
||||
if FeatureCoolant & features:
|
||||
oldvalue = str(obj.CoolantMode) if hasattr(obj, "CoolantMode") else "None"
|
||||
if (
|
||||
hasattr(obj, "CoolantMode")
|
||||
and not obj.getTypeIdOfProperty("CoolantMode")
|
||||
== "App::PropertyEnumeration"
|
||||
):
|
||||
obj.removeProperty("CoolantMode")
|
||||
|
||||
if not hasattr(obj, "CoolantMode"):
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"CoolantMode",
|
||||
"Path",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Coolant option for this operation"
|
||||
),
|
||||
)
|
||||
for n in self.opPropertyEnumerations():
|
||||
if n[0] == "CoolantMode":
|
||||
setattr(obj, n[0], n[1])
|
||||
obj.CoolantMode = oldvalue
|
||||
|
||||
if FeatureDepths & features and not hasattr(obj, "OpStartDepth"):
|
||||
self.addOpValues(obj, ["start", "final"])
|
||||
@@ -396,7 +452,7 @@ class ObjectOp(object):
|
||||
"App::PropertyString",
|
||||
"CycleTime",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Operations Cycle Time Estimation"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Operations Cycle Time Estimation"),
|
||||
)
|
||||
|
||||
self.setEditorModes(obj, features)
|
||||
@@ -515,6 +571,8 @@ class ObjectOp(object):
|
||||
obj.OpToolDiameter = obj.ToolController.Tool.Diameter
|
||||
|
||||
if FeatureCoolant & features:
|
||||
PathLog.track()
|
||||
PathLog.debug(obj.getEnumerationsOfProperty("CoolantMode"))
|
||||
obj.CoolantMode = job.SetupSheet.CoolantMode
|
||||
|
||||
if FeatureDepths & features:
|
||||
@@ -711,14 +769,6 @@ class ObjectOp(object):
|
||||
# make sure Base is still valid or clear it
|
||||
self.sanitizeBase(obj)
|
||||
|
||||
if FeatureCoolant & self.opFeatures(obj):
|
||||
if not hasattr(obj, "CoolantMode"):
|
||||
PathLog.error(
|
||||
translate(
|
||||
"Path", "No coolant property found. Please recreate operation."
|
||||
)
|
||||
)
|
||||
|
||||
if FeatureTool & self.opFeatures(obj):
|
||||
tc = obj.ToolController
|
||||
if tc is None or tc.ToolNumber == 0:
|
||||
|
||||
@@ -21,40 +21,46 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathLog as PathLog
|
||||
import math
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
__title__ = "PathOpTools - Tools for Path operations."
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Collection of functions used by various Path operations. The functions are specific to Path and the algorithms employed by Path's operations."
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
PrintWireDebug = False
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
def debugEdge(label, e):
|
||||
'''debugEdge(label, e) ... prints a python statement to create e
|
||||
Currently lines and arcs are supported.'''
|
||||
"""debugEdge(label, e) ... prints a python statement to create e
|
||||
Currently lines and arcs are supported."""
|
||||
if not PrintWireDebug:
|
||||
return
|
||||
p0 = e.valueAt(e.FirstParameter)
|
||||
p1 = e.valueAt(e.LastParameter)
|
||||
if Part.Line == type(e.Curve):
|
||||
print("%s Part.makeLine((%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f))" % (label, p0.x, p0.y, p0.z, p1.x, p1.y, p1.z))
|
||||
print(
|
||||
"%s Part.makeLine((%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f))"
|
||||
% (label, p0.x, p0.y, p0.z, p1.x, p1.y, p1.z)
|
||||
)
|
||||
elif Part.Circle == type(e.Curve):
|
||||
r = e.Curve.Radius
|
||||
c = e.Curve.Center
|
||||
@@ -65,26 +71,37 @@ def debugEdge(label, e):
|
||||
else:
|
||||
first = math.degrees(xu + e.FirstParameter)
|
||||
last = first + math.degrees(e.LastParameter - e.FirstParameter)
|
||||
print("%s Part.makeCircle(%.2f, App.Vector(%.2f, %.2f, %.2f), App.Vector(%.2f, %.2f, %.2f), %.2f, %.2f)" % (label, r, c.x, c.y, c.z, a.x, a.y, a.z, first, last))
|
||||
print(
|
||||
"%s Part.makeCircle(%.2f, App.Vector(%.2f, %.2f, %.2f), App.Vector(%.2f, %.2f, %.2f), %.2f, %.2f)"
|
||||
% (label, r, c.x, c.y, c.z, a.x, a.y, a.z, first, last)
|
||||
)
|
||||
else:
|
||||
print("%s %s (%.2f, %.2f, %.2f) -> (%.2f, %.2f, %.2f)" % (label, type(e.Curve).__name__, p0.x, p0.y, p0.z, p1.x, p1.y, p1.z))
|
||||
print(
|
||||
"%s %s (%.2f, %.2f, %.2f) -> (%.2f, %.2f, %.2f)"
|
||||
% (label, type(e.Curve).__name__, p0.x, p0.y, p0.z, p1.x, p1.y, p1.z)
|
||||
)
|
||||
|
||||
|
||||
def debugWire(label, w):
|
||||
'''debugWire(label, w) ... prints python statements for all edges of w to be added to the object tree in a group.'''
|
||||
"""debugWire(label, w) ... prints python statements for all edges of w to be added to the object tree in a group."""
|
||||
if not PrintWireDebug:
|
||||
return
|
||||
print("#%s wire >>>>>>>>>>>>>>>>>>>>>>>>" % label)
|
||||
print("grp = FreeCAD.ActiveDocument.addObject('App::DocumentObjectGroup', '%s')" % label)
|
||||
for i,e in enumerate(w.Edges):
|
||||
print(
|
||||
"grp = FreeCAD.ActiveDocument.addObject('App::DocumentObjectGroup', '%s')"
|
||||
% label
|
||||
)
|
||||
for i, e in enumerate(w.Edges):
|
||||
edge = "%s_e%d" % (label, i)
|
||||
debugEdge("%s = " % edge, e)
|
||||
print("Part.show(%s, '%s')" % (edge, edge))
|
||||
print("grp.addObject(FreeCAD.ActiveDocument.ActiveObject)")
|
||||
print("#%s wire <<<<<<<<<<<<<<<<<<<<<<<<" % label)
|
||||
|
||||
|
||||
def _orientEdges(inEdges):
|
||||
'''_orientEdges(inEdges) ... internal worker function to orient edges so the last vertex of one edge connects to the first vertex of the next edge.
|
||||
Assumes the edges are in an order so they can be connected.'''
|
||||
"""_orientEdges(inEdges) ... internal worker function to orient edges so the last vertex of one edge connects to the first vertex of the next edge.
|
||||
Assumes the edges are in an order so they can be connected."""
|
||||
PathLog.track()
|
||||
# orient all edges of the wire so each edge's last value connects to the next edge's first value
|
||||
e0 = inEdges[0]
|
||||
@@ -92,21 +109,28 @@ def _orientEdges(inEdges):
|
||||
if 1 < len(inEdges):
|
||||
last = e0.valueAt(e0.LastParameter)
|
||||
e1 = inEdges[1]
|
||||
if not PathGeom.pointsCoincide(last, e1.valueAt(e1.FirstParameter)) and not PathGeom.pointsCoincide(last, e1.valueAt(e1.LastParameter)):
|
||||
debugEdge('# _orientEdges - flip first', e0)
|
||||
if not PathGeom.pointsCoincide(
|
||||
last, e1.valueAt(e1.FirstParameter)
|
||||
) and not PathGeom.pointsCoincide(last, e1.valueAt(e1.LastParameter)):
|
||||
debugEdge("# _orientEdges - flip first", e0)
|
||||
e0 = PathGeom.flipEdge(e0)
|
||||
|
||||
edges = [e0]
|
||||
last = e0.valueAt(e0.LastParameter)
|
||||
for e in inEdges[1:]:
|
||||
edge = e if PathGeom.pointsCoincide(last, e.valueAt(e.FirstParameter)) else PathGeom.flipEdge(e)
|
||||
edge = (
|
||||
e
|
||||
if PathGeom.pointsCoincide(last, e.valueAt(e.FirstParameter))
|
||||
else PathGeom.flipEdge(e)
|
||||
)
|
||||
edges.append(edge)
|
||||
last = edge.valueAt(edge.LastParameter)
|
||||
return edges
|
||||
|
||||
|
||||
def _isWireClockwise(w):
|
||||
'''_isWireClockwise(w) ... return True if wire is oriented clockwise.
|
||||
Assumes the edges of w are already properly oriented - for generic access use isWireClockwise(w).'''
|
||||
"""_isWireClockwise(w) ... return True if wire is oriented clockwise.
|
||||
Assumes the edges of w are already properly oriented - for generic access use isWireClockwise(w)."""
|
||||
# handle wires consisting of a single circle or 2 edges where one is an arc.
|
||||
# in both cases, because the edges are expected to be oriented correctly, the orientation can be
|
||||
# determined by looking at (one of) the circle curves.
|
||||
@@ -125,31 +149,33 @@ def _isWireClockwise(w):
|
||||
PathLog.track(area)
|
||||
return area < 0
|
||||
|
||||
|
||||
def isWireClockwise(w):
|
||||
'''isWireClockwise(w) ... returns True if the wire winds clockwise. '''
|
||||
"""isWireClockwise(w) ... returns True if the wire winds clockwise."""
|
||||
return _isWireClockwise(Part.Wire(_orientEdges(w.Edges)))
|
||||
|
||||
|
||||
def orientWire(w, forward=True):
|
||||
'''orientWire(w, forward=True) ... orients given wire in a specific direction.
|
||||
"""orientWire(w, forward=True) ... orients given wire in a specific direction.
|
||||
If forward = True (the default) the wire is oriented clockwise, looking down the negative Z axis.
|
||||
If forward = False the wire is oriented counter clockwise.
|
||||
If forward = None the orientation is determined by the order in which the edges appear in the wire.'''
|
||||
PathLog.debug('orienting forward: {}'.format(forward))
|
||||
If forward = None the orientation is determined by the order in which the edges appear in the wire."""
|
||||
PathLog.debug("orienting forward: {}".format(forward))
|
||||
wire = Part.Wire(_orientEdges(w.Edges))
|
||||
if forward is not None:
|
||||
if forward != _isWireClockwise(wire):
|
||||
PathLog.track('orientWire - needs flipping')
|
||||
PathLog.track("orientWire - needs flipping")
|
||||
return PathGeom.flipWire(wire)
|
||||
PathLog.track('orientWire - ok')
|
||||
PathLog.track("orientWire - ok")
|
||||
return wire
|
||||
|
||||
def offsetWire(wire, base, offset, forward, Side = None):
|
||||
'''offsetWire(wire, base, offset, forward) ... offsets the wire away from base and orients the wire accordingly.
|
||||
|
||||
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.
|
||||
'''
|
||||
PathLog.track('offsetWire')
|
||||
"""
|
||||
PathLog.track("offsetWire")
|
||||
|
||||
if 1 == len(wire.Edges):
|
||||
edge = wire.Edges[0]
|
||||
@@ -159,24 +185,34 @@ 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
|
||||
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):
|
||||
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
|
||||
if Side:
|
||||
Side[0] = "Inside"
|
||||
print("inside")
|
||||
new_edge = Part.makeCircle(curve.Radius - offset, curve.Center, FreeCAD.Vector(0, 0, -z))
|
||||
|
||||
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
|
||||
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)
|
||||
|
||||
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)
|
||||
@@ -186,26 +222,41 @@ 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:
|
||||
|
||||
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):
|
||||
if base.isInside(edge.Vertexes[0].Point, offset / 2, True):
|
||||
offset *= -1
|
||||
if Side:
|
||||
Side[0] = "Inside"
|
||||
|
||||
# Create new arc
|
||||
if curve.AngleXU > 0:
|
||||
edge = Part.ArcOfCircle(Part.Circle(curve.Center, FreeCAD.Vector(0,0,1), curve.Radius+offset), start_angle, end_angle).toShape()
|
||||
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()
|
||||
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
|
||||
@@ -217,7 +268,11 @@ def offsetWire(wire, base, offset, forward, Side = None):
|
||||
edge.translate(o)
|
||||
|
||||
# offset edde the other way if the result is inside
|
||||
if base.isInside(edge.valueAt((edge.FirstParameter + edge.LastParameter) / 2), offset / 2, True):
|
||||
if base.isInside(
|
||||
edge.valueAt((edge.FirstParameter + edge.LastParameter) / 2),
|
||||
offset / 2,
|
||||
True,
|
||||
):
|
||||
edge.translate(-2 * o)
|
||||
|
||||
# flip the edge if it's not on the right side of the original edge
|
||||
@@ -229,23 +284,23 @@ def offsetWire(wire, base, offset, forward, Side = None):
|
||||
return Part.Wire([edge])
|
||||
|
||||
# if we get to this point the assumption is that makeOffset2D can deal with the edge
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
owire = orientWire(wire.makeOffset2D(offset), True)
|
||||
debugWire('makeOffset2D_%d' % len(wire.Edges), owire)
|
||||
debugWire("makeOffset2D_%d" % len(wire.Edges), owire)
|
||||
|
||||
if wire.isClosed():
|
||||
if not base.isInside(owire.Edges[0].Vertexes[0].Point, offset/2, True):
|
||||
PathLog.track('closed - outside')
|
||||
if not base.isInside(owire.Edges[0].Vertexes[0].Point, offset / 2, True):
|
||||
PathLog.track("closed - outside")
|
||||
if Side:
|
||||
Side[0] = "Outside"
|
||||
return orientWire(owire, forward)
|
||||
PathLog.track('closed - inside')
|
||||
PathLog.track("closed - inside")
|
||||
if Side:
|
||||
Side[0] = "Inside"
|
||||
try:
|
||||
owire = wire.makeOffset2D(-offset)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# most likely offsetting didn't work because the wire is a hole
|
||||
# and the offset is too big - making the hole vanish
|
||||
return None
|
||||
@@ -269,8 +324,8 @@ def offsetWire(wire, base, offset, forward, Side = None):
|
||||
# determine the start and end point
|
||||
start = edges[0].firstVertex().Point
|
||||
end = edges[-1].lastVertex().Point
|
||||
debugWire('wire', wire)
|
||||
debugWire('wedges', Part.Wire(edges))
|
||||
debugWire("wire", wire)
|
||||
debugWire("wedges", Part.Wire(edges))
|
||||
|
||||
# find edges that are not inside the shape
|
||||
common = base.common(owire)
|
||||
@@ -281,7 +336,9 @@ def offsetWire(wire, base, offset, forward, Side = None):
|
||||
p0 = edge.firstVertex().Point
|
||||
p1 = edge.lastVertex().Point
|
||||
for p in insideEndpoints:
|
||||
if PathGeom.pointsCoincide(p, p0, 0.01) or PathGeom.pointsCoincide(p, p1, 0.01):
|
||||
if PathGeom.pointsCoincide(p, p0, 0.01) or PathGeom.pointsCoincide(
|
||||
p, p1, 0.01
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -292,16 +349,15 @@ def offsetWire(wire, base, offset, forward, Side = None):
|
||||
if not longestWire or longestWire.Length < w.Length:
|
||||
longestWire = w
|
||||
|
||||
debugWire('outside', Part.Wire(outside))
|
||||
debugWire('longest', longestWire)
|
||||
debugWire("outside", Part.Wire(outside))
|
||||
debugWire("longest", longestWire)
|
||||
|
||||
def isCircleAt(edge, center):
|
||||
'''isCircleAt(edge, center) ... helper function returns True if edge is a circle at the given center.'''
|
||||
"""isCircleAt(edge, center) ... helper function returns True if edge is a circle at the given center."""
|
||||
if Part.Circle == type(edge.Curve) or Part.ArcOfCircle == type(edge.Curve):
|
||||
return PathGeom.pointsCoincide(edge.Curve.Center, center)
|
||||
return False
|
||||
|
||||
|
||||
# split offset wire into edges to the left side and edges to the right side
|
||||
collectLeft = False
|
||||
collectRight = False
|
||||
@@ -312,7 +368,7 @@ def offsetWire(wire, base, offset, forward, Side = None):
|
||||
# an end point (circle centered at one of the end points of the original wire).
|
||||
# should we come to an end point and determine that we've already collected the
|
||||
# next side, we're done
|
||||
for e in (owire.Edges + owire.Edges):
|
||||
for e in owire.Edges + owire.Edges:
|
||||
if isCircleAt(e, start):
|
||||
if PathGeom.pointsCoincide(e.Curve.Axis, FreeCAD.Vector(0, 0, 1)):
|
||||
if not collectLeft and leftSideEdges:
|
||||
@@ -340,8 +396,8 @@ def offsetWire(wire, base, offset, forward, Side = None):
|
||||
elif collectRight:
|
||||
rightSideEdges.append(e)
|
||||
|
||||
debugWire('left', Part.Wire(leftSideEdges))
|
||||
debugWire('right', Part.Wire(rightSideEdges))
|
||||
debugWire("left", Part.Wire(leftSideEdges))
|
||||
debugWire("right", Part.Wire(rightSideEdges))
|
||||
|
||||
# figure out if all the left sided edges or the right sided edges are the ones
|
||||
# that are 'outside'. However, we return the full side.
|
||||
@@ -365,4 +421,3 @@ def offsetWire(wire, base, offset, forward, Side = None):
|
||||
edges.reverse()
|
||||
|
||||
return orientWire(Part.Wire(edges), None)
|
||||
|
||||
|
||||
@@ -20,31 +20,39 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import PySide
|
||||
import re
|
||||
import PathScripts.PathLog as PathLog
|
||||
|
||||
__title__ = 'Generic property container to store some values.'
|
||||
__author__ = 'sliptonic (Brad Collette)'
|
||||
__url__ = 'https://www.freecadweb.org'
|
||||
__doc__ = 'A generic container for typed properties in arbitrary categories.'
|
||||
__title__ = "Generic property container to store some values."
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "A generic container for typed properties in arbitrary categories."
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return PySide.QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
SupportedPropertyType = {
|
||||
'Angle' : 'App::PropertyAngle',
|
||||
'Bool' : 'App::PropertyBool',
|
||||
'Distance' : 'App::PropertyDistance',
|
||||
'Enumeration' : 'App::PropertyEnumeration',
|
||||
'File' : 'App::PropertyFile',
|
||||
'Float' : 'App::PropertyFloat',
|
||||
'Integer' : 'App::PropertyInteger',
|
||||
'Length' : 'App::PropertyLength',
|
||||
'Percent' : 'App::PropertyPercent',
|
||||
'String' : 'App::PropertyString',
|
||||
}
|
||||
"Angle": "App::PropertyAngle",
|
||||
"Bool": "App::PropertyBool",
|
||||
"Distance": "App::PropertyDistance",
|
||||
"Enumeration": "App::PropertyEnumeration",
|
||||
"File": "App::PropertyFile",
|
||||
"Float": "App::PropertyFloat",
|
||||
"Integer": "App::PropertyInteger",
|
||||
"Length": "App::PropertyLength",
|
||||
"Percent": "App::PropertyPercent",
|
||||
"String": "App::PropertyString",
|
||||
}
|
||||
|
||||
|
||||
def getPropertyTypeName(o):
|
||||
for typ in SupportedPropertyType:
|
||||
@@ -52,14 +60,22 @@ def getPropertyTypeName(o):
|
||||
return typ
|
||||
raise IndexError()
|
||||
|
||||
class PropertyBag(object):
|
||||
'''Property container object.'''
|
||||
|
||||
CustomPropertyGroups = 'CustomPropertyGroups'
|
||||
CustomPropertyGroupDefault = 'User'
|
||||
class PropertyBag(object):
|
||||
"""Property container object."""
|
||||
|
||||
CustomPropertyGroups = "CustomPropertyGroups"
|
||||
CustomPropertyGroupDefault = "User"
|
||||
|
||||
def __init__(self, obj):
|
||||
obj.addProperty('App::PropertyStringList', self.CustomPropertyGroups, 'Base', PySide.QtCore.QT_TRANSLATE_NOOP('PathPropertyBag', 'List of custom property groups'))
|
||||
obj.addProperty(
|
||||
"App::PropertyStringList",
|
||||
self.CustomPropertyGroups,
|
||||
"Base",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "List of custom property groups"
|
||||
),
|
||||
)
|
||||
self.onDocumentRestored(obj)
|
||||
|
||||
def __getstate__(self):
|
||||
@@ -69,14 +85,14 @@ class PropertyBag(object):
|
||||
return None
|
||||
|
||||
def __sanitizePropertyName(self, name):
|
||||
if(len(name) == 0):
|
||||
if len(name) == 0:
|
||||
return
|
||||
clean = name[0]
|
||||
for i in range(1, len(name)):
|
||||
if (name[i] == ' '):
|
||||
if name[i] == " ":
|
||||
clean += name[i + 1].upper()
|
||||
i += 1
|
||||
elif(name[i - 1] != ' '):
|
||||
elif name[i - 1] != " ":
|
||||
clean += name[i]
|
||||
return clean
|
||||
|
||||
@@ -85,20 +101,24 @@ class PropertyBag(object):
|
||||
obj.setEditorMode(self.CustomPropertyGroups, 2) # hide
|
||||
|
||||
def getCustomProperties(self):
|
||||
'''getCustomProperties() ... Return a list of all custom properties created in this container.'''
|
||||
return [p for p in self.obj.PropertiesList if self.obj.getGroupOfProperty(p) in self.obj.CustomPropertyGroups]
|
||||
"""getCustomProperties() ... Return a list of all custom properties created in this container."""
|
||||
return [
|
||||
p
|
||||
for p in self.obj.PropertiesList
|
||||
if self.obj.getGroupOfProperty(p) in self.obj.CustomPropertyGroups
|
||||
]
|
||||
|
||||
def addCustomProperty(self, propertyType, name, group=None, desc=None):
|
||||
'''addCustomProperty(propertyType, name, group=None, desc=None) ... adds a custom property and tracks its group.'''
|
||||
"""addCustomProperty(propertyType, name, group=None, desc=None) ... adds a custom property and tracks its group."""
|
||||
if desc is None:
|
||||
desc = ''
|
||||
desc = ""
|
||||
if group is None:
|
||||
group = self.CustomPropertyGroupDefault
|
||||
groups = self.obj.CustomPropertyGroups
|
||||
|
||||
name = self.__sanitizePropertyName(name)
|
||||
if not re.match("^[A-Za-z0-9_]*$", name):
|
||||
raise ValueError('Property Name can only contain letters and numbers')
|
||||
raise ValueError("Property Name can only contain letters and numbers")
|
||||
|
||||
if not group in groups:
|
||||
groups.append(group)
|
||||
@@ -107,7 +127,7 @@ class PropertyBag(object):
|
||||
return name
|
||||
|
||||
def refreshCustomPropertyGroups(self):
|
||||
'''refreshCustomPropertyGroups() ... removes empty property groups, should be called after deleting properties.'''
|
||||
"""refreshCustomPropertyGroups() ... removes empty property groups, should be called after deleting properties."""
|
||||
customGroups = []
|
||||
for p in self.obj.PropertiesList:
|
||||
group = self.obj.getGroupOfProperty(p)
|
||||
@@ -116,17 +136,17 @@ class PropertyBag(object):
|
||||
self.obj.CustomPropertyGroups = customGroups
|
||||
|
||||
|
||||
def Create(name = 'PropertyBag'):
|
||||
obj = FreeCAD.ActiveDocument.addObject('App::FeaturePython', name)
|
||||
def Create(name="PropertyBag"):
|
||||
obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", name)
|
||||
obj.Proxy = PropertyBag(obj)
|
||||
return obj
|
||||
|
||||
|
||||
def IsPropertyBag(obj):
|
||||
'''Returns True if the supplied object is a property container (or its Proxy).'''
|
||||
"""Returns True if the supplied object is a property container (or its Proxy)."""
|
||||
|
||||
if type(obj) == PropertyBag:
|
||||
return True
|
||||
if hasattr(obj, 'Proxy'):
|
||||
if hasattr(obj, "Proxy"):
|
||||
return IsPropertyBag(obj.Proxy)
|
||||
return False
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
#import PathGui
|
||||
import PathScripts.PathIconViewProvider as PathIconViewProvider
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPropertyBag as PathPropertyBag
|
||||
@@ -30,23 +30,24 @@ import PathScripts.PathPropertyEditor as PathPropertyEditor
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import re
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
__title__ = "Property Bag Editor"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Task panel editor for a PropertyBag"
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
class ViewProvider(object):
|
||||
'''ViewProvider for a PropertyBag.
|
||||
It's sole job is to provide an icon and invoke the TaskPanel on edit.'''
|
||||
"""ViewProvider for a PropertyBag.
|
||||
It's sole job is to provide an icon and invoke the TaskPanel on edit."""
|
||||
|
||||
def __init__(self, vobj, name):
|
||||
PathLog.track(name)
|
||||
@@ -73,7 +74,7 @@ class ViewProvider(object):
|
||||
|
||||
def getDisplayMode(self, mode):
|
||||
# pylint: disable=unused-argument
|
||||
return 'Default'
|
||||
return "Default"
|
||||
|
||||
def setEdit(self, vobj, mode=0):
|
||||
# pylint: disable=unused-argument
|
||||
@@ -95,18 +96,20 @@ class ViewProvider(object):
|
||||
def doubleClicked(self, vobj):
|
||||
self.setEdit(vobj)
|
||||
|
||||
|
||||
class Delegate(QtGui.QStyledItemDelegate):
|
||||
RoleObject = QtCore.Qt.UserRole + 1
|
||||
RoleObject = QtCore.Qt.UserRole + 1
|
||||
RoleProperty = QtCore.Qt.UserRole + 2
|
||||
RoleEditor = QtCore.Qt.UserRole + 3
|
||||
RoleEditor = QtCore.Qt.UserRole + 3
|
||||
|
||||
|
||||
#def paint(self, painter, option, index):
|
||||
# def paint(self, painter, option, index):
|
||||
# #PathLog.track(index.column(), type(option))
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
# pylint: disable=unused-argument
|
||||
editor = PathPropertyEditor.Editor(index.data(self.RoleObject), index.data(self.RoleProperty))
|
||||
editor = PathPropertyEditor.Editor(
|
||||
index.data(self.RoleObject), index.data(self.RoleProperty)
|
||||
)
|
||||
index.model().setData(index, editor, self.RoleEditor)
|
||||
return editor.widget(parent)
|
||||
|
||||
@@ -125,8 +128,8 @@ class Delegate(QtGui.QStyledItemDelegate):
|
||||
# pylint: disable=unused-argument
|
||||
widget.setGeometry(option.rect)
|
||||
|
||||
class PropertyCreate(object):
|
||||
|
||||
class PropertyCreate(object):
|
||||
def __init__(self, obj, grp, typ, another):
|
||||
self.obj = obj
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":panels/PropertyCreate.ui")
|
||||
@@ -144,7 +147,7 @@ class PropertyCreate(object):
|
||||
if typ:
|
||||
self.form.propertyType.setCurrentText(typ)
|
||||
else:
|
||||
self.form.propertyType.setCurrentText('String')
|
||||
self.form.propertyType.setCurrentText("String")
|
||||
self.form.createAnother.setChecked(another)
|
||||
|
||||
self.form.propertyGroup.currentTextChanged.connect(self.updateUI)
|
||||
@@ -159,12 +162,12 @@ class PropertyCreate(object):
|
||||
if self.propertyIsEnumeration():
|
||||
self.form.labelEnum.setEnabled(True)
|
||||
self.form.propertyEnum.setEnabled(True)
|
||||
typeSet = self.form.propertyEnum.text().strip() != ''
|
||||
typeSet = self.form.propertyEnum.text().strip() != ""
|
||||
else:
|
||||
self.form.labelEnum.setEnabled(False)
|
||||
self.form.propertyEnum.setEnabled(False)
|
||||
if self.form.propertyEnum.text().strip():
|
||||
self.form.propertyEnum.setText('')
|
||||
self.form.propertyEnum.setText("")
|
||||
|
||||
ok = self.form.buttonBox.button(QtGui.QDialogButtonBox.Ok)
|
||||
|
||||
@@ -178,25 +181,35 @@ class PropertyCreate(object):
|
||||
|
||||
def propertyName(self):
|
||||
return self.form.propertyName.text().strip()
|
||||
|
||||
def propertyGroup(self):
|
||||
return self.form.propertyGroup.currentText().strip()
|
||||
|
||||
def propertyType(self):
|
||||
return PathPropertyBag.SupportedPropertyType[self.form.propertyType.currentText()].strip()
|
||||
return PathPropertyBag.SupportedPropertyType[
|
||||
self.form.propertyType.currentText()
|
||||
].strip()
|
||||
|
||||
def propertyInfo(self):
|
||||
return self.form.propertyInfo.toPlainText().strip()
|
||||
|
||||
def createAnother(self):
|
||||
return self.form.createAnother.isChecked()
|
||||
|
||||
def propertyEnumerations(self):
|
||||
return [s.strip() for s in self.form.propertyEnum.text().strip().split(',')]
|
||||
return [s.strip() for s in self.form.propertyEnum.text().strip().split(",")]
|
||||
|
||||
def propertyIsEnumeration(self):
|
||||
return self.propertyType() == 'App::PropertyEnumeration'
|
||||
return self.propertyType() == "App::PropertyEnumeration"
|
||||
|
||||
def exec_(self, name):
|
||||
if name:
|
||||
# property exists - this is an edit operation
|
||||
self.form.propertyName.setText(name)
|
||||
if self.propertyIsEnumeration():
|
||||
self.form.propertyEnum.setText(','.join(self.obj.getEnumerationsOfProperty(name)))
|
||||
self.form.propertyEnum.setText(
|
||||
",".join(self.obj.getEnumerationsOfProperty(name))
|
||||
)
|
||||
self.form.propertyInfo.setText(self.obj.getDocumentationOfProperty(name))
|
||||
|
||||
self.form.labelName.setEnabled(False)
|
||||
@@ -206,65 +219,80 @@ class PropertyCreate(object):
|
||||
self.form.createAnother.setEnabled(False)
|
||||
|
||||
else:
|
||||
self.form.propertyName.setText('')
|
||||
self.form.propertyInfo.setText('')
|
||||
self.form.propertyEnum.setText('')
|
||||
#self.form.propertyName.setFocus()
|
||||
self.form.propertyName.setText("")
|
||||
self.form.propertyInfo.setText("")
|
||||
self.form.propertyEnum.setText("")
|
||||
# self.form.propertyName.setFocus()
|
||||
|
||||
self.updateUI()
|
||||
|
||||
return self.form.exec_()
|
||||
|
||||
|
||||
Panel = []
|
||||
|
||||
|
||||
class TaskPanel(object):
|
||||
ColumnName = 0
|
||||
#ColumnType = 1
|
||||
ColumnVal = 1
|
||||
#TableHeaders = ['Property', 'Type', 'Value']
|
||||
TableHeaders = ['Property', 'Value']
|
||||
# ColumnType = 1
|
||||
ColumnVal = 1
|
||||
# TableHeaders = ['Property', 'Type', 'Value']
|
||||
TableHeaders = ["Property", "Value"]
|
||||
|
||||
def __init__(self, vobj):
|
||||
self.obj = vobj.Object
|
||||
self.obj = vobj.Object
|
||||
self.props = sorted(self.obj.Proxy.getCustomProperties())
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":panels/PropertyBag.ui")
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":panels/PropertyBag.ui")
|
||||
|
||||
# initialized later
|
||||
self.model = None
|
||||
self.model = None
|
||||
self.delegate = None
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("PathPropertyBag", "Edit PropertyBag"))
|
||||
FreeCAD.ActiveDocument.openTransaction("Edit PropertyBag")
|
||||
Panel.append(self)
|
||||
|
||||
def updateData(self, topLeft, bottomRight):
|
||||
pass
|
||||
|
||||
|
||||
def _setupProperty(self, i, name):
|
||||
typ = PathPropertyBag.getPropertyTypeName(self.obj.getTypeIdOfProperty(name))
|
||||
val = PathUtil.getPropertyValueString(self.obj, name)
|
||||
val = PathUtil.getPropertyValueString(self.obj, name)
|
||||
info = self.obj.getDocumentationOfProperty(name)
|
||||
|
||||
self.model.setData(self.model.index(i, self.ColumnName), name, QtCore.Qt.EditRole)
|
||||
#self.model.setData(self.model.index(i, self.ColumnType), typ, QtCore.Qt.EditRole)
|
||||
self.model.setData(self.model.index(i, self.ColumnVal), self.obj, Delegate.RoleObject)
|
||||
self.model.setData(self.model.index(i, self.ColumnVal), name, Delegate.RoleProperty)
|
||||
self.model.setData(self.model.index(i, self.ColumnVal), val, QtCore.Qt.DisplayRole)
|
||||
self.model.setData(
|
||||
self.model.index(i, self.ColumnName), name, QtCore.Qt.EditRole
|
||||
)
|
||||
# self.model.setData(self.model.index(i, self.ColumnType), typ, QtCore.Qt.EditRole)
|
||||
self.model.setData(
|
||||
self.model.index(i, self.ColumnVal), self.obj, Delegate.RoleObject
|
||||
)
|
||||
self.model.setData(
|
||||
self.model.index(i, self.ColumnVal), name, Delegate.RoleProperty
|
||||
)
|
||||
self.model.setData(
|
||||
self.model.index(i, self.ColumnVal), val, QtCore.Qt.DisplayRole
|
||||
)
|
||||
|
||||
self.model.setData(self.model.index(i, self.ColumnName), typ, QtCore.Qt.ToolTipRole)
|
||||
#self.model.setData(self.model.index(i, self.ColumnType), info, QtCore.Qt.ToolTipRole)
|
||||
self.model.setData(self.model.index(i, self.ColumnVal), info, QtCore.Qt.ToolTipRole)
|
||||
self.model.setData(
|
||||
self.model.index(i, self.ColumnName), typ, QtCore.Qt.ToolTipRole
|
||||
)
|
||||
# self.model.setData(self.model.index(i, self.ColumnType), info, QtCore.Qt.ToolTipRole)
|
||||
self.model.setData(
|
||||
self.model.index(i, self.ColumnVal), info, QtCore.Qt.ToolTipRole
|
||||
)
|
||||
|
||||
self.model.item(i, self.ColumnName).setEditable(False)
|
||||
#self.model.item(i, self.ColumnType).setEditable(False)
|
||||
# self.model.item(i, self.ColumnType).setEditable(False)
|
||||
|
||||
def setupUi(self):
|
||||
PathLog.track()
|
||||
|
||||
self.delegate = Delegate(self.form)
|
||||
self.model = QtGui.QStandardItemModel(len(self.props), len(self.TableHeaders), self.form)
|
||||
self.model = QtGui.QStandardItemModel(
|
||||
len(self.props), len(self.TableHeaders), self.form
|
||||
)
|
||||
self.model.setHorizontalHeaderLabels(self.TableHeaders)
|
||||
|
||||
for i,name in enumerate(self.props):
|
||||
for i, name in enumerate(self.props):
|
||||
self._setupProperty(i, name)
|
||||
|
||||
self.form.table.setModel(self.model)
|
||||
@@ -301,8 +329,8 @@ class TaskPanel(object):
|
||||
|
||||
def addCustomProperty(self, obj, dialog):
|
||||
name = dialog.propertyName()
|
||||
typ = dialog.propertyType()
|
||||
grp = dialog.propertyGroup()
|
||||
typ = dialog.propertyType()
|
||||
grp = dialog.propertyGroup()
|
||||
info = dialog.propertyInfo()
|
||||
propname = self.obj.Proxy.addCustomProperty(typ, name, grp, info)
|
||||
if dialog.propertyIsEnumeration():
|
||||
@@ -318,17 +346,22 @@ class TaskPanel(object):
|
||||
dialog = PropertyCreate(self.obj, grp, typ, more)
|
||||
if dialog.exec_(None):
|
||||
# if we block signals the view doesn't get updated, surprise, surprise
|
||||
#self.model.blockSignals(True)
|
||||
# self.model.blockSignals(True)
|
||||
name, info = self.addCustomProperty(self.obj, dialog)
|
||||
index = 0
|
||||
for i in range(self.model.rowCount()):
|
||||
index = i
|
||||
if self.model.item(i, self.ColumnName).data(QtCore.Qt.EditRole) > dialog.propertyName():
|
||||
if (
|
||||
self.model.item(i, self.ColumnName).data(QtCore.Qt.EditRole)
|
||||
> dialog.propertyName()
|
||||
):
|
||||
break
|
||||
self.model.insertRows(index, 1)
|
||||
self._setupProperty(index, name)
|
||||
self.form.table.selectionModel().setCurrentIndex(self.model.index(index, 0), QtCore.QItemSelectionModel.Rows)
|
||||
#self.model.blockSignals(False)
|
||||
self.form.table.selectionModel().setCurrentIndex(
|
||||
self.model.index(index, 0), QtCore.QItemSelectionModel.Rows
|
||||
)
|
||||
# self.model.blockSignals(False)
|
||||
more = dialog.createAnother()
|
||||
else:
|
||||
more = False
|
||||
@@ -355,10 +388,14 @@ class TaskPanel(object):
|
||||
# this can happen if the old enumeration value doesn't exist anymore
|
||||
pass
|
||||
newVal = PathUtil.getPropertyValueString(obj, nam)
|
||||
self.model.setData(self.model.index(row, self.ColumnVal), newVal, QtCore.Qt.DisplayRole)
|
||||
self.model.setData(
|
||||
self.model.index(row, self.ColumnVal), newVal, QtCore.Qt.DisplayRole
|
||||
)
|
||||
|
||||
#self.model.setData(self.model.index(row, self.ColumnType), info, QtCore.Qt.ToolTipRole)
|
||||
self.model.setData(self.model.index(row, self.ColumnVal), info, QtCore.Qt.ToolTipRole)
|
||||
# self.model.setData(self.model.index(row, self.ColumnType), info, QtCore.Qt.ToolTipRole)
|
||||
self.model.setData(
|
||||
self.model.index(row, self.ColumnVal), info, QtCore.Qt.ToolTipRole
|
||||
)
|
||||
|
||||
def propertyModify(self):
|
||||
PathLog.track()
|
||||
@@ -371,7 +408,6 @@ class TaskPanel(object):
|
||||
|
||||
self.propertyModifyIndex(index)
|
||||
|
||||
|
||||
def propertyRemove(self):
|
||||
PathLog.track()
|
||||
# first find all rows which need to be removed
|
||||
@@ -387,24 +423,31 @@ class TaskPanel(object):
|
||||
self.model.removeRow(row)
|
||||
|
||||
|
||||
def Create(name = 'PropertyBag'):
|
||||
'''Create(name = 'PropertyBag') ... creates a new setup sheet'''
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("PathPropertyBag", "Create PropertyBag"))
|
||||
def Create(name="PropertyBag"):
|
||||
"""Create(name = 'PropertyBag') ... creates a new setup sheet"""
|
||||
FreeCAD.ActiveDocument.openTransaction("Create PropertyBag")
|
||||
pcont = PathPropertyBag.Create(name)
|
||||
PathIconViewProvider.Attach(pcont.ViewObject, name)
|
||||
return pcont
|
||||
|
||||
PathIconViewProvider.RegisterViewProvider('PropertyBag', ViewProvider)
|
||||
|
||||
PathIconViewProvider.RegisterViewProvider("PropertyBag", ViewProvider)
|
||||
|
||||
|
||||
class PropertyBagCreateCommand(object):
|
||||
'''Command to create a property container object'''
|
||||
"""Command to create a property container object"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetResources(self):
|
||||
return {'MenuText': translate('PathPropertyBag', 'PropertyBag'),
|
||||
'ToolTip': translate('PathPropertyBag', 'Creates an object which can be used to store reference properties.')}
|
||||
return {
|
||||
"MenuText": translate("Path_PropertyBag", "PropertyBag"),
|
||||
"ToolTip": translate(
|
||||
"Path_PropertyBag",
|
||||
"Creates an object which can be used to store reference properties.",
|
||||
),
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
@@ -414,17 +457,18 @@ class PropertyBagCreateCommand(object):
|
||||
obj = Create()
|
||||
body = None
|
||||
if sel:
|
||||
if 'PartDesign::Body' == sel[0].Object.TypeId:
|
||||
if "PartDesign::Body" == sel[0].Object.TypeId:
|
||||
body = sel[0].Object
|
||||
elif hasattr(sel[0].Object, 'getParentGeoFeatureGroup'):
|
||||
elif hasattr(sel[0].Object, "getParentGeoFeatureGroup"):
|
||||
body = sel[0].Object.getParentGeoFeatureGroup()
|
||||
if body:
|
||||
obj.Label = 'Attributes'
|
||||
obj.Label = "Attributes"
|
||||
group = body.Group
|
||||
group.append(obj)
|
||||
body.Group = group
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('Path_PropertyBag', PropertyBagCreateCommand())
|
||||
FreeCADGui.addCommand("Path_PropertyBag", PropertyBagCreateCommand())
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathPropertyBagGui ... done\n")
|
||||
|
||||
@@ -44,13 +44,14 @@ except ImportError:
|
||||
# import sys
|
||||
# sys.exit(msg)
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import Path
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathSurfaceSupport as PathSurfaceSupport
|
||||
import time
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import math
|
||||
import time
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
@@ -60,13 +61,14 @@ Part = LazyLoader("Part", globals(), "Part")
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
class ObjectSurface(PathOp.ObjectOp):
|
||||
@@ -99,7 +101,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
|
||||
def initOpProperties(self, obj, warn=False):
|
||||
"""initOpProperties(obj) ... create operation specific properties"""
|
||||
self.addNewProps = list()
|
||||
self.addNewProps = []
|
||||
|
||||
for (prtyp, nm, grp, tt) in self.opPropertyDefinitions():
|
||||
if not hasattr(obj, nm):
|
||||
@@ -107,17 +109,14 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
self.addNewProps.append(nm)
|
||||
|
||||
# Set enumeration lists for enumeration properties
|
||||
if len(self.addNewProps) > 0:
|
||||
ENUMS = self.opPropertyEnumerations()
|
||||
for n in ENUMS:
|
||||
if n in self.addNewProps:
|
||||
setattr(obj, n, ENUMS[n])
|
||||
for n in self.propertyEnumerations():
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
if warn:
|
||||
newPropMsg = translate("PathSurface", "New property added to")
|
||||
newPropMsg += ' "{}": {}'.format(obj.Label, self.addNewProps) + ". "
|
||||
newPropMsg += translate("PathSurface", "Check default value(s).")
|
||||
FreeCAD.Console.PrintWarning(newPropMsg + "\n")
|
||||
# if warn:
|
||||
# newPropMsg = translate("PathSurface", "New property added to")
|
||||
# newPropMsg += ' "{}": {}'.format(obj.Label, self.addNewProps) + ". "
|
||||
# newPropMsg += translate("PathSurface", "Check default value(s).")
|
||||
# FreeCAD.Console.PrintWarning(newPropMsg + "\n")
|
||||
|
||||
self.propertiesReady = True
|
||||
|
||||
@@ -129,7 +128,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"ShowTempObjects",
|
||||
"Debug",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Show the temporary path construction objects when module is in DEBUG mode.",
|
||||
),
|
||||
@@ -138,7 +137,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"AngularDeflection",
|
||||
"Mesh Conversion",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Smaller values yield a finer, more accurate mesh. Smaller values increase processing time a lot.",
|
||||
),
|
||||
@@ -147,7 +146,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"LinearDeflection",
|
||||
"Mesh Conversion",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Smaller values yield a finer, more accurate mesh. Smaller values do not increase processing time much.",
|
||||
),
|
||||
@@ -156,7 +155,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyFloat",
|
||||
"CutterTilt",
|
||||
"Rotation",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Stop index(angle) for rotational scan"
|
||||
),
|
||||
),
|
||||
@@ -164,7 +163,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"DropCutterDir",
|
||||
"Rotation",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Dropcutter lines are created parallel to this axis.",
|
||||
),
|
||||
@@ -173,7 +172,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyVectorDistance",
|
||||
"DropCutterExtraOffset",
|
||||
"Rotation",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Additional offset to the selected bounding box"
|
||||
),
|
||||
),
|
||||
@@ -181,7 +180,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"RotationAxis",
|
||||
"Rotation",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "The model will be rotated around this axis."
|
||||
),
|
||||
),
|
||||
@@ -189,7 +188,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyFloat",
|
||||
"StartIndex",
|
||||
"Rotation",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Start index(angle) for rotational scan"
|
||||
),
|
||||
),
|
||||
@@ -197,7 +196,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyFloat",
|
||||
"StopIndex",
|
||||
"Rotation",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Stop index(angle) for rotational scan"
|
||||
),
|
||||
),
|
||||
@@ -205,7 +204,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"ScanType",
|
||||
"Surface",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Planar: Flat, 3D surface scan. Rotational: 4th-axis rotational scan.",
|
||||
),
|
||||
@@ -214,7 +213,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyInteger",
|
||||
"AvoidLastX_Faces",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Avoid cutting the last 'N' faces in the Base Geometry list of selected faces.",
|
||||
),
|
||||
@@ -223,7 +222,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"AvoidLastX_InternalFeatures",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Do not cut internal features on avoided faces."
|
||||
),
|
||||
),
|
||||
@@ -231,7 +230,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"BoundaryAdjustment",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Positive values push the cutter toward, or beyond, the boundary. Negative values retract the cutter away from the boundary.",
|
||||
),
|
||||
@@ -240,7 +239,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"BoundaryEnforcement",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"If true, the cutter will remain inside the boundaries of the model or selected face(s).",
|
||||
),
|
||||
@@ -249,7 +248,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"HandleMultipleFeatures",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Choose how to process multiple Base Geometry features.",
|
||||
),
|
||||
@@ -258,7 +257,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"InternalFeaturesAdjustment",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Positive values push the cutter toward, or into, the feature. Negative values retract the cutter away from the feature.",
|
||||
),
|
||||
@@ -267,7 +266,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"InternalFeaturesCut",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Cut internal feature areas within a larger selected face.",
|
||||
),
|
||||
@@ -276,7 +275,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"BoundBox",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Select the overall boundary for the operation."
|
||||
),
|
||||
),
|
||||
@@ -284,7 +283,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"CutMode",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set the direction for the cutting tool to engage the material: Climb (ClockWise) or Conventional (CounterClockWise)",
|
||||
),
|
||||
@@ -293,7 +292,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"CutPattern",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set the geometric clearing pattern to use for the operation.",
|
||||
),
|
||||
@@ -302,7 +301,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyFloat",
|
||||
"CutPatternAngle",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "The yaw angle used for certain clearing patterns"
|
||||
),
|
||||
),
|
||||
@@ -310,7 +309,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"CutPatternReversed",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Reverse the cut order of the stepover paths. For circular cut patterns, begin at the outside and work toward the center.",
|
||||
),
|
||||
@@ -319,7 +318,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"DepthOffset",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set the Z-axis depth offset from the target surface.",
|
||||
),
|
||||
@@ -328,7 +327,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"LayerMode",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Complete the operation in a single pass at depth, or mulitiple passes to final depth.",
|
||||
),
|
||||
@@ -337,7 +336,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyVectorDistance",
|
||||
"PatternCenterCustom",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Set the start point for the cut pattern."
|
||||
),
|
||||
),
|
||||
@@ -345,7 +344,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"PatternCenterAt",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Choose location of the center point for starting the cut pattern.",
|
||||
),
|
||||
@@ -354,7 +353,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"ProfileEdges",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Profile the edges of the selection."
|
||||
),
|
||||
),
|
||||
@@ -362,7 +361,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"SampleInterval",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set the sampling resolution. Smaller values quickly increase processing time.",
|
||||
),
|
||||
@@ -371,7 +370,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyFloat",
|
||||
"StepOver",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set the stepover percentage, based on the tool's diameter.",
|
||||
),
|
||||
@@ -380,7 +379,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"OptimizeLinearPaths",
|
||||
"Optimization",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Enable optimization of linear paths (co-linear points). Removes unnecessary co-linear points from G-Code output.",
|
||||
),
|
||||
@@ -389,7 +388,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"OptimizeStepOverTransitions",
|
||||
"Optimization",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Enable separate optimization of transitions between, and breaks within, each step over path.",
|
||||
),
|
||||
@@ -398,7 +397,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"CircularUseG2G3",
|
||||
"Optimization",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Convert co-planar arcs to G2/G3 gcode commands for `Circular` and `CircularZigZag` cut patterns.",
|
||||
),
|
||||
@@ -407,7 +406,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"GapThreshold",
|
||||
"Optimization",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Collinear and co-radial artifact gaps that are smaller than this threshold are closed in the path.",
|
||||
),
|
||||
@@ -416,7 +415,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyString",
|
||||
"GapSizes",
|
||||
"Optimization",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Feedback: three smallest gaps identified in the path geometry.",
|
||||
),
|
||||
@@ -425,7 +424,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyVectorDistance",
|
||||
"StartPoint",
|
||||
"Start Point",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"The custom start point for the path of this operation",
|
||||
),
|
||||
@@ -434,39 +433,86 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"UseStartPoint",
|
||||
"Start Point",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Make True, if specifying a Start Point"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
def opPropertyEnumerations(self):
|
||||
@classmethod
|
||||
def propertyEnumerations(self, dataType="data"):
|
||||
"""propertyEnumerations(dataType="data")... return property enumeration lists of specified dataType.
|
||||
Args:
|
||||
dataType = 'data', 'raw', 'translated'
|
||||
Notes:
|
||||
'data' is list of internal string literals used in code
|
||||
'raw' is list of (translated_text, data_string) tuples
|
||||
'translated' is list of translated string literals
|
||||
"""
|
||||
|
||||
# Enumeration lists for App::PropertyEnumeration properties
|
||||
return {
|
||||
"BoundBox": ["BaseBoundBox", "Stock"],
|
||||
"PatternCenterAt": [
|
||||
"CenterOfMass",
|
||||
"CenterOfBoundBox",
|
||||
"XminYmin",
|
||||
"Custom",
|
||||
enums = {
|
||||
"BoundBox": [
|
||||
(translate("Path_Surface", "BaseBoundBox"), "BaseBoundBox"),
|
||||
(translate("Path_Surface", "Stock"), "Stock"),
|
||||
],
|
||||
"PatternCenterAt": [
|
||||
(translate("Path_Surface", "CenterOfMass"), "CenterOfMass"),
|
||||
(translate("Path_Surface", "CenterOfBoundBox"), "CenterOfBoundBox"),
|
||||
(translate("Path_Surface", "XminYmin"), "XminYmin"),
|
||||
(translate("Path_Surface", "Custom"), "Custom"),
|
||||
],
|
||||
"CutMode": [
|
||||
(translate("Path_Surface", "Conventional"), "Conventional"),
|
||||
(translate("Path_Surface", "Climb"), "Climb"),
|
||||
],
|
||||
"CutMode": ["Conventional", "Climb"],
|
||||
"CutPattern": [
|
||||
"Circular",
|
||||
"CircularZigZag",
|
||||
"Line",
|
||||
"Offset",
|
||||
"Spiral",
|
||||
"ZigZag",
|
||||
], # Additional goals ['Offset', 'ZigZagOffset', 'Grid', 'Triangle']
|
||||
"DropCutterDir": ["X", "Y"],
|
||||
"HandleMultipleFeatures": ["Collectively", "Individually"],
|
||||
"LayerMode": ["Single-pass", "Multi-pass"],
|
||||
"ProfileEdges": ["None", "Only", "First", "Last"],
|
||||
"RotationAxis": ["X", "Y"],
|
||||
"ScanType": ["Planar", "Rotational"],
|
||||
(translate("Path_Surface", "Circular"), "Circular"),
|
||||
(translate("Path_Surface", "CircularZigZag"), "CircularZigZag"),
|
||||
(translate("Path_Surface", "Line"), "Line"),
|
||||
(translate("Path_Surface", "Offset"), "Offset"),
|
||||
(translate("Path_Surface", "Spiral"), "Spiral"),
|
||||
(translate("Path_Surface", "ZigZag"), "ZigZag"),
|
||||
],
|
||||
"DropCutterDir": [
|
||||
(translate("Path_Surface", "X"), "X"),
|
||||
(translate("Path_Surface", "Y"), "Y"),
|
||||
],
|
||||
"HandleMultipleFeatures": [
|
||||
(translate("Path_Surface", "Collectively"), "Collectively"),
|
||||
(translate("Path_Surface", "Individually"), "Individually"),
|
||||
],
|
||||
"LayerMode": [
|
||||
(translate("Path_Surface", "Single-pass"), "Single-pass"),
|
||||
(translate("Path_Surface", "Multi-pass"), "Multi-pass"),
|
||||
],
|
||||
"ProfileEdges": [
|
||||
(translate("Path_Surface", "None"), "None"),
|
||||
(translate("Path_Surface", "Only"), "Only"),
|
||||
(translate("Path_Surface", "First"), "First"),
|
||||
(translate("Path_Surface", "Last"), "Last"),
|
||||
],
|
||||
"RotationAxis": [
|
||||
(translate("Path_Surface", "X"), "X"),
|
||||
(translate("Path_Surface", "Y"), "Y"),
|
||||
],
|
||||
"ScanType": [
|
||||
(translate("Path_Surface", "Planar"), "Planar"),
|
||||
(translate("Path_Surface", "Rotational"), "Rotational"),
|
||||
],
|
||||
}
|
||||
|
||||
if dataType == "raw":
|
||||
return enums
|
||||
|
||||
data = []
|
||||
idx = 0 if dataType == "translated" else 1
|
||||
|
||||
for k, v in enumerate(enums):
|
||||
data.append((v, [tup[idx] for tup in enums[v]]))
|
||||
|
||||
return data
|
||||
|
||||
def opPropertyDefaults(self, obj, job):
|
||||
"""opPropertyDefaults(obj, job) ... returns a dictionary of default values
|
||||
for the operation's properties."""
|
||||
@@ -646,32 +692,20 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
# Limit sample interval
|
||||
if obj.SampleInterval.Value < 0.0001:
|
||||
obj.SampleInterval.Value = 0.0001
|
||||
PathLog.error(
|
||||
translate(
|
||||
"PathSurface",
|
||||
"Sample interval limits are 0.001 to 25.4 millimeters.",
|
||||
)
|
||||
)
|
||||
PathLog.error("Sample interval limits are 0.001 to 25.4 millimeters.")
|
||||
|
||||
if obj.SampleInterval.Value > 25.4:
|
||||
obj.SampleInterval.Value = 25.4
|
||||
PathLog.error(
|
||||
translate(
|
||||
"PathSurface",
|
||||
"Sample interval limits are 0.001 to 25.4 millimeters.",
|
||||
)
|
||||
)
|
||||
PathLog.error("Sample interval limits are 0.001 to 25.4 millimeters.")
|
||||
|
||||
# Limit cut pattern angle
|
||||
if obj.CutPatternAngle < -360.0:
|
||||
obj.CutPatternAngle = 0.0
|
||||
PathLog.error(
|
||||
translate("PathSurface", "Cut pattern angle limits are +-360 degrees.")
|
||||
)
|
||||
PathLog.error("Cut pattern angle limits are +-360 degrees.")
|
||||
|
||||
if obj.CutPatternAngle >= 360.0:
|
||||
obj.CutPatternAngle = 0.0
|
||||
PathLog.error(
|
||||
translate("PathSurface", "Cut pattern angle limits are +- 360 degrees.")
|
||||
)
|
||||
PathLog.error("Cut pattern angle limits are +- 360 degrees.")
|
||||
|
||||
# Limit StepOver to natural number percentage
|
||||
if obj.StepOver > 100.0:
|
||||
@@ -682,20 +716,11 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
# Limit AvoidLastX_Faces to zero and positive values
|
||||
if obj.AvoidLastX_Faces < 0:
|
||||
obj.AvoidLastX_Faces = 0
|
||||
PathLog.error(
|
||||
translate(
|
||||
"PathSurface",
|
||||
"AvoidLastX_Faces: Only zero or positive values permitted.",
|
||||
)
|
||||
)
|
||||
PathLog.error("AvoidLastX_Faces: Only zero or positive values permitted.")
|
||||
|
||||
if obj.AvoidLastX_Faces > 100:
|
||||
obj.AvoidLastX_Faces = 100
|
||||
PathLog.error(
|
||||
translate(
|
||||
"PathSurface",
|
||||
"AvoidLastX_Faces: Avoid last X faces count limited to 100.",
|
||||
)
|
||||
)
|
||||
PathLog.error("AvoidLastX_Faces: Avoid last X faces count limited to 100.")
|
||||
|
||||
def opUpdateDepths(self, obj):
|
||||
if hasattr(obj, "Base") and obj.Base:
|
||||
@@ -726,22 +751,22 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"""opExecute(obj) ... process surface operation"""
|
||||
PathLog.track()
|
||||
|
||||
self.modelSTLs = list()
|
||||
self.safeSTLs = list()
|
||||
self.modelTypes = list()
|
||||
self.boundBoxes = list()
|
||||
self.profileShapes = list()
|
||||
self.collectiveShapes = list()
|
||||
self.individualShapes = list()
|
||||
self.avoidShapes = list()
|
||||
self.modelSTLs = []
|
||||
self.safeSTLs = []
|
||||
self.modelTypes = []
|
||||
self.boundBoxes = []
|
||||
self.profileShapes = []
|
||||
self.collectiveShapes = []
|
||||
self.individualShapes = []
|
||||
self.avoidShapes = []
|
||||
self.tempGroup = None
|
||||
self.CutClimb = False
|
||||
self.closedGap = False
|
||||
self.tmpCOM = None
|
||||
self.gaps = [0.1, 0.2, 0.3]
|
||||
self.cancelOperation = False
|
||||
CMDS = list()
|
||||
modelVisibility = list()
|
||||
CMDS = []
|
||||
modelVisibility = []
|
||||
FCAD = FreeCAD.ActiveDocument
|
||||
|
||||
try:
|
||||
@@ -876,26 +901,25 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
|
||||
# Save model visibilities for restoration
|
||||
if FreeCAD.GuiUp:
|
||||
for m in range(0, len(JOB.Model.Group)):
|
||||
mNm = JOB.Model.Group[m].Name
|
||||
for model in JOB.Model.Group:
|
||||
mNm = model.Name
|
||||
modelVisibility.append(
|
||||
FreeCADGui.ActiveDocument.getObject(mNm).Visibility
|
||||
)
|
||||
|
||||
# Setup STL, model type, and bound box containers for each model in Job
|
||||
for m in range(0, len(JOB.Model.Group)):
|
||||
M = JOB.Model.Group[m]
|
||||
for model in JOB.Model.Group:
|
||||
self.modelSTLs.append(False)
|
||||
self.safeSTLs.append(False)
|
||||
self.profileShapes.append(False)
|
||||
# Set bound box
|
||||
if obj.BoundBox == "BaseBoundBox":
|
||||
if M.TypeId.startswith("Mesh"):
|
||||
if model.TypeId.startswith("Mesh"):
|
||||
self.modelTypes.append("M") # Mesh
|
||||
self.boundBoxes.append(M.Mesh.BoundBox)
|
||||
self.boundBoxes.append(model.Mesh.BoundBox)
|
||||
else:
|
||||
self.modelTypes.append("S") # Solid
|
||||
self.boundBoxes.append(M.Shape.BoundBox)
|
||||
self.boundBoxes.append(model.Shape.BoundBox)
|
||||
elif obj.BoundBox == "Stock":
|
||||
self.modelTypes.append("S") # Solid
|
||||
self.boundBoxes.append(JOB.Stock.Shape.BoundBox)
|
||||
@@ -916,18 +940,20 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
self.modelSTLs = PSF.modelSTLs
|
||||
self.profileShapes = PSF.profileShapes
|
||||
|
||||
for m in range(0, len(JOB.Model.Group)):
|
||||
for idx, model in enumerate(JOB.Model.Group):
|
||||
PathLog.debug(idx)
|
||||
# Create OCL.stl model objects
|
||||
PathSurfaceSupport._prepareModelSTLs(self, JOB, obj, m, ocl)
|
||||
PathSurfaceSupport._prepareModelSTLs(self, JOB, obj, idx, ocl)
|
||||
|
||||
Mdl = JOB.Model.Group[m]
|
||||
if FACES[m]:
|
||||
PathLog.debug("Working on Model.Group[{}]: {}".format(m, Mdl.Label))
|
||||
if m > 0:
|
||||
if FACES[idx]:
|
||||
PathLog.debug(
|
||||
"Working on Model.Group[{}]: {}".format(idx, model.Label)
|
||||
)
|
||||
if idx > 0:
|
||||
# Raise to clearance between models
|
||||
CMDS.append(
|
||||
Path.Command(
|
||||
"N (Transition to base: {}.)".format(Mdl.Label)
|
||||
"N (Transition to base: {}.)".format(model.Label)
|
||||
)
|
||||
)
|
||||
CMDS.append(
|
||||
@@ -938,14 +964,14 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
)
|
||||
# make stock-model-voidShapes STL model for avoidance detection on transitions
|
||||
PathSurfaceSupport._makeSafeSTL(
|
||||
self, JOB, obj, m, FACES[m], VOIDS[m], ocl
|
||||
self, JOB, obj, idx, FACES[idx], VOIDS[idx], ocl
|
||||
)
|
||||
# Process model/faces - OCL objects must be ready
|
||||
CMDS.extend(self._processCutAreas(JOB, obj, m, FACES[m], VOIDS[m]))
|
||||
else:
|
||||
PathLog.debug(
|
||||
"No data for model base: {}".format(JOB.Model.Group[m].Label)
|
||||
CMDS.extend(
|
||||
self._processCutAreas(JOB, obj, idx, FACES[idx], VOIDS[idx])
|
||||
)
|
||||
else:
|
||||
PathLog.debug("No data for model base: {}".format(model.Label))
|
||||
|
||||
# Save gcode produced
|
||||
self.commandlist.extend(CMDS)
|
||||
@@ -976,7 +1002,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
tempGroup.purgeTouched()
|
||||
|
||||
# Provide user feedback for gap sizes
|
||||
gaps = list()
|
||||
gaps = []
|
||||
for g in self.gaps:
|
||||
if g != self.toolDiam:
|
||||
gaps.append(g)
|
||||
@@ -1039,7 +1065,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
It then calls the correct scan method depending on the ScanType property."""
|
||||
PathLog.debug("_processCutAreas()")
|
||||
|
||||
final = list()
|
||||
final = []
|
||||
|
||||
# Process faces Collectively or Individually
|
||||
if obj.HandleMultipleFeatures == "Collectively":
|
||||
@@ -1091,8 +1117,8 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
It calls the correct Single or Multi-pass method as needed.
|
||||
It returns the gcode for the operation."""
|
||||
PathLog.debug("_processPlanarOp()")
|
||||
final = list()
|
||||
SCANDATA = list()
|
||||
final = []
|
||||
SCANDATA = []
|
||||
|
||||
def getTransition(two):
|
||||
first = two[0][0][0] # [step][item][point]
|
||||
@@ -1121,23 +1147,23 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
self.cutter,
|
||||
)
|
||||
|
||||
profScan = list()
|
||||
profScan = []
|
||||
if obj.ProfileEdges != "None":
|
||||
prflShp = self.profileShapes[mdlIdx][fsi]
|
||||
if prflShp is False:
|
||||
msg = translate("PathSurface", "No profile geometry shape returned.")
|
||||
PathLog.error(msg)
|
||||
return list()
|
||||
return []
|
||||
self.showDebugObject(prflShp, "NewProfileShape")
|
||||
# get offset path geometry and perform OCL scan with that geometry
|
||||
pathOffsetGeom = self._offsetFacesToPointData(obj, prflShp)
|
||||
if pathOffsetGeom is False:
|
||||
msg = translate("PathSurface", "No profile path geometry returned.")
|
||||
PathLog.error(msg)
|
||||
return list()
|
||||
return []
|
||||
profScan = [self._planarPerformOclScan(obj, pdc, pathOffsetGeom, True)]
|
||||
|
||||
geoScan = list()
|
||||
geoScan = []
|
||||
if obj.ProfileEdges != "Only":
|
||||
self.showDebugObject(cmpdShp, "CutArea")
|
||||
# get internal path geometry and perform OCL scan with that geometry
|
||||
@@ -1149,7 +1175,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
if pathGeom is False:
|
||||
msg = translate("PathSurface", "No clearing shape returned.")
|
||||
PathLog.error(msg)
|
||||
return list()
|
||||
return []
|
||||
if obj.CutPattern == "Offset":
|
||||
useGeom = self._offsetFacesToPointData(obj, pathGeom, profile=False)
|
||||
if useGeom is False:
|
||||
@@ -1157,7 +1183,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
"PathSurface", "No clearing path geometry returned."
|
||||
)
|
||||
PathLog.error(msg)
|
||||
return list()
|
||||
return []
|
||||
geoScan = [self._planarPerformOclScan(obj, pdc, useGeom, True)]
|
||||
else:
|
||||
geoScan = self._planarPerformOclScan(obj, pdc, pathGeom, False)
|
||||
@@ -1177,7 +1203,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
if len(SCANDATA) == 0:
|
||||
msg = translate("PathSurface", "No scan data to convert to Gcode.")
|
||||
PathLog.error(msg)
|
||||
return list()
|
||||
return []
|
||||
|
||||
# Apply depth offset
|
||||
if obj.DepthOffset.Value != 0.0:
|
||||
@@ -1214,7 +1240,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
def _offsetFacesToPointData(self, obj, subShp, profile=True):
|
||||
PathLog.debug("_offsetFacesToPointData()")
|
||||
|
||||
offsetLists = list()
|
||||
offsetLists = []
|
||||
dist = obj.SampleInterval.Value / 5.0
|
||||
# defl = obj.SampleInterval.Value / 5.0
|
||||
|
||||
@@ -1246,18 +1272,18 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
Switching function for calling the appropriate path-geometry to OCL points conversion function
|
||||
for the various cut patterns."""
|
||||
PathLog.debug("_planarPerformOclScan()")
|
||||
SCANS = list()
|
||||
SCANS = []
|
||||
|
||||
if offsetPoints or obj.CutPattern == "Offset":
|
||||
PNTSET = PathSurfaceSupport.pathGeomToOffsetPointSet(obj, pathGeom)
|
||||
for D in PNTSET:
|
||||
stpOvr = list()
|
||||
ofst = list()
|
||||
stpOvr = []
|
||||
ofst = []
|
||||
for I in D:
|
||||
if I == "BRK":
|
||||
stpOvr.append(ofst)
|
||||
stpOvr.append(I)
|
||||
ofst = list()
|
||||
ofst = []
|
||||
else:
|
||||
# D format is ((p1, p2), (p3, p4))
|
||||
(A, B) = I
|
||||
@@ -1266,7 +1292,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
stpOvr.append(ofst)
|
||||
SCANS.extend(stpOvr)
|
||||
elif obj.CutPattern in ["Line", "Spiral", "ZigZag"]:
|
||||
stpOvr = list()
|
||||
stpOvr = []
|
||||
if obj.CutPattern == "Line":
|
||||
# PNTSET = PathSurfaceSupport.pathGeomToLinesPointSet(obj, pathGeom, self.CutClimb, self.toolDiam, self.closedGap, self.gaps)
|
||||
PNTSET = PathSurfaceSupport.pathGeomToLinesPointSet(self, obj, pathGeom)
|
||||
@@ -1287,7 +1313,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
(A, B) = LN
|
||||
stpOvr.append(self._planarDropCutScan(pdc, A, B))
|
||||
SCANS.append(stpOvr)
|
||||
stpOvr = list()
|
||||
stpOvr = []
|
||||
elif obj.CutPattern in ["Circular", "CircularZigZag"]:
|
||||
# PNTSET is list, by stepover.
|
||||
# Each stepover is a list containing arc/loop descriptions, (sp, ep, cp)
|
||||
@@ -1295,7 +1321,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
PNTSET = PathSurfaceSupport.pathGeomToCircularPointSet(self, obj, pathGeom)
|
||||
|
||||
for so in range(0, len(PNTSET)):
|
||||
stpOvr = list()
|
||||
stpOvr = []
|
||||
erFlg = False
|
||||
(aTyp, dirFlg, ARCS) = PNTSET[so]
|
||||
|
||||
@@ -1386,7 +1412,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
odd = True
|
||||
lstStpEnd = None
|
||||
for so in range(0, lenSCANDATA):
|
||||
cmds = list()
|
||||
cmds = []
|
||||
PRTS = SCANDATA[so]
|
||||
lenPRTS = len(PRTS)
|
||||
first = PRTS[0][0] # first point of arc/line stepover group
|
||||
@@ -1489,7 +1515,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
odd = True # ZigZag directional switch
|
||||
lyrHasCmds = False
|
||||
actvSteps = 0
|
||||
LYR = list()
|
||||
LYR = []
|
||||
# if lyr > 0:
|
||||
# if prvStpLast is not None:
|
||||
# lastPrvStpLast = prvStpLast
|
||||
@@ -1503,8 +1529,8 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
lenSO = len(SO)
|
||||
|
||||
# Pre-process step-over parts for layer depth and holds
|
||||
ADJPRTS = list()
|
||||
LMAX = list()
|
||||
ADJPRTS = []
|
||||
LMAX = []
|
||||
soHasPnts = False
|
||||
brkFlg = False
|
||||
for i in range(0, lenSO):
|
||||
@@ -1530,9 +1556,9 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
# Process existing parts within current step over
|
||||
prtsHasCmds = False
|
||||
stepHasCmds = False
|
||||
prtsCmds = list()
|
||||
stpOvrCmds = list()
|
||||
transCmds = list()
|
||||
prtsCmds = []
|
||||
stpOvrCmds = []
|
||||
transCmds = []
|
||||
if soHasPnts is True:
|
||||
first = ADJPRTS[0][0] # first point of arc/line stepover group
|
||||
last = None
|
||||
@@ -1657,8 +1683,8 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
return GCODE
|
||||
|
||||
def _planarMultipassPreProcess(self, obj, LN, prvDep, layDep):
|
||||
ALL = list()
|
||||
PTS = list()
|
||||
ALL = []
|
||||
PTS = []
|
||||
optLinTrans = obj.OptimizeStepOverTransitions
|
||||
safe = math.ceil(obj.SafeHeight.Value)
|
||||
|
||||
@@ -1685,7 +1711,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
|
||||
if optLinTrans is True:
|
||||
# Remove leading and trailing Hold Points
|
||||
popList = list()
|
||||
popList = []
|
||||
for i in range(0, len(PTS)): # identify leading string
|
||||
if PTS[i].z == safe:
|
||||
popList.append(i)
|
||||
@@ -1695,7 +1721,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
for p in popList: # Remove hold points
|
||||
PTS.pop(p)
|
||||
ALL.pop(p)
|
||||
popList = list()
|
||||
popList = []
|
||||
for i in range(len(PTS) - 1, -1, -1): # identify trailing string
|
||||
if PTS[i].z == safe:
|
||||
popList.append(i)
|
||||
@@ -1717,7 +1743,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
return (PTS, lMax)
|
||||
|
||||
def _planarMultipassProcess(self, obj, PNTS, lMax):
|
||||
output = list()
|
||||
output = []
|
||||
optimize = obj.OptimizeLinearPaths
|
||||
safe = math.ceil(obj.SafeHeight.Value)
|
||||
lenPNTS = len(PNTS)
|
||||
@@ -1800,7 +1826,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
passes, as well as other kinds of breaks. When
|
||||
OptimizeStepOverTransitions is enabled, uses safePDC to safely optimize
|
||||
short (~order of cutter diameter) transitions."""
|
||||
cmds = list()
|
||||
cmds = []
|
||||
rtpd = False
|
||||
height = obj.SafeHeight.Value
|
||||
# Allow cutter-down transitions with a distance up to 2x cutter
|
||||
@@ -1869,7 +1895,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
return cmds
|
||||
|
||||
def _arcsToG2G3(self, LN, numPts, odd, gDIR, tolrnc):
|
||||
cmds = list()
|
||||
cmds = []
|
||||
strtPnt = LN[0]
|
||||
endPnt = LN[numPts - 1]
|
||||
strtHght = strtPnt.z
|
||||
|
||||
@@ -20,47 +20,69 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide import QtCore
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathScripts.PathSurface as PathSurface
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathScripts.PathGui as PathGui
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
import PathScripts.PathSurface as PathSurface
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
__title__ = "Path Surface Operation UI"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Surface operation page controller and command implementation."
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
'''Page controller class for the Surface operation.'''
|
||||
"""Page controller class for the Surface operation."""
|
||||
|
||||
def initPage(self, obj):
|
||||
self.setTitle("3D Surface - " + obj.Label)
|
||||
# self.updateVisibility()
|
||||
# retrieve property enumerations
|
||||
self.propEnums = PathSurface.ObjectSurface.opPropertyEnumerations(False)
|
||||
# self.propEnums = PathSurface.ObjectSurface.opPropertyEnumerations(False)
|
||||
self.propEnums = PathSurface.ObjectSurface.propertyEnumerations(False)
|
||||
|
||||
def getForm(self):
|
||||
'''getForm() ... returns UI'''
|
||||
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpSurfaceEdit.ui")
|
||||
"""getForm() ... returns UI"""
|
||||
form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpSurfaceEdit.ui")
|
||||
comboToPropertyMap = [
|
||||
("boundBoxSelect", "BoundBox"),
|
||||
("scanType", "ScanType"),
|
||||
("cutPattern", "CutPattern"),
|
||||
("profileEdges", "ProfileEdges"),
|
||||
("layerMode", "LayerMode"),
|
||||
("dropCutterDirSelect", "DropCutterDir"),
|
||||
]
|
||||
enumTups = PathSurface.ObjectSurface.propertyEnumerations(dataType="raw")
|
||||
PathGui.populateCombobox(form, enumTups, comboToPropertyMap)
|
||||
|
||||
return form
|
||||
|
||||
def getFields(self, obj):
|
||||
'''getFields(obj) ... transfers values from UI to obj's proprties'''
|
||||
"""getFields(obj) ... transfers values from UI to obj's proprties"""
|
||||
self.updateToolController(obj, self.form.toolController)
|
||||
self.updateCoolant(obj, self.form.coolantController)
|
||||
|
||||
if obj.BoundBox != str(self.form.boundBoxSelect.currentText()):
|
||||
obj.BoundBox = str(self.form.boundBoxSelect.currentText())
|
||||
if obj.BoundBox != str(self.form.boundBoxSelect.currentData()):
|
||||
obj.BoundBox = str(self.form.boundBoxSelect.currentData())
|
||||
|
||||
if obj.ScanType != str(self.form.scanType.currentText()):
|
||||
obj.ScanType = str(self.form.scanType.currentText())
|
||||
if obj.ScanType != str(self.form.scanType.currentData()):
|
||||
obj.ScanType = str(self.form.scanType.currentData())
|
||||
|
||||
if obj.LayerMode != str(self.form.layerMode.currentText()):
|
||||
obj.LayerMode = str(self.form.layerMode.currentText())
|
||||
if obj.LayerMode != str(self.form.layerMode.currentData()):
|
||||
obj.LayerMode = str(self.form.layerMode.currentData())
|
||||
|
||||
"""
|
||||
The following method of getting values from the UI form
|
||||
@@ -74,29 +96,36 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
This type of dynamic combobox population is done for the
|
||||
Tool Controller selection.
|
||||
"""
|
||||
val = self.propEnums['CutPattern'][self.form.cutPattern.currentIndex()]
|
||||
if obj.CutPattern != val:
|
||||
obj.CutPattern = val
|
||||
# val = self.propEnums["CutPattern"][self.form.cutPattern.currentIndex()]
|
||||
# if obj.CutPattern != val:
|
||||
# obj.CutPattern = val
|
||||
|
||||
val = self.propEnums['ProfileEdges'][self.form.profileEdges.currentIndex()]
|
||||
if obj.ProfileEdges != val:
|
||||
obj.ProfileEdges = val
|
||||
# val = self.propEnums["ProfileEdges"][self.form.profileEdges.currentIndex()]
|
||||
# if obj.ProfileEdges != val:
|
||||
# obj.ProfileEdges = val
|
||||
|
||||
obj.CutPattern = self.form.cutPattern.currentData()
|
||||
obj.ProfileEdges = self.form.profileEdges.currentData()
|
||||
|
||||
if obj.AvoidLastX_Faces != self.form.avoidLastX_Faces.value():
|
||||
obj.AvoidLastX_Faces = self.form.avoidLastX_Faces.value()
|
||||
|
||||
obj.DropCutterExtraOffset.x = FreeCAD.Units.Quantity(self.form.boundBoxExtraOffsetX.text()).Value
|
||||
obj.DropCutterExtraOffset.y = FreeCAD.Units.Quantity(self.form.boundBoxExtraOffsetY.text()).Value
|
||||
obj.DropCutterExtraOffset.x = FreeCAD.Units.Quantity(
|
||||
self.form.boundBoxExtraOffsetX.text()
|
||||
).Value
|
||||
obj.DropCutterExtraOffset.y = FreeCAD.Units.Quantity(
|
||||
self.form.boundBoxExtraOffsetY.text()
|
||||
).Value
|
||||
|
||||
if obj.DropCutterDir != str(self.form.dropCutterDirSelect.currentText()):
|
||||
obj.DropCutterDir = str(self.form.dropCutterDirSelect.currentText())
|
||||
if obj.DropCutterDir != str(self.form.dropCutterDirSelect.currentData()):
|
||||
obj.DropCutterDir = str(self.form.dropCutterDirSelect.currentData())
|
||||
|
||||
PathGui.updateInputField(obj, 'DepthOffset', self.form.depthOffset)
|
||||
PathGui.updateInputField(obj, "DepthOffset", self.form.depthOffset)
|
||||
|
||||
if obj.StepOver != self.form.stepOver.value():
|
||||
obj.StepOver = self.form.stepOver.value()
|
||||
|
||||
PathGui.updateInputField(obj, 'SampleInterval', self.form.sampleInterval)
|
||||
PathGui.updateInputField(obj, "SampleInterval", self.form.sampleInterval)
|
||||
|
||||
if obj.UseStartPoint != self.form.useStartPoint.isChecked():
|
||||
obj.UseStartPoint = self.form.useStartPoint.isChecked()
|
||||
@@ -107,11 +136,16 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
if obj.OptimizeLinearPaths != self.form.optimizeEnabled.isChecked():
|
||||
obj.OptimizeLinearPaths = self.form.optimizeEnabled.isChecked()
|
||||
|
||||
if obj.OptimizeStepOverTransitions != self.form.optimizeStepOverTransitions.isChecked():
|
||||
obj.OptimizeStepOverTransitions = self.form.optimizeStepOverTransitions.isChecked()
|
||||
if (
|
||||
obj.OptimizeStepOverTransitions
|
||||
!= self.form.optimizeStepOverTransitions.isChecked()
|
||||
):
|
||||
obj.OptimizeStepOverTransitions = (
|
||||
self.form.optimizeStepOverTransitions.isChecked()
|
||||
)
|
||||
|
||||
def setFields(self, obj):
|
||||
'''setFields(obj) ... transfers obj's property values to UI'''
|
||||
"""setFields(obj) ... transfers obj's property values to UI"""
|
||||
self.setupToolController(obj, self.form.toolController)
|
||||
self.setupCoolant(obj, self.form.coolantController)
|
||||
self.selectInComboBox(obj.BoundBox, self.form.boundBoxSelect)
|
||||
@@ -126,20 +160,36 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
and the UI panel QComboBox list.
|
||||
The original method is commented out below.
|
||||
"""
|
||||
idx = self.propEnums['CutPattern'].index(obj.CutPattern)
|
||||
self.form.cutPattern.setCurrentIndex(idx)
|
||||
idx = self.propEnums['ProfileEdges'].index(obj.ProfileEdges)
|
||||
self.form.profileEdges.setCurrentIndex(idx)
|
||||
# self.selectInComboBox(obj.CutPattern, self.form.cutPattern)
|
||||
# self.selectInComboBox(obj.ProfileEdges, self.form.profileEdges)
|
||||
# idx = self.propEnums["CutPattern"].index(obj.CutPattern)
|
||||
# self.form.cutPattern.setCurrentIndex(idx)
|
||||
# idx = self.propEnums["ProfileEdges"].index(obj.ProfileEdges)
|
||||
# self.form.profileEdges.setCurrentIndex(idx)
|
||||
self.selectInComboBox(obj.CutPattern, self.form.cutPattern)
|
||||
self.selectInComboBox(obj.ProfileEdges, self.form.profileEdges)
|
||||
|
||||
self.form.avoidLastX_Faces.setValue(obj.AvoidLastX_Faces)
|
||||
self.form.boundBoxExtraOffsetX.setText(FreeCAD.Units.Quantity(obj.DropCutterExtraOffset.x, FreeCAD.Units.Length).UserString)
|
||||
self.form.boundBoxExtraOffsetY.setText(FreeCAD.Units.Quantity(obj.DropCutterExtraOffset.y, FreeCAD.Units.Length).UserString)
|
||||
self.form.boundBoxExtraOffsetX.setText(
|
||||
FreeCAD.Units.Quantity(
|
||||
obj.DropCutterExtraOffset.x, FreeCAD.Units.Length
|
||||
).UserString
|
||||
)
|
||||
self.form.boundBoxExtraOffsetY.setText(
|
||||
FreeCAD.Units.Quantity(
|
||||
obj.DropCutterExtraOffset.y, FreeCAD.Units.Length
|
||||
).UserString
|
||||
)
|
||||
self.selectInComboBox(obj.DropCutterDir, self.form.dropCutterDirSelect)
|
||||
self.form.depthOffset.setText(FreeCAD.Units.Quantity(obj.DepthOffset.Value, FreeCAD.Units.Length).UserString)
|
||||
self.form.depthOffset.setText(
|
||||
FreeCAD.Units.Quantity(
|
||||
obj.DepthOffset.Value, FreeCAD.Units.Length
|
||||
).UserString
|
||||
)
|
||||
self.form.stepOver.setValue(obj.StepOver)
|
||||
self.form.sampleInterval.setText(FreeCAD.Units.Quantity(obj.SampleInterval.Value, FreeCAD.Units.Length).UserString)
|
||||
self.form.sampleInterval.setText(
|
||||
FreeCAD.Units.Quantity(
|
||||
obj.SampleInterval.Value, FreeCAD.Units.Length
|
||||
).UserString
|
||||
)
|
||||
|
||||
if obj.UseStartPoint:
|
||||
self.form.useStartPoint.setCheckState(QtCore.Qt.Checked)
|
||||
@@ -164,7 +214,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
self.updateVisibility()
|
||||
|
||||
def getSignalsForUpdate(self, obj):
|
||||
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
|
||||
"""getSignalsForUpdate(obj) ... return list of signals for updating obj"""
|
||||
signals = []
|
||||
signals.append(self.form.toolController.currentIndexChanged)
|
||||
signals.append(self.form.coolantController.currentIndexChanged)
|
||||
@@ -188,12 +238,12 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
return signals
|
||||
|
||||
def updateVisibility(self, sentObj=None):
|
||||
'''updateVisibility(sentObj=None)... Updates visibility of Tasks panel objects.'''
|
||||
if self.form.scanType.currentText() == 'Planar':
|
||||
"""updateVisibility(sentObj=None)... Updates visibility of Tasks panel objects."""
|
||||
if self.form.scanType.currentText() == "Planar":
|
||||
self.form.cutPattern.show()
|
||||
self.form.cutPattern_label.show()
|
||||
self.form.optimizeStepOverTransitions.show()
|
||||
if hasattr(self.form, 'profileEdges'):
|
||||
if hasattr(self.form, "profileEdges"):
|
||||
self.form.profileEdges.show()
|
||||
self.form.profileEdges_label.show()
|
||||
self.form.avoidLastX_Faces.show()
|
||||
@@ -204,11 +254,11 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
self.form.boundBoxExtraOffset_label.hide()
|
||||
self.form.dropCutterDirSelect.hide()
|
||||
self.form.dropCutterDirSelect_label.hide()
|
||||
elif self.form.scanType.currentText() == 'Rotational':
|
||||
elif self.form.scanType.currentText() == "Rotational":
|
||||
self.form.cutPattern.hide()
|
||||
self.form.cutPattern_label.hide()
|
||||
self.form.optimizeStepOverTransitions.hide()
|
||||
if hasattr(self.form, 'profileEdges'):
|
||||
if hasattr(self.form, "profileEdges"):
|
||||
self.form.profileEdges.hide()
|
||||
self.form.profileEdges_label.hide()
|
||||
self.form.avoidLastX_Faces.hide()
|
||||
@@ -224,12 +274,16 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
self.form.scanType.currentIndexChanged.connect(self.updateVisibility)
|
||||
|
||||
|
||||
Command = PathOpGui.SetupOperation('Surface',
|
||||
PathSurface.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path_3DSurface',
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Surface", "3D Surface"),
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Surface", "Create a 3D Surface Operation from a model"),
|
||||
PathSurface.SetupProperties)
|
||||
Command = PathOpGui.SetupOperation(
|
||||
"Surface",
|
||||
PathSurface.Create,
|
||||
TaskPanelOpPage,
|
||||
"Path_3DSurface",
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Surface", "3D Surface"),
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"Path_Surface", "Create a 3D Surface Operation from a model"
|
||||
),
|
||||
PathSurface.SetupProperties,
|
||||
)
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathSurfaceGui... done\n")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,8 @@
|
||||
# ***************************************************************************
|
||||
|
||||
from __future__ import print_function
|
||||
from PySide import QtCore
|
||||
import FreeCAD
|
||||
|
||||
__title__ = "Path Waterline Operation"
|
||||
__author__ = "russ4262 (Russell Johnson), sliptonic (Brad Collette)"
|
||||
@@ -29,28 +31,24 @@ __url__ = "http://www.freecadweb.org"
|
||||
__doc__ = "Class and implementation of Waterline operation."
|
||||
__contributors__ = ""
|
||||
|
||||
import FreeCAD
|
||||
from PySide import QtCore
|
||||
|
||||
# OCL must be installed
|
||||
try:
|
||||
import ocl
|
||||
except ImportError:
|
||||
msg = QtCore.QCoreApplication.translate(
|
||||
"PathWaterline", "This operation requires OpenCamLib to be installed."
|
||||
"path_waterline", "This operation requires OpenCamLib to be installed."
|
||||
)
|
||||
FreeCAD.Console.PrintError(msg + "\n")
|
||||
raise ImportError
|
||||
# import sys
|
||||
# sys.exit(msg)
|
||||
|
||||
import Path
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathSurfaceSupport as PathSurfaceSupport
|
||||
import time
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import math
|
||||
import time
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
@@ -60,13 +58,13 @@ Part = LazyLoader("Part", globals(), "Part")
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class ObjectWaterline(PathOp.ObjectOp):
|
||||
@@ -83,6 +81,79 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
| PathOp.FeatureBaseFaces
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def propertyEnumerations(self, dataType="data"):
|
||||
"""propertyEnumerations(dataType="data")... return property enumeration lists of specified dataType.
|
||||
Args:
|
||||
dataType = 'data', 'raw', 'translated'
|
||||
Notes:
|
||||
'data' is list of internal string literals used in code
|
||||
'raw' is list of (translated_text, data_string) tuples
|
||||
'translated' is list of translated string literals
|
||||
"""
|
||||
|
||||
# Enumeration lists for App::PropertyEnumeration properties
|
||||
enums = {
|
||||
"Algorithm": [
|
||||
(translate("path_waterline", "OCL Dropcutter"), "OCL Dropcutter"),
|
||||
(translate("path_waterline", "Experimental"), "Experimental"),
|
||||
],
|
||||
"BoundBox": [
|
||||
(translate("path_waterline", "BaseBoundBox"), "BaseBoundBox"),
|
||||
(translate("path_waterline", "Stock"), "Stock"),
|
||||
],
|
||||
"PatternCenterAt": [
|
||||
(translate("path_waterline", "CenterOfMass"), "CenterOfMass"),
|
||||
(translate("path_waterline", "CenterOfBoundBox"), "CenterOfBoundBox"),
|
||||
(translate("path_waterline", "XminYmin"), "XminYmin"),
|
||||
(translate("path_waterline", "Custom"), "Custom"),
|
||||
],
|
||||
"ClearLastLayer": [
|
||||
(translate("path_waterline", "Off"), "Off"),
|
||||
(translate("path_waterline", "Circular"), "Circular"),
|
||||
(translate("path_waterline", "CircularZigZag"), "CircularZigZag"),
|
||||
(translate("path_waterline", "Line"), "Line"),
|
||||
(translate("path_waterline", "Offset"), "Offset"),
|
||||
(translate("path_waterline", "Spiral"), "Spiral"),
|
||||
(translate("path_waterline", "ZigZag"), "ZigZag"),
|
||||
],
|
||||
"CutMode": [
|
||||
(translate("path_waterline", "Conventional"), "Conventional"),
|
||||
(translate("path_waterline", "Climb"), "Climb"),
|
||||
],
|
||||
"CutPattern": [
|
||||
(translate("path_waterline", "None"), "None"),
|
||||
(translate("path_waterline", "Circular"), "Circular"),
|
||||
(translate("path_waterline", "CircularZigZag"), "CircularZigZag"),
|
||||
(translate("path_waterline", "Line"), "Line"),
|
||||
(translate("path_waterline", "Offset"), "Offset"),
|
||||
(translate("path_waterline", "Spiral"), "Spiral"),
|
||||
(translate("path_waterline", "ZigZag"), "ZigZag"),
|
||||
],
|
||||
"HandleMultipleFeatures": [
|
||||
(translate("path_waterline", "Collectively"), "Collectively"),
|
||||
(translate("path_waterline", "Individually"), "Individually"),
|
||||
],
|
||||
"LayerMode": [
|
||||
(translate("path_waterline", "Single-pass"), "Single-pass"),
|
||||
(translate("path_waterline", "Multi-pass"), "Multi-pass"),
|
||||
],
|
||||
}
|
||||
|
||||
if dataType == "raw":
|
||||
return enums
|
||||
|
||||
data = list()
|
||||
idx = 0 if dataType == "translated" else 1
|
||||
|
||||
PathLog.debug(enums)
|
||||
|
||||
for k, v in enumerate(enums):
|
||||
data.append((v, [tup[idx] for tup in enums[v]]))
|
||||
PathLog.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
def initOperation(self, obj):
|
||||
"""initOperation(obj) ... Initialize the operation by
|
||||
managing property creation and property editor status."""
|
||||
@@ -108,10 +179,10 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
|
||||
# Set enumeration lists for enumeration properties
|
||||
if len(self.addNewProps) > 0:
|
||||
ENUMS = self.opPropertyEnumerations()
|
||||
ENUMS = self.propertyEnumerations()
|
||||
for n in ENUMS:
|
||||
if n in self.addNewProps:
|
||||
setattr(obj, n, ENUMS[n])
|
||||
if n[0] in self.addNewProps:
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
if warn:
|
||||
newPropMsg = translate("PathWaterline", "New property added to")
|
||||
@@ -128,7 +199,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"ShowTempObjects",
|
||||
"Debug",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Show the temporary path construction objects when module is in DEBUG mode.",
|
||||
),
|
||||
@@ -137,7 +208,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"AngularDeflection",
|
||||
"Mesh Conversion",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Smaller values yield a finer, more accurate the mesh. Smaller values increase processing time a lot.",
|
||||
),
|
||||
@@ -146,7 +217,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"LinearDeflection",
|
||||
"Mesh Conversion",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Smaller values yield a finer, more accurate the mesh. Smaller values do not increase processing time much.",
|
||||
),
|
||||
@@ -155,7 +226,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyInteger",
|
||||
"AvoidLastX_Faces",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Avoid cutting the last 'N' faces in the Base Geometry list of selected faces.",
|
||||
),
|
||||
@@ -164,7 +235,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"AvoidLastX_InternalFeatures",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Do not cut internal features on avoided faces."
|
||||
),
|
||||
),
|
||||
@@ -172,7 +243,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"BoundaryAdjustment",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Positive values push the cutter toward, or beyond, the boundary. Negative values retract the cutter away from the boundary.",
|
||||
),
|
||||
@@ -181,7 +252,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"BoundaryEnforcement",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"If true, the cutter will remain inside the boundaries of the model or selected face(s).",
|
||||
),
|
||||
@@ -190,7 +261,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"HandleMultipleFeatures",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Choose how to process multiple Base Geometry features.",
|
||||
),
|
||||
@@ -199,7 +270,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"InternalFeaturesAdjustment",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Positive values push the cutter toward, or into, the feature. Negative values retract the cutter away from the feature.",
|
||||
),
|
||||
@@ -208,7 +279,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"InternalFeaturesCut",
|
||||
"Selected Geometry Settings",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Cut internal feature areas within a larger selected face.",
|
||||
),
|
||||
@@ -217,7 +288,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"Algorithm",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Select the algorithm to use: OCL Dropcutter*, or Experimental (Not OCL based).",
|
||||
),
|
||||
@@ -226,7 +297,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"BoundBox",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Select the overall boundary for the operation."
|
||||
),
|
||||
),
|
||||
@@ -234,7 +305,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"ClearLastLayer",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set to clear last layer in a `Multi-pass` operation.",
|
||||
),
|
||||
@@ -243,7 +314,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"CutMode",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set the direction for the cutting tool to engage the material: Climb (ClockWise) or Conventional (CounterClockWise)",
|
||||
),
|
||||
@@ -252,7 +323,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"CutPattern",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set the geometric clearing pattern to use for the operation.",
|
||||
),
|
||||
@@ -261,7 +332,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyFloat",
|
||||
"CutPatternAngle",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "The yaw angle used for certain clearing patterns"
|
||||
),
|
||||
),
|
||||
@@ -269,7 +340,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"CutPatternReversed",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Reverse the cut order of the stepover paths. For circular cut patterns, begin at the outside and work toward the center.",
|
||||
),
|
||||
@@ -278,7 +349,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"DepthOffset",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set the Z-axis depth offset from the target surface.",
|
||||
),
|
||||
@@ -287,7 +358,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"IgnoreOuterAbove",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Ignore outer waterlines above this height."
|
||||
),
|
||||
),
|
||||
@@ -295,7 +366,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"LayerMode",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Complete the operation in a single pass at depth, or mulitiple passes to final depth.",
|
||||
),
|
||||
@@ -304,7 +375,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyVectorDistance",
|
||||
"PatternCenterCustom",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Set the start point for the cut pattern."
|
||||
),
|
||||
),
|
||||
@@ -312,7 +383,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyEnumeration",
|
||||
"PatternCenterAt",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Choose location of the center point for starting the cut pattern.",
|
||||
),
|
||||
@@ -321,7 +392,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"SampleInterval",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set the sampling resolution. Smaller values quickly increase processing time.",
|
||||
),
|
||||
@@ -330,7 +401,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyFloat",
|
||||
"StepOver",
|
||||
"Clearing Options",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set the stepover percentage, based on the tool's diameter.",
|
||||
),
|
||||
@@ -339,7 +410,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"OptimizeLinearPaths",
|
||||
"Optimization",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Enable optimization of linear paths (co-linear points). Removes unnecessary co-linear points from G-Code output.",
|
||||
),
|
||||
@@ -348,7 +419,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"OptimizeStepOverTransitions",
|
||||
"Optimization",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Enable separate optimization of transitions between, and breaks within, each step over path.",
|
||||
),
|
||||
@@ -357,7 +428,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyDistance",
|
||||
"GapThreshold",
|
||||
"Optimization",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Collinear and co-radial artifact gaps that are smaller than this threshold are closed in the path.",
|
||||
),
|
||||
@@ -366,7 +437,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyString",
|
||||
"GapSizes",
|
||||
"Optimization",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Feedback: three smallest gaps identified in the path geometry.",
|
||||
),
|
||||
@@ -375,7 +446,7 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyVectorDistance",
|
||||
"StartPoint",
|
||||
"Start Point",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"The custom start point for the path of this operation",
|
||||
),
|
||||
@@ -384,46 +455,12 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
"App::PropertyBool",
|
||||
"UseStartPoint",
|
||||
"Start Point",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Make True, if specifying a Start Point"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
def opPropertyEnumerations(self):
|
||||
# Enumeration lists for App::PropertyEnumeration properties
|
||||
return {
|
||||
"Algorithm": ["OCL Dropcutter", "Experimental"],
|
||||
"BoundBox": ["BaseBoundBox", "Stock"],
|
||||
"PatternCenterAt": [
|
||||
"CenterOfMass",
|
||||
"CenterOfBoundBox",
|
||||
"XminYmin",
|
||||
"Custom",
|
||||
],
|
||||
"ClearLastLayer": [
|
||||
"Off",
|
||||
"Circular",
|
||||
"CircularZigZag",
|
||||
"Line",
|
||||
"Offset",
|
||||
"Spiral",
|
||||
"ZigZag",
|
||||
],
|
||||
"CutMode": ["Conventional", "Climb"],
|
||||
"CutPattern": [
|
||||
"None",
|
||||
"Circular",
|
||||
"CircularZigZag",
|
||||
"Line",
|
||||
"Offset",
|
||||
"Spiral",
|
||||
"ZigZag",
|
||||
], # Additional goals ['Offset', 'Spiral', 'ZigZagOffset', 'Grid', 'Triangle']
|
||||
"HandleMultipleFeatures": ["Collectively", "Individually"],
|
||||
"LayerMode": ["Single-pass", "Multi-pass"],
|
||||
}
|
||||
|
||||
def opPropertyDefaults(self, obj, job):
|
||||
"""opPropertyDefaults(obj, job) ... returns a dictionary
|
||||
of default values for the operation's properties."""
|
||||
@@ -543,15 +580,16 @@ class ObjectWaterline(PathOp.ObjectOp):
|
||||
obj.setEditorMode("ShowTempObjects", mode)
|
||||
|
||||
# Repopulate enumerations in case of changes
|
||||
ENUMS = self.opPropertyEnumerations()
|
||||
|
||||
ENUMS = self.propertyEnumerations()
|
||||
for n in ENUMS:
|
||||
restore = False
|
||||
if hasattr(obj, n):
|
||||
val = obj.getPropertyByName(n)
|
||||
if hasattr(obj, n[0]):
|
||||
val = obj.getPropertyByName(n[0])
|
||||
restore = True
|
||||
setattr(obj, n, ENUMS[n])
|
||||
setattr(obj, n[0], n[1])
|
||||
if restore:
|
||||
setattr(obj, n, val)
|
||||
setattr(obj, n[0], val)
|
||||
|
||||
self.setEditorProperties(obj)
|
||||
|
||||
|
||||
@@ -21,70 +21,97 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathScripts.PathWaterline as PathWaterline
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathGui as PathGui
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
|
||||
from PySide import QtCore
|
||||
import PathScripts.PathWaterline as PathWaterline
|
||||
|
||||
__title__ = "Path Waterline Operation UI"
|
||||
__author__ = "sliptonic (Brad Collette), russ4262 (Russell Johnson)"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
__doc__ = "Waterline operation page controller and command implementation."
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
'''Page controller class for the Waterline operation.'''
|
||||
"""Page controller class for the Waterline operation."""
|
||||
|
||||
def initPage(self, obj):
|
||||
self.setTitle("Waterline - " + obj.Label)
|
||||
self.updateVisibility()
|
||||
|
||||
def getForm(self):
|
||||
'''getForm() ... returns UI'''
|
||||
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpWaterlineEdit.ui")
|
||||
"""getForm() ... returns UI"""
|
||||
form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpWaterlineEdit.ui")
|
||||
comboToPropertyMap = [
|
||||
("algorithmSelect", "Algorithm"),
|
||||
("boundBoxSelect", "BoundBox"),
|
||||
("layerMode", "LayerMode"),
|
||||
("cutPattern", "CutPattern"),
|
||||
]
|
||||
enumTups = PathWaterline.ObjectWaterline.propertyEnumerations(dataType="raw")
|
||||
PathGui.populateCombobox(form, enumTups, comboToPropertyMap)
|
||||
return form
|
||||
|
||||
def getFields(self, obj):
|
||||
'''getFields(obj) ... transfers values from UI to obj's proprties'''
|
||||
"""getFields(obj) ... transfers values from UI to obj's proprties"""
|
||||
self.updateToolController(obj, self.form.toolController)
|
||||
self.updateCoolant(obj, self.form.coolantController)
|
||||
|
||||
if obj.Algorithm != str(self.form.algorithmSelect.currentText()):
|
||||
obj.Algorithm = str(self.form.algorithmSelect.currentText())
|
||||
if obj.Algorithm != str(self.form.algorithmSelect.currentData()):
|
||||
obj.Algorithm = str(self.form.algorithmSelect.currentData())
|
||||
|
||||
if obj.BoundBox != str(self.form.boundBoxSelect.currentText()):
|
||||
obj.BoundBox = str(self.form.boundBoxSelect.currentText())
|
||||
if obj.BoundBox != str(self.form.boundBoxSelect.currentData()):
|
||||
obj.BoundBox = str(self.form.boundBoxSelect.currentData())
|
||||
|
||||
if obj.LayerMode != str(self.form.layerMode.currentText()):
|
||||
obj.LayerMode = str(self.form.layerMode.currentText())
|
||||
if obj.LayerMode != str(self.form.layerMode.currentData()):
|
||||
obj.LayerMode = str(self.form.layerMode.currentData())
|
||||
|
||||
if obj.CutPattern != str(self.form.cutPattern.currentText()):
|
||||
obj.CutPattern = str(self.form.cutPattern.currentText())
|
||||
if obj.CutPattern != str(self.form.cutPattern.currentData()):
|
||||
obj.CutPattern = str(self.form.cutPattern.currentData())
|
||||
|
||||
PathGui.updateInputField(obj, 'BoundaryAdjustment', self.form.boundaryAdjustment)
|
||||
PathGui.updateInputField(
|
||||
obj, "BoundaryAdjustment", self.form.boundaryAdjustment
|
||||
)
|
||||
|
||||
if obj.StepOver != self.form.stepOver.value():
|
||||
obj.StepOver = self.form.stepOver.value()
|
||||
|
||||
PathGui.updateInputField(obj, 'SampleInterval', self.form.sampleInterval)
|
||||
PathGui.updateInputField(obj, "SampleInterval", self.form.sampleInterval)
|
||||
|
||||
if obj.OptimizeLinearPaths != self.form.optimizeEnabled.isChecked():
|
||||
obj.OptimizeLinearPaths = self.form.optimizeEnabled.isChecked()
|
||||
|
||||
def setFields(self, obj):
|
||||
'''setFields(obj) ... transfers obj's property values to UI'''
|
||||
"""setFields(obj) ... transfers obj's property values to UI"""
|
||||
self.setupToolController(obj, self.form.toolController)
|
||||
self.setupCoolant(obj, self.form.coolantController)
|
||||
self.selectInComboBox(obj.Algorithm, self.form.algorithmSelect)
|
||||
self.selectInComboBox(obj.BoundBox, self.form.boundBoxSelect)
|
||||
self.selectInComboBox(obj.LayerMode, self.form.layerMode)
|
||||
self.selectInComboBox(obj.CutPattern, self.form.cutPattern)
|
||||
self.form.boundaryAdjustment.setText(FreeCAD.Units.Quantity(obj.BoundaryAdjustment.Value, FreeCAD.Units.Length).UserString)
|
||||
self.form.boundaryAdjustment.setText(
|
||||
FreeCAD.Units.Quantity(
|
||||
obj.BoundaryAdjustment.Value, FreeCAD.Units.Length
|
||||
).UserString
|
||||
)
|
||||
self.form.stepOver.setValue(obj.StepOver)
|
||||
self.form.sampleInterval.setText(FreeCAD.Units.Quantity(obj.SampleInterval.Value, FreeCAD.Units.Length).UserString)
|
||||
self.form.sampleInterval.setText(
|
||||
FreeCAD.Units.Quantity(
|
||||
obj.SampleInterval.Value, FreeCAD.Units.Length
|
||||
).UserString
|
||||
)
|
||||
|
||||
if obj.OptimizeLinearPaths:
|
||||
self.form.optimizeEnabled.setCheckState(QtCore.Qt.Checked)
|
||||
@@ -94,7 +121,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
self.updateVisibility()
|
||||
|
||||
def getSignalsForUpdate(self, obj):
|
||||
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
|
||||
"""getSignalsForUpdate(obj) ... return list of signals for updating obj"""
|
||||
signals = []
|
||||
signals.append(self.form.toolController.currentIndexChanged)
|
||||
signals.append(self.form.coolantController.currentIndexChanged)
|
||||
@@ -110,11 +137,11 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
return signals
|
||||
|
||||
def updateVisibility(self, sentObj=None):
|
||||
'''updateVisibility(sentObj=None)... Updates visibility of Tasks panel objects.'''
|
||||
Algorithm = self.form.algorithmSelect.currentText()
|
||||
"""updateVisibility(sentObj=None)... Updates visibility of Tasks panel objects."""
|
||||
Algorithm = self.form.algorithmSelect.currentData()
|
||||
self.form.optimizeEnabled.hide() # Has no independent QLabel object
|
||||
|
||||
if Algorithm == 'OCL Dropcutter':
|
||||
if Algorithm == "OCL Dropcutter":
|
||||
self.form.cutPattern.hide()
|
||||
self.form.cutPattern_label.hide()
|
||||
self.form.boundaryAdjustment.hide()
|
||||
@@ -123,12 +150,12 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
self.form.stepOver_label.hide()
|
||||
self.form.sampleInterval.show()
|
||||
self.form.sampleInterval_label.show()
|
||||
elif Algorithm == 'Experimental':
|
||||
elif Algorithm == "Experimental":
|
||||
self.form.cutPattern.show()
|
||||
self.form.boundaryAdjustment.show()
|
||||
self.form.cutPattern_label.show()
|
||||
self.form.boundaryAdjustment_label.show()
|
||||
if self.form.cutPattern.currentText() == 'None':
|
||||
if self.form.cutPattern.currentData() == "None":
|
||||
self.form.stepOver.hide()
|
||||
self.form.stepOver_label.hide()
|
||||
else:
|
||||
@@ -142,12 +169,14 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
self.form.cutPattern.currentIndexChanged.connect(self.updateVisibility)
|
||||
|
||||
|
||||
Command = PathOpGui.SetupOperation('Waterline',
|
||||
PathWaterline.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path_Waterline',
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Waterline", "Waterline"),
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Waterline", "Create a Waterline Operation from a model"),
|
||||
PathWaterline.SetupProperties)
|
||||
Command = PathOpGui.SetupOperation(
|
||||
"Waterline",
|
||||
PathWaterline.Create,
|
||||
TaskPanelOpPage,
|
||||
"Path_Waterline",
|
||||
QT_TRANSLATE_NOOP("Path_Waterline", "Waterline"),
|
||||
QT_TRANSLATE_NOOP("Path_Waterline", "Create a Waterline Operation from a model"),
|
||||
PathWaterline.SetupProperties,
|
||||
)
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathWaterlineGui... done\n")
|
||||
|
||||
Reference in New Issue
Block a user