Merge pull request #3461 from Russ4262/3D_Surface_updates

Path:  3D Surface and Waterline updates and fixes.
This commit is contained in:
sliptonic
2020-05-25 18:16:31 -05:00
committed by GitHub
7 changed files with 1192 additions and 848 deletions

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>368</width>
<height>400</height>
<height>442</height>
</rect>
</property>
<property name="windowTitle">
@@ -57,142 +57,24 @@
<item row="1" column="0">
<widget class="QWidget" name="widget" native="true">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="scanType">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Planar: Flat, 3D surface scan. Rotational: 4th-axis rotational scan.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<item>
<property name="text">
<string>Planar</string>
</property>
</item>
<item>
<property name="text">
<string>Rotational</string>
</property>
</item>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QComboBox" name="layerMode">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Complete the operation in a single pass at depth, or mulitiple passes to final depth.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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="8" column="1" colspan="2">
<widget class="QSpinBox" name="stepOver">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The amount by which the tool is laterally displaced on each cycle of the pattern, specified in percent of the tool diameter.&lt;/p&gt;&lt;p&gt;A step over of 100% results in no overlap between two different cycles.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="stepOver_label">
<item row="3" column="0">
<widget class="QLabel" name="cutPattern_label">
<property name="text">
<string>Step over</string>
<string>Cut Pattern</string>
</property>
</widget>
</item>
<item row="10" column="0">
<item row="12" column="0">
<widget class="QLabel" name="sampleInterval_label">
<property name="text">
<string>Sample interval</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="layerMode_label">
<property name="text">
<string>Layer Mode</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QCheckBox" name="optimizeEnabled">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable optimization of linear paths (co-linear points). Removes unnecessary co-linear points from G-Code output.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Optimize Linear Paths</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="dropCutterDirSelect_label">
<property name="text">
<string>Drop Cutter Direction</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="boundBoxExtraOffset_label">
<property name="text">
<string>BoundBox extra offset X, Y</string>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QCheckBox" name="useStartPoint">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make True, if specifying a Start Point&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use Start Point</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="scanType_label">
<property name="text">
<string>Scan Type</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="boundBoxSelect_label">
<property name="text">
<string>BoundBox</string>
</property>
</widget>
</item>
<item row="7" column="1" colspan="2">
<widget class="Gui::InputField" name="depthOffset" native="true">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the Z-axis depth offset from the target surface.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<item row="6" column="1" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="Gui::InputField" name="boundBoxExtraOffsetX" native="true">
<widget class="Gui::InputField" name="boundBoxExtraOffsetX">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -208,7 +90,7 @@
</widget>
</item>
<item>
<widget class="Gui::InputField" name="boundBoxExtraOffsetY" native="true">
<widget class="Gui::InputField" name="boundBoxExtraOffsetY">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Additional offset to the selected bounding box along the Y axis.&quot;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@@ -219,8 +101,8 @@
</item>
</layout>
</item>
<item row="10" column="1" colspan="2">
<widget class="Gui::InputField" name="sampleInterval" native="true">
<item row="12" column="1" colspan="2">
<widget class="Gui::InputField" name="sampleInterval">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the sampling resolution. Smaller values quickly increase processing time.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@@ -229,28 +111,21 @@
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="depthOffset_label">
<item row="14" column="1">
<widget class="QCheckBox" name="optimizeEnabled">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable optimization of linear paths (co-linear points). Removes unnecessary co-linear points from G-Code output.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Depth offset</string>
<string>Optimize Linear Paths</string>
</property>
</widget>
</item>
<item row="6" column="1" colspan="2">
<widget class="QComboBox" name="dropCutterDirSelect">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Dropcutter lines are created parallel to this axis.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<item row="0" column="0">
<widget class="QLabel" name="boundBoxSelect_label">
<property name="text">
<string>BoundBox</string>
</property>
<item>
<property name="text">
<string>X</string>
</property>
</item>
<item>
<property name="text">
<string>Y</string>
</property>
</item>
</widget>
</item>
<item row="0" column="1" colspan="2">
@@ -270,7 +145,7 @@
</item>
</widget>
</item>
<item row="13" column="1">
<item row="15" column="1">
<widget class="QCheckBox" name="optimizeStepOverTransitions">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable separate optimization of transitions between, and breaks within, each step over path.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -280,10 +155,37 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="cutPattern_label">
<item row="4" column="1" colspan="2">
<widget class="QComboBox" name="comboBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Profile the edges of the selection.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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">
<widget class="QLabel" name="stepOver_label">
<property name="text">
<string>Cut Pattern</string>
<string>Step over</string>
</property>
</widget>
</item>
@@ -324,6 +226,146 @@
</item>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="scanType">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Planar: Flat, 3D surface scan. Rotational: 4th-axis rotational scan.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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">
<widget class="QLabel" name="boundBoxExtraOffset_label">
<property name="text">
<string>BoundBox extra offset X, Y</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="depthOffset_label">
<property name="text">
<string>Depth offset</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QComboBox" name="layerMode">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Complete the operation in a single pass at depth, or mulitiple passes to final depth.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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">
<widget class="QLabel" name="layerMode_label">
<property name="text">
<string>Layer Mode</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="scanType_label">
<property name="text">
<string>Scan Type</string>
</property>
</widget>
</item>
<item row="8" column="1" colspan="2">
<widget class="QComboBox" name="dropCutterDirSelect">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Dropcutter lines are created parallel to this axis.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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">
<widget class="Gui::InputField" name="depthOffset">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the Z-axis depth offset from the target surface.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="dropCutterDirSelect_label">
<property name="text">
<string>Drop Cutter Direction</string>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QCheckBox" name="useStartPoint">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Make True, if specifying a Start Point&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use Start Point</string>
</property>
</widget>
</item>
<item row="5" column="1" colspan="2">
<widget class="QSpinBox" name="avoidLastX_Faces">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Avoid cutting the last 'N' faces in the Base Geometry list of selected faces.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="profileEdges_label">
<property name="text">
<string>Profile Edges</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="avoidLastX_Faces_label">
<property name="text">
<string>Avoid Last X Faces</string>
</property>
</widget>
</item>
<item row="10" column="1" colspan="2">
<widget class="QSpinBox" name="stepOver">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The amount by which the tool is laterally displaced on each cycle of the pattern, specified in percent of the tool diameter.&lt;/p&gt;&lt;p&gt;A step over of 100% results in no overlap between two different cycles.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@@ -194,12 +194,6 @@
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item row="2" column="0">

View File

@@ -54,7 +54,6 @@ import math
# lazily loaded modules
from lazy_loader.lazy_loader import LazyLoader
MeshPart = LazyLoader('MeshPart', globals(), 'MeshPart')
Part = LazyLoader('Part', globals(), 'Part')
if FreeCAD.GuiUp:
@@ -72,18 +71,18 @@ def translate(context, text, disambig=None):
class ObjectSurface(PathOp.ObjectOp):
'''Proxy object for Surfacing operation.'''
def baseObject(self):
'''baseObject() ... returns super of receiver
Used to call base implementation in overwritten functions.'''
return super(self.__class__, self)
def opFeatures(self, obj):
'''opFeatures(obj) ... return all standard features and edges based geometries'''
return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureHeights | PathOp.FeatureStepDown | PathOp.FeatureCoolant | PathOp.FeatureBaseFaces
'''opFeatures(obj) ... return all standard features'''
return PathOp.FeatureTool | PathOp.FeatureDepths \
| PathOp.FeatureHeights | PathOp.FeatureStepDown \
| PathOp.FeatureCoolant | PathOp.FeatureBaseFaces
def initOperation(self, obj):
'''initPocketOp(obj) ... create operation specific properties'''
self.initOpProperties(obj)
'''initOperation(obj) ... Initialize the operation by
managing property creation and property editor status.'''
self.propertiesReady = False
self.initOpProperties(obj) # Initialize operation-specific properties
# For debugging
if PathLog.getLevel(PathLog.thisModule()) != 4:
@@ -94,28 +93,30 @@ class ObjectSurface(PathOp.ObjectOp):
def initOpProperties(self, obj, warn=False):
'''initOpProperties(obj) ... create operation specific properties'''
missing = list()
self.addNewProps = list()
for (prtyp, nm, grp, tt) in self.opProperties():
for (prtyp, nm, grp, tt) in self.opPropertyDefinitions():
if not hasattr(obj, nm):
obj.addProperty(prtyp, nm, grp, tt)
missing.append(nm)
if warn:
newPropMsg = translate('PathSurface', 'New property added to') + ' "{}": '.format(obj.Label) + nm + '. '
newPropMsg += translate('PathSurface', 'Check its default value.')
PathLog.warning(newPropMsg)
self.addNewProps.append(nm)
# Set enumeration lists for enumeration properties
if len(missing) > 0:
ENUMS = self.propertyEnumerations()
if len(self.addNewProps) > 0:
ENUMS = self.opPropertyEnumerations()
for n in ENUMS:
if n in missing:
if n in self.addNewProps:
setattr(obj, n, ENUMS[n])
self.addedAllProperties = True
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')
def opProperties(self):
'''opProperties(obj) ... Store operation specific properties'''
self.propertiesReady = True
def opPropertyDefinitions(self):
'''opPropertyDefinitions(obj) ... Store operation specific properties'''
return [
("App::PropertyBool", "ShowTempObjects", "Debug",
@@ -179,7 +180,7 @@ class ObjectSurface(PathOp.ObjectOp):
QtCore.QT_TRANSLATE_NOOP("App::Property", "Profile the edges of the selection.")),
("App::PropertyDistance", "SampleInterval", "Clearing Options",
QtCore.QT_TRANSLATE_NOOP("App::Property", "Set the sampling resolution. Smaller values quickly increase processing time.")),
("App::PropertyPercent", "StepOver", "Clearing Options",
("App::PropertyFloat", "StepOver", "Clearing Options",
QtCore.QT_TRANSLATE_NOOP("App::Property", "Set the stepover percentage, based on the tool's diameter.")),
("App::PropertyBool", "OptimizeLinearPaths", "Optimization",
@@ -199,7 +200,7 @@ class ObjectSurface(PathOp.ObjectOp):
QtCore.QT_TRANSLATE_NOOP("App::Property", "Make True, if specifying a Start Point"))
]
def propertyEnumerations(self):
def opPropertyEnumerations(self):
# Enumeration lists for App::PropertyEnumeration properties
return {
'BoundBox': ['BaseBoundBox', 'Stock'],
@@ -214,6 +215,59 @@ class ObjectSurface(PathOp.ObjectOp):
'ScanType': ['Planar', 'Rotational']
}
def opPropertyDefaults(self, obj, job):
'''opPropertyDefaults(obj, job) ... returns a dictionary of default values
for the operation's properties.'''
defaults = {
'OptimizeLinearPaths': True,
'InternalFeaturesCut': True,
'OptimizeStepOverTransitions': False,
'CircularUseG2G3': False,
'BoundaryEnforcement': True,
'UseStartPoint': False,
'AvoidLastX_InternalFeatures': True,
'CutPatternReversed': False,
'StartPoint': FreeCAD.Vector(0.0, 0.0, obj.ClearanceHeight.Value),
'ProfileEdges': 'None',
'LayerMode': 'Single-pass',
'ScanType': 'Planar',
'RotationAxis': 'X',
'CutMode': 'Conventional',
'CutPattern': 'Line',
'HandleMultipleFeatures': 'Collectively',
'PatternCenterAt': 'CenterOfMass',
'GapSizes': 'No gaps identified.',
'StepOver': 100.0,
'CutPatternAngle': 0.0,
'CutterTilt': 0.0,
'StartIndex': 0.0,
'StopIndex': 360.0,
'SampleInterval': 1.0,
'BoundaryAdjustment': 0.0,
'InternalFeaturesAdjustment': 0.0,
'AvoidLastX_Faces': 0,
'PatternCenterCustom': FreeCAD.Vector(0.0, 0.0, 0.0),
'GapThreshold': 0.005,
'AngularDeflection': 0.25,
'LinearDeflection': 0.0001,
# For debugging
'ShowTempObjects': False
}
warn = True
if hasattr(job, 'GeometryTolerance'):
if job.GeometryTolerance.Value != 0.0:
warn = False
defaults['LinearDeflection'] = job.GeometryTolerance.Value
if warn:
msg = translate('PathSurface',
'The GeometryTolerance for this Job is 0.0.')
msg += translate('PathSurface',
'Initializing LinearDeflection to 0.0001 mm.')
FreeCAD.Console.PrintWarning(msg + '\n')
return defaults
def setEditorProperties(self, obj):
# Used to hide inputs in properties list
@@ -241,23 +295,23 @@ class ObjectSurface(PathOp.ObjectOp):
obj.setEditorMode('PatternCenterCustom', P2)
def onChanged(self, obj, prop):
if hasattr(self, 'addedAllProperties'):
if self.addedAllProperties is True:
if prop == 'ScanType':
self.setEditorProperties(obj)
if prop == 'CutPattern':
if hasattr(self, 'propertiesReady'):
if self.propertiesReady:
if prop in ['ScanType', 'CutPattern']:
self.setEditorProperties(obj)
def opOnDocumentRestored(self, obj):
self.initOpProperties(obj, warn=True)
self.propertiesReady = False
job = PathUtils.findParentJob(obj)
if PathLog.getLevel(PathLog.thisModule()) != 4:
obj.setEditorMode('ShowTempObjects', 2) # hide
else:
obj.setEditorMode('ShowTempObjects', 0) # show
self.initOpProperties(obj, warn=True)
self.opApplyPropertyDefaults(obj, job, self.addNewProps)
mode = 2 if PathLog.getLevel(PathLog.thisModule()) != 4 else 0
obj.setEditorMode('ShowTempObjects', mode)
# Repopulate enumerations in case of changes
ENUMS = self.propertyEnumerations()
ENUMS = self.opPropertyEnumerations()
for n in ENUMS:
restore = False
if hasattr(obj, n):
@@ -269,51 +323,28 @@ class ObjectSurface(PathOp.ObjectOp):
self.setEditorProperties(obj)
def opApplyPropertyDefaults(self, obj, job, propList):
# Set standard property defaults
PROP_DFLTS = self.opPropertyDefaults(obj, job)
for n in PROP_DFLTS:
if n in propList:
prop = getattr(obj, n)
val = PROP_DFLTS[n]
setVal = False
if hasattr(prop, 'Value'):
if isinstance(val, int) or isinstance(val, float):
setVal = True
if setVal:
propVal = getattr(prop, 'Value')
setattr(prop, 'Value', val)
else:
setattr(obj, n, val)
def opSetDefaultValues(self, obj, job):
'''opSetDefaultValues(obj, job) ... initialize defaults'''
job = PathUtils.findParentJob(obj)
obj.OptimizeLinearPaths = True
obj.InternalFeaturesCut = True
obj.OptimizeStepOverTransitions = False
obj.CircularUseG2G3 = False
obj.BoundaryEnforcement = True
obj.UseStartPoint = False
obj.AvoidLastX_InternalFeatures = True
obj.CutPatternReversed = False
obj.StartPoint.x = 0.0
obj.StartPoint.y = 0.0
obj.StartPoint.z = obj.ClearanceHeight.Value
obj.ProfileEdges = 'None'
obj.LayerMode = 'Single-pass'
obj.ScanType = 'Planar'
obj.RotationAxis = 'X'
obj.CutMode = 'Conventional'
obj.CutPattern = 'Line'
obj.HandleMultipleFeatures = 'Collectively' # 'Individually'
obj.PatternCenterAt = 'CenterOfMass' # 'CenterOfBoundBox', 'XminYmin', 'Custom'
obj.GapSizes = 'No gaps identified.'
obj.StepOver = 100
obj.CutPatternAngle = 0.0
obj.CutterTilt = 0.0
obj.StartIndex = 0.0
obj.StopIndex = 360.0
obj.SampleInterval.Value = 1.0
obj.BoundaryAdjustment.Value = 0.0
obj.InternalFeaturesAdjustment.Value = 0.0
obj.AvoidLastX_Faces = 0
obj.PatternCenterCustom.x = 0.0
obj.PatternCenterCustom.y = 0.0
obj.PatternCenterCustom.z = 0.0
obj.GapThreshold.Value = 0.005
obj.AngularDeflection.Value = 0.25
obj.LinearDeflection.Value = job.GeometryTolerance.Value
# For debugging
obj.ShowTempObjects = False
if job.GeometryTolerance.Value == 0.0:
PathLog.warning(translate('PathSurface', 'The GeometryTolerance for this Job is 0.0. Initializing LinearDeflection to 0.0001 mm.'))
obj.LinearDeflection.Value = 0.0001
self.opApplyPropertyDefaults(obj, job, self.addNewProps)
# need to overwrite the default depth calculations for facing
d = None
@@ -373,10 +404,10 @@ class ObjectSurface(PathOp.ObjectOp):
PathLog.error(translate('PathSurface', 'Cut pattern angle limits are +- 360 degrees.'))
# Limit StepOver to natural number percentage
if obj.StepOver > 100:
obj.StepOver = 100
if obj.StepOver < 1:
obj.StepOver = 1
if obj.StepOver > 100.0:
obj.StepOver = 100.0
if obj.StepOver < 1.0:
obj.StepOver = 1.0
# Limit AvoidLastX_Faces to zero and positive values
if obj.AvoidLastX_Faces < 0:
@@ -403,6 +434,7 @@ class ObjectSurface(PathOp.ObjectOp):
self.closedGap = False
self.tmpCOM = None
self.gaps = [0.1, 0.2, 0.3]
self.cancelOperation = False
CMDS = list()
modelVisibility = list()
FCAD = FreeCAD.ActiveDocument
@@ -423,7 +455,6 @@ class ObjectSurface(PathOp.ObjectOp):
self.showDebugObjects = False
# mark beginning of operation and identify parent Job
PathLog.info('\nBegin 3D Surface operation...')
startTime = time.time()
# Identify parent Job
@@ -531,18 +562,18 @@ class ObjectSurface(PathOp.ObjectOp):
PSF.radius = self.radius
PSF.depthParams = self.depthParams
pPM = PSF.preProcessModel(self.module)
# Process selected faces, if available
if pPM is False:
PathLog.error('Unable to pre-process obj.Base.')
else:
if pPM:
self.cancelOperation = False
(FACES, VOIDS) = pPM
self.modelSTLs = PSF.modelSTLs
self.profileShapes = PSF.profileShapes
# Create OCL.stl model objects
self._prepareModelSTLs(JOB, obj)
for m in range(0, len(JOB.Model.Group)):
# Create OCL.stl model objects
PathSurfaceSupport._prepareModelSTLs(self, JOB, obj, m, ocl)
Mdl = JOB.Model.Group[m]
if FACES[m] is False:
PathLog.error('No data for model base: {}'.format(JOB.Model.Group[m].Label))
@@ -553,7 +584,7 @@ class ObjectSurface(PathOp.ObjectOp):
CMDS.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
PathLog.info('Working on Model.Group[{}]: {}'.format(m, Mdl.Label))
# make stock-model-voidShapes STL model for avoidance detection on transitions
self._makeSafeSTL(JOB, obj, m, FACES[m], VOIDS[m])
PathSurfaceSupport._makeSafeSTL(self, JOB, obj, m, FACES[m], VOIDS[m], ocl)
# Process model/faces - OCL objects must be ready
CMDS.extend(self._processCutAreas(JOB, obj, m, FACES[m], VOIDS[m]))
@@ -622,118 +653,22 @@ class ObjectSurface(PathOp.ObjectOp):
del self.midDep
execTime = time.time() - startTime
PathLog.info('Operation time: {} sec.'.format(execTime))
if execTime > 60.0:
tMins = math.floor(execTime / 60.0)
tSecs = execTime - (tMins * 60.0)
exTime = str(tMins) + ' min. ' + str(round(tSecs, 5)) + ' sec.'
else:
exTime = str(round(execTime, 5)) + ' sec.'
FreeCAD.Console.PrintMessage('3D Surface operation time is {}\n'.format(exTime))
if self.cancelOperation:
FreeCAD.ActiveDocument.openTransaction(translate("PathSurface", "Canceled 3D Surface operation."))
FreeCAD.ActiveDocument.removeObject(obj.Name)
FreeCAD.ActiveDocument.commitTransaction()
return True
# Methods for constructing the cut area
def _prepareModelSTLs(self, JOB, obj):
PathLog.debug('_prepareModelSTLs()')
for m in range(0, len(JOB.Model.Group)):
M = JOB.Model.Group[m]
# PathLog.debug(f" -self.modelTypes[{m}] == 'M'")
if self.modelTypes[m] == 'M':
# TODO: test if this works
facets = M.Mesh.Facets.Points
else:
facets = Part.getFacets(M.Shape)
if self.modelSTLs[m] is True:
stl = ocl.STLSurf()
for tri in facets:
t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2]),
ocl.Point(tri[1][0], tri[1][1], tri[1][2]),
ocl.Point(tri[2][0], tri[2][1], tri[2][2]))
stl.addTriangle(t)
self.modelSTLs[m] = stl
return
def _makeSafeSTL(self, JOB, obj, mdlIdx, faceShapes, voidShapes):
'''_makeSafeSTL(JOB, obj, mdlIdx, faceShapes, voidShapes)...
Creates and OCL.stl object with combined data with waste stock,
model, and avoided faces. Travel lines can be checked against this
STL object to determine minimum travel height to clear stock and model.'''
PathLog.debug('_makeSafeSTL()')
fuseShapes = list()
Mdl = JOB.Model.Group[mdlIdx]
mBB = Mdl.Shape.BoundBox
sBB = JOB.Stock.Shape.BoundBox
# add Model shape to safeSTL shape
fuseShapes.append(Mdl.Shape)
if obj.BoundBox == 'BaseBoundBox':
cont = False
extFwd = (sBB.ZLength)
zmin = mBB.ZMin
zmax = mBB.ZMin + extFwd
stpDwn = (zmax - zmin) / 4.0
dep_par = PathUtils.depth_params(zmax + 5.0, zmax + 3.0, zmax, stpDwn, 0.0, zmin)
try:
envBB = PathUtils.getEnvelope(partshape=Mdl.Shape, depthparams=dep_par) # Produces .Shape
cont = True
except Exception as ee:
PathLog.error(str(ee))
shell = Mdl.Shape.Shells[0]
solid = Part.makeSolid(shell)
try:
envBB = PathUtils.getEnvelope(partshape=solid, depthparams=dep_par) # Produces .Shape
cont = True
except Exception as eee:
PathLog.error(str(eee))
if cont:
stckWst = JOB.Stock.Shape.cut(envBB)
if obj.BoundaryAdjustment > 0.0:
cmpndFS = Part.makeCompound(faceShapes)
baBB = PathUtils.getEnvelope(partshape=cmpndFS, depthparams=self.depthParams) # Produces .Shape
adjStckWst = stckWst.cut(baBB)
else:
adjStckWst = stckWst
fuseShapes.append(adjStckWst)
else:
PathLog.warning('Path transitions might not avoid the model. Verify paths.')
else:
# If boundbox is Job.Stock, add hidden pad under stock as base plate
toolDiam = self.cutter.getDiameter()
zMin = JOB.Stock.Shape.BoundBox.ZMin
xMin = JOB.Stock.Shape.BoundBox.XMin - toolDiam
yMin = JOB.Stock.Shape.BoundBox.YMin - toolDiam
bL = JOB.Stock.Shape.BoundBox.XLength + (2 * toolDiam)
bW = JOB.Stock.Shape.BoundBox.YLength + (2 * toolDiam)
bH = 1.0
crnr = FreeCAD.Vector(xMin, yMin, zMin - 1.0)
B = Part.makeBox(bL, bW, bH, crnr, FreeCAD.Vector(0, 0, 1))
fuseShapes.append(B)
if voidShapes is not False:
voidComp = Part.makeCompound(voidShapes)
voidEnv = PathUtils.getEnvelope(partshape=voidComp, depthparams=self.depthParams) # Produces .Shape
fuseShapes.append(voidEnv)
fused = Part.makeCompound(fuseShapes)
if self.showDebugObjects:
T = FreeCAD.ActiveDocument.addObject('Part::Feature', 'safeSTLShape')
T.Shape = fused
T.purgeTouched()
self.tempGroup.addObject(T)
facets = Part.getFacets(fused)
stl = ocl.STLSurf()
for tri in facets:
t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2]),
ocl.Point(tri[1][0], tri[1][1], tri[1][2]),
ocl.Point(tri[2][0], tri[2][1], tri[2][2]))
stl.addTriangle(t)
self.safeSTLs[mdlIdx] = stl
# Methods for constructing the cut area and creating path geometry
def _processCutAreas(self, JOB, obj, mdlIdx, FCS, VDS):
'''_processCutAreas(JOB, obj, mdlIdx, FCS, VDS)...
This method applies any avoided faces or regions to the selected faces.
@@ -784,10 +719,9 @@ class ObjectSurface(PathOp.ObjectOp):
return final
# Methods for creating path geometry
def _processPlanarOp(self, JOB, obj, mdlIdx, cmpdShp, fsi):
'''_processPlanarOp(JOB, obj, mdlIdx, cmpdShp)...
This method compiles the main components for the procedural portion of a planar operation (non-rotational).
'''_processPlanarOp(JOB, obj, mdlIdx, cmpdShp)...
This method compiles the main components for the procedural portion of a planar operation (non-rotational).
It creates the OCL PathDropCutter objects: model and safeTravel.
It makes the necessary facial geometries for the actual cut area.
It calls the correct Single or Multi-pass method as needed.

View File

@@ -141,11 +141,17 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
return signals
def updateVisibility(self):
def updateVisibility(self, sentObj=None):
'''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'):
self.form.profileEdges.show()
self.form.profileEdges_label.show()
self.form.avoidLastX_Faces.show()
self.form.avoidLastX_Faces_label.show()
self.form.boundBoxExtraOffsetX.hide()
self.form.boundBoxExtraOffsetY.hide()
@@ -156,6 +162,11 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
self.form.cutPattern.hide()
self.form.cutPattern_label.hide()
self.form.optimizeStepOverTransitions.hide()
if hasattr(self.form, 'profileEdges'):
self.form.profileEdges.hide()
self.form.profileEdges_label.hide()
self.form.avoidLastX_Faces.hide()
self.form.avoidLastX_Faces_label.hide()
self.form.boundBoxExtraOffsetX.show()
self.form.boundBoxExtraOffsetY.show()

File diff suppressed because it is too large Load Diff

View File

@@ -54,7 +54,6 @@ import math
# lazily loaded modules
from lazy_loader.lazy_loader import LazyLoader
MeshPart = LazyLoader('MeshPart', globals(), 'MeshPart')
Part = LazyLoader('Part', globals(), 'Part')
if FreeCAD.GuiUp:
@@ -72,19 +71,18 @@ def translate(context, text, disambig=None):
class ObjectWaterline(PathOp.ObjectOp):
'''Proxy object for Surfacing operation.'''
def baseObject(self):
'''baseObject() ... returns super of receiver
Used to call base implementation in overwritten functions.'''
return super(self.__class__, self)
def opFeatures(self, obj):
'''opFeatures(obj) ... return all standard features and edges based geomtries'''
return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureHeights | PathOp.FeatureStepDown | PathOp.FeatureCoolant | PathOp.FeatureBaseFaces
'''opFeatures(obj) ... return all standard features'''
return PathOp.FeatureTool | PathOp.FeatureDepths \
| PathOp.FeatureHeights | PathOp.FeatureStepDown \
| PathOp.FeatureCoolant | PathOp.FeatureBaseFaces
def initOperation(self, obj):
'''initPocketOp(obj) ...
Initialize the operation - property creation and property editor status.'''
self.initOpProperties(obj)
'''initOperation(obj) ... Initialize the operation by
managing property creation and property editor status.'''
self.propertiesReady = False
self.initOpProperties(obj) # Initialize operation-specific properties
# For debugging
if PathLog.getLevel(PathLog.thisModule()) != 4:
@@ -95,28 +93,30 @@ class ObjectWaterline(PathOp.ObjectOp):
def initOpProperties(self, obj, warn=False):
'''initOpProperties(obj) ... create operation specific properties'''
missing = list()
self.addNewProps = list()
for (prtyp, nm, grp, tt) in self.opProperties():
for (prtyp, nm, grp, tt) in self.opPropertyDefinitions():
if not hasattr(obj, nm):
obj.addProperty(prtyp, nm, grp, tt)
missing.append(nm)
if warn:
newPropMsg = translate('PathWaterline', 'New property added to') + ' "{}": '.format(obj.Label) + nm + '. '
newPropMsg += translate('PathWaterline', 'Check its default value.')
PathLog.warning(newPropMsg)
self.addNewProps.append(nm)
# Set enumeration lists for enumeration properties
if len(missing) > 0:
ENUMS = self.propertyEnumerations()
if len(self.addNewProps) > 0:
ENUMS = self.opPropertyEnumerations()
for n in ENUMS:
if n in missing:
if n in self.addNewProps:
setattr(obj, n, ENUMS[n])
self.addedAllProperties = True
if warn:
newPropMsg = translate('PathWaterline', 'New property added to')
newPropMsg += ' "{}": {}'.format(obj.Label, self.addNewProps) + '. '
newPropMsg += translate('PathWaterline', 'Check default value(s).')
FreeCAD.Console.PrintWarning(newPropMsg + '\n')
def opProperties(self):
'''opProperties() ... return list of tuples containing operation specific properties'''
self.propertiesReady = True
def opPropertyDefinitions(self):
'''opPropertyDefinitions() ... return list of tuples containing operation specific properties'''
return [
("App::PropertyBool", "ShowTempObjects", "Debug",
QtCore.QT_TRANSLATE_NOOP("App::Property", "Show the temporary path construction objects when module is in DEBUG mode.")),
@@ -167,7 +167,7 @@ class ObjectWaterline(PathOp.ObjectOp):
QtCore.QT_TRANSLATE_NOOP("App::Property", "Choose location of the center point for starting the cut pattern.")),
("App::PropertyDistance", "SampleInterval", "Clearing Options",
QtCore.QT_TRANSLATE_NOOP("App::Property", "Set the sampling resolution. Smaller values quickly increase processing time.")),
("App::PropertyPercent", "StepOver", "Clearing Options",
("App::PropertyFloat", "StepOver", "Clearing Options",
QtCore.QT_TRANSLATE_NOOP("App::Property", "Set the stepover percentage, based on the tool's diameter.")),
("App::PropertyBool", "OptimizeLinearPaths", "Optimization",
@@ -185,7 +185,7 @@ class ObjectWaterline(PathOp.ObjectOp):
QtCore.QT_TRANSLATE_NOOP("App::Property", "Make True, if specifying a Start Point"))
]
def propertyEnumerations(self):
def opPropertyEnumerations(self):
# Enumeration lists for App::PropertyEnumeration properties
return {
'Algorithm': ['OCL Dropcutter', 'Experimental'],
@@ -198,6 +198,56 @@ class ObjectWaterline(PathOp.ObjectOp):
'LayerMode': ['Single-pass', 'Multi-pass'],
}
def opPropertyDefaults(self, obj, job):
'''opPropertyDefaults(obj, job) ... returns a dictionary
of default values for the operation's properties.'''
defaults = {
'OptimizeLinearPaths': True,
'InternalFeaturesCut': True,
'OptimizeStepOverTransitions': False,
'BoundaryEnforcement': True,
'UseStartPoint': False,
'AvoidLastX_InternalFeatures': True,
'CutPatternReversed': False,
'IgnoreOuterAbove': obj.StartDepth.Value + 0.00001,
'StartPoint': FreeCAD.Vector(0.0, 0.0, obj.ClearanceHeight.Value),
'Algorithm': 'OCL Dropcutter',
'LayerMode': 'Single-pass',
'CutMode': 'Conventional',
'CutPattern': 'None',
'HandleMultipleFeatures': 'Collectively',
'PatternCenterAt': 'CenterOfMass',
'GapSizes': 'No gaps identified.',
'ClearLastLayer': 'Off',
'StepOver': 100.0,
'CutPatternAngle': 0.0,
'DepthOffset': 0.0,
'SampleInterval': 1.0,
'BoundaryAdjustment': 0.0,
'InternalFeaturesAdjustment': 0.0,
'AvoidLastX_Faces': 0,
'PatternCenterCustom': FreeCAD.Vector(0.0, 0.0, 0.0),
'GapThreshold': 0.005,
'AngularDeflection': 0.25,
'LinearDeflection': 0.0001,
# For debugging
'ShowTempObjects': False
}
warn = True
if hasattr(job, 'GeometryTolerance'):
if job.GeometryTolerance.Value != 0.0:
warn = False
defaults['LinearDeflection'] = job.GeometryTolerance.Value
if warn:
msg = translate('PathWaterline',
'The GeometryTolerance for this Job is 0.0.')
msg += translate('PathWaterline',
'Initializing LinearDeflection to 0.0001 mm.')
FreeCAD.Console.PrintWarning(msg + '\n')
return defaults
def setEditorProperties(self, obj):
# Used to hide inputs in properties list
expMode = G = 0
@@ -251,21 +301,23 @@ class ObjectWaterline(PathOp.ObjectOp):
obj.setEditorMode('AngularDeflection', expMode)
def onChanged(self, obj, prop):
if hasattr(self, 'addedAllProperties'):
if self.addedAllProperties is True:
if hasattr(self, 'propertiesReady'):
if self.propertiesReady:
if prop in ['Algorithm', 'CutPattern']:
self.setEditorProperties(obj)
def opOnDocumentRestored(self, obj):
self.initOpProperties(obj, warn=True)
self.propertiesReady = False
job = PathUtils.findParentJob(obj)
if PathLog.getLevel(PathLog.thisModule()) != 4:
obj.setEditorMode('ShowTempObjects', 2) # hide
else:
obj.setEditorMode('ShowTempObjects', 0) # show
self.initOpProperties(obj, warn=True)
self.opApplyPropertyDefaults(obj, job, self.addNewProps)
mode = 2 if PathLog.getLevel(PathLog.thisModule()) != 4 else 0
obj.setEditorMode('ShowTempObjects', mode)
# Repopulate enumerations in case of changes
ENUMS = self.propertyEnumerations()
ENUMS = self.opPropertyEnumerations()
for n in ENUMS:
restore = False
if hasattr(obj, n):
@@ -277,40 +329,28 @@ class ObjectWaterline(PathOp.ObjectOp):
self.setEditorProperties(obj)
def opApplyPropertyDefaults(self, obj, job, propList):
# Set standard property defaults
PROP_DFLTS = self.opPropertyDefaults(obj, job)
for n in PROP_DFLTS:
if n in propList:
prop = getattr(obj, n)
val = PROP_DFLTS[n]
setVal = False
if hasattr(prop, 'Value'):
if isinstance(val, int) or isinstance(val, float):
setVal = True
if setVal:
propVal = getattr(prop, 'Value')
setattr(prop, 'Value', val)
else:
setattr(obj, n, val)
def opSetDefaultValues(self, obj, job):
'''opSetDefaultValues(obj, job) ... initialize defaults'''
job = PathUtils.findParentJob(obj)
obj.OptimizeLinearPaths = True
obj.InternalFeaturesCut = True
obj.OptimizeStepOverTransitions = False
obj.BoundaryEnforcement = True
obj.UseStartPoint = False
obj.AvoidLastX_InternalFeatures = True
obj.CutPatternReversed = False
obj.IgnoreOuterAbove = obj.StartDepth.Value + 0.00001
obj.StartPoint = FreeCAD.Vector(0.0, 0.0, obj.ClearanceHeight.Value)
obj.Algorithm = 'OCL Dropcutter'
obj.LayerMode = 'Single-pass'
obj.CutMode = 'Conventional'
obj.CutPattern = 'None'
obj.HandleMultipleFeatures = 'Collectively' # 'Individually'
obj.PatternCenterAt = 'CenterOfMass' # 'CenterOfBoundBox', 'XminYmin', 'Custom'
obj.GapSizes = 'No gaps identified.'
obj.ClearLastLayer = 'Off'
obj.StepOver = 100
obj.CutPatternAngle = 0.0
obj.DepthOffset.Value = 0.0
obj.SampleInterval.Value = 1.0
obj.BoundaryAdjustment.Value = 0.0
obj.InternalFeaturesAdjustment.Value = 0.0
obj.AvoidLastX_Faces = 0
obj.PatternCenterCustom = FreeCAD.Vector(0.0, 0.0, 0.0)
obj.GapThreshold.Value = 0.005
obj.LinearDeflection.Value = 0.0001
obj.AngularDeflection.Value = 0.25
# For debugging
obj.ShowTempObjects = False
self.opApplyPropertyDefaults(obj, job, self.addNewProps)
# need to overwrite the default depth calculations for facing
d = None
@@ -353,10 +393,10 @@ class ObjectWaterline(PathOp.ObjectOp):
PathLog.error(translate('PathWaterline', 'Cut pattern angle limits are +- 360 degrees.'))
# Limit StepOver to natural number percentage
if obj.StepOver > 100:
obj.StepOver = 100
if obj.StepOver < 1:
obj.StepOver = 1
if obj.StepOver > 100.0:
obj.StepOver = 100.0
if obj.StepOver < 1.0:
obj.StepOver = 1.0
# Limit AvoidLastX_Faces to zero and positive values
if obj.AvoidLastX_Faces < 0:
@@ -536,13 +576,12 @@ class ObjectWaterline(PathOp.ObjectOp):
self.modelSTLs = PSF.modelSTLs
self.profileShapes = PSF.profileShapes
# Create OCL.stl model objects
if obj.Algorithm == 'OCL Dropcutter':
self._prepareModelSTLs(JOB, obj)
PathLog.debug('obj.LinearDeflection.Value: {}'.format(obj.LinearDeflection.Value))
PathLog.debug('obj.AngularDeflection.Value: {}'.format(obj.AngularDeflection.Value))
for m in range(0, len(JOB.Model.Group)):
# Create OCL.stl model objects
if obj.Algorithm == 'OCL Dropcutter':
PathSurfaceSupport._prepareModelSTLs(self, JOB, obj, m, ocl)
Mdl = JOB.Model.Group[m]
if FACES[m] is False:
PathLog.error('No data for model base: {}'.format(JOB.Model.Group[m].Label))
@@ -554,7 +593,7 @@ class ObjectWaterline(PathOp.ObjectOp):
PathLog.info('Working on Model.Group[{}]: {}'.format(m, Mdl.Label))
# make stock-model-voidShapes STL model for avoidance detection on transitions
if obj.Algorithm == 'OCL Dropcutter':
self._makeSafeSTL(JOB, obj, m, FACES[m], VOIDS[m])
PathSurfaceSupport._makeSafeSTL(self, JOB, obj, m, FACES[m], VOIDS[m], ocl)
# Process model/faces - OCL objects must be ready
CMDS.extend(self._processWaterlineAreas(JOB, obj, m, FACES[m], VOIDS[m]))
@@ -627,114 +666,7 @@ class ObjectWaterline(PathOp.ObjectOp):
return True
# Methods for constructing the cut area
def _prepareModelSTLs(self, JOB, obj):
PathLog.debug('_prepareModelSTLs()')
for m in range(0, len(JOB.Model.Group)):
M = JOB.Model.Group[m]
# PathLog.debug(f" -self.modelTypes[{m}] == 'M'")
if self.modelTypes[m] == 'M':
# TODO: test if this works
facets = M.Mesh.Facets.Points
else:
facets = Part.getFacets(M.Shape)
if self.modelSTLs[m] is True:
stl = ocl.STLSurf()
for tri in facets:
t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2]),
ocl.Point(tri[1][0], tri[1][1], tri[1][2]),
ocl.Point(tri[2][0], tri[2][1], tri[2][2]))
stl.addTriangle(t)
self.modelSTLs[m] = stl
return
def _makeSafeSTL(self, JOB, obj, mdlIdx, faceShapes, voidShapes):
'''_makeSafeSTL(JOB, obj, mdlIdx, faceShapes, voidShapes)...
Creates and OCL.stl object with combined data with waste stock,
model, and avoided faces. Travel lines can be checked against this
STL object to determine minimum travel height to clear stock and model.'''
PathLog.debug('_makeSafeSTL()')
fuseShapes = list()
Mdl = JOB.Model.Group[mdlIdx]
mBB = Mdl.Shape.BoundBox
sBB = JOB.Stock.Shape.BoundBox
# add Model shape to safeSTL shape
fuseShapes.append(Mdl.Shape)
if obj.BoundBox == 'BaseBoundBox':
cont = False
extFwd = (sBB.ZLength)
zmin = mBB.ZMin
zmax = mBB.ZMin + extFwd
stpDwn = (zmax - zmin) / 4.0
dep_par = PathUtils.depth_params(zmax + 5.0, zmax + 3.0, zmax, stpDwn, 0.0, zmin)
try:
envBB = PathUtils.getEnvelope(partshape=Mdl.Shape, depthparams=dep_par) # Produces .Shape
cont = True
except Exception as ee:
PathLog.error(str(ee))
shell = Mdl.Shape.Shells[0]
solid = Part.makeSolid(shell)
try:
envBB = PathUtils.getEnvelope(partshape=solid, depthparams=dep_par) # Produces .Shape
cont = True
except Exception as eee:
PathLog.error(str(eee))
if cont:
stckWst = JOB.Stock.Shape.cut(envBB)
if obj.BoundaryAdjustment > 0.0:
cmpndFS = Part.makeCompound(faceShapes)
baBB = PathUtils.getEnvelope(partshape=cmpndFS, depthparams=self.depthParams) # Produces .Shape
adjStckWst = stckWst.cut(baBB)
else:
adjStckWst = stckWst
fuseShapes.append(adjStckWst)
else:
PathLog.warning('Path transitions might not avoid the model. Verify paths.')
else:
# If boundbox is Job.Stock, add hidden pad under stock as base plate
toolDiam = self.cutter.getDiameter()
zMin = JOB.Stock.Shape.BoundBox.ZMin
xMin = JOB.Stock.Shape.BoundBox.XMin - toolDiam
yMin = JOB.Stock.Shape.BoundBox.YMin - toolDiam
bL = JOB.Stock.Shape.BoundBox.XLength + (2 * toolDiam)
bW = JOB.Stock.Shape.BoundBox.YLength + (2 * toolDiam)
bH = 1.0
crnr = FreeCAD.Vector(xMin, yMin, zMin - 1.0)
B = Part.makeBox(bL, bW, bH, crnr, FreeCAD.Vector(0, 0, 1))
fuseShapes.append(B)
if voidShapes is not False:
voidComp = Part.makeCompound(voidShapes)
voidEnv = PathUtils.getEnvelope(partshape=voidComp, depthparams=self.depthParams) # Produces .Shape
fuseShapes.append(voidEnv)
fused = Part.makeCompound(fuseShapes)
if self.showDebugObjects is True:
T = FreeCAD.ActiveDocument.addObject('Part::Feature', 'safeSTLShape')
T.Shape = fused
T.purgeTouched()
self.tempGroup.addObject(T)
facets = Part.getFacets(fused)
stl = ocl.STLSurf()
for tri in facets:
t = ocl.Triangle(ocl.Point(tri[0][0], tri[0][1], tri[0][2]),
ocl.Point(tri[1][0], tri[1][1], tri[1][2]),
ocl.Point(tri[2][0], tri[2][1], tri[2][2]))
stl.addTriangle(t)
self.safeSTLs[mdlIdx] = stl
# Methods for constructing the cut area and creating path geometry
def _processWaterlineAreas(self, JOB, obj, mdlIdx, FCS, VDS):
'''_processWaterlineAreas(JOB, obj, mdlIdx, FCS, VDS)...
This method applies any avoided faces or regions to the selected faces.
@@ -787,7 +719,6 @@ class ObjectWaterline(PathOp.ObjectOp):
return final
# Methods for creating path geometry
def _getExperimentalWaterlinePaths(self, PNTSET, csHght, cutPattern):
'''_getExperimentalWaterlinePaths(PNTSET, csHght, cutPattern)...
Switching function for calling the appropriate path-geometry to OCL points conversion function
@@ -864,7 +795,6 @@ class ObjectWaterline(PathOp.ObjectOp):
height = first.z
elif (minSTH + (2.0 * tolrnc)) >= max(first.z, lstPnt.z):
height = False # allow end of Zig to cut to beginning of Zag
# Create raise, shift, and optional lower commands
if height is not False:
@@ -979,7 +909,9 @@ class ObjectWaterline(PathOp.ObjectOp):
scanLines[L].append(oclScan[pi])
lenSL = len(scanLines)
pntsPerLine = len(scanLines[0])
PathLog.debug("--OCL scan: " + str(lenSL * pntsPerLine) + " points, with " + str(numScanLines) + " lines and " + str(pntsPerLine) + " pts/line")
msg = "--OCL scan: " + str(lenSL * pntsPerLine) + " points, with "
msg += str(numScanLines) + " lines and " + str(pntsPerLine) + " pts/line"
PathLog.debug(msg)
# Extract Wl layers per depthparams
lyr = 0
@@ -1694,7 +1626,7 @@ class ObjectWaterline(PathOp.ObjectOp):
return False
def _getModelCrossSection(self, shape, csHght):
PathLog.debug('getCrossSection()')
PathLog.debug('_getModelCrossSection()')
wires = list()
def byArea(fc):

View File

@@ -107,8 +107,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
return signals
def updateVisibility(self):
'''updateVisibility()... Updates visibility of Tasks panel objects.'''
def updateVisibility(self, sentObj=None):
'''updateVisibility(sentObj=None)... Updates visibility of Tasks panel objects.'''
Algorithm = self.form.algorithmSelect.currentText()
self.form.optimizeEnabled.hide() # Has no independent QLabel object