Merge branch 'master' of github.com:FreeCAD/FreeCAD

This commit is contained in:
Yorik van Havre
2017-05-31 19:18:31 -03:00
18 changed files with 490 additions and 476 deletions

View File

@@ -30,8 +30,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>334</width>
<height>292</height>
<width>108</width>
<height>92</height>
</rect>
</property>
<attribute name="icon">
@@ -82,7 +82,7 @@
</item>
<item row="2" column="0">
<widget class="Gui::InputField" name="stepDown">
<property name="minimum">
<property name="minimum" stdset="0">
<double>0.000000000000000</double>
</property>
<property name="unit" stdset="0">
@@ -97,8 +97,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>334</width>
<height>292</height>
<width>141</width>
<height>65</height>
</rect>
</property>
<attribute name="icon">
@@ -147,8 +147,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>334</width>
<height>292</height>
<width>336</width>
<height>303</height>
</rect>
</property>
<attribute name="icon">
@@ -212,13 +212,6 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="useEndPoint">
<property name="text">
<string>Use End Point</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -238,19 +231,9 @@
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="Gui::InputField" name="rollRadius">
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="Gui::InputField" name="extraOffset">
<property name="minimum">
<property name="minimum" stdset="0">
<double>0.000000000000000</double>
</property>
<property name="unit" stdset="0">
@@ -258,13 +241,6 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Roll Radius</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>323</width>
<height>482</height>
<width>379</width>
<height>523</height>
</rect>
</property>
<property name="minimumSize">
@@ -33,7 +33,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>305</width>
<width>361</width>
<height>277</height>
</rect>
</property>
@@ -111,8 +111,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>305</width>
<height>124</height>
<width>361</width>
<height>130</height>
</rect>
</property>
<attribute name="icon">
@@ -192,8 +192,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>305</width>
<height>109</height>
<width>361</width>
<height>130</height>
</rect>
</property>
<attribute name="icon">
@@ -242,8 +242,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>289</width>
<height>297</height>
<width>361</width>
<height>329</height>
</rect>
</property>
<attribute name="icon">
@@ -254,94 +254,6 @@
<string>Operation</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="3" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="2">
<widget class="Gui::QuantitySpinBox" name="zigZagAngle">
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="useZigZag">
<property name="text">
<string>Use ZigZag</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="zigZagUnidirectional">
<property name="text">
<string>ZigZag Unidirectional</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_10">
<property name="text">
<string>ZigZag Angle</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QWidget" name="widget_3" native="true">
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Cut Mode</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Boundary Shape</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QComboBox" name="boundaryShape">
<item>
<property name="text">
<string>Perimeter</string>
</property>
</item>
<item>
<property name="text">
<string>Boundbox</string>
</property>
</item>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="cutMode">
<item>
<property name="text">
<string>Climb</string>
</property>
</item>
<item>
<property name="text">
<string>Conventional</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QWidget" name="widget_4" native="true">
<layout class="QGridLayout" name="gridLayout_5">
@@ -410,6 +322,114 @@
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QWidget" name="widget_3" native="true">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Cut Mode</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cutMode">
<item>
<property name="text">
<string>Climb</string>
</property>
</item>
<item>
<property name="text">
<string>Conventional</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Boundary Shape</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="boundaryShape">
<item>
<property name="text">
<string>Perimeter</string>
</property>
</item>
<item>
<property name="text">
<string>Boundbox</string>
</property>
</item>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="offsetpattern">
<item>
<property name="text">
<string>ZigZag</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>ZigZagOffset</string>
</property>
</item>
<item>
<property name="text">
<string>Line</string>
</property>
</item>
<item>
<property name="text">
<string>Grid</string>
</property>
</item>
<item>
<property name="text">
<string>Triangle</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Pattern</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="Gui::QuantitySpinBox" name="zigZagAngle">
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>ZigZag Angle</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>

View File

@@ -166,13 +166,6 @@
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QSpinBox" name="spindleSpeed">
<property name="maximum">
<number>99999</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cboSpindleDirection">
<item>
@@ -187,6 +180,9 @@
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QDoubleSpinBox" name="spindleSpeed"/>
</item>
</layout>
</widget>
</item>

View File

@@ -140,7 +140,7 @@ class PathWorkbench (Workbench):
if "Profile" or "Contour" in FreeCADGui.Selection.getSelection()[0].Name:
#self.appendContextMenu("", ["Add_Tag"])
self.appendContextMenu("", ["Set_StartPoint"])
self.appendContextMenu("", ["Set_EndPoint"])
#self.appendContextMenu("", ["Set_EndPoint"])
if "Remote" in FreeCADGui.Selection.getSelection()[0].Name:
self.appendContextMenu("", ["Refresh_Path"])

View File

@@ -23,18 +23,20 @@
# ***************************************************************************
import FreeCAD
from FreeCAD import Vector
import Path
import PathScripts.PathLog as PathLog
from PathScripts import PathUtils
from PathScripts.PathUtils import depth_params
from PySide import QtCore
import TechDraw
import ArchPanel
import Part
from PathScripts.PathUtils import waiting_effects
from PathScripts.PathUtils import makeWorkplane
LOG_MODULE = 'PathContour'
PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
#PathLog.trackModule('PathContour')
FreeCAD.setLogLevel('Path.Area', 0)
if FreeCAD.GuiUp:
import FreeCADGui
@@ -78,15 +80,6 @@ class ObjectContour:
# Start Point Properties
obj.addProperty("App::PropertyVector", "StartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The start point of this path"))
obj.addProperty("App::PropertyBool", "UseStartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if specifying a Start Point"))
obj.addProperty("App::PropertyLength", "ExtendAtStart", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "extra length of tool path before start of part edge"))
obj.addProperty("App::PropertyLength", "LeadInLineLen", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "length of straight segment of toolpath that comes in at angle to first part edge"))
# End Point Properties
obj.addProperty("App::PropertyBool", "UseEndPoint", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if specifying an End Point"))
obj.addProperty("App::PropertyLength", "ExtendAtEnd", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "extra length of tool path after end of part edge"))
obj.addProperty("App::PropertyLength", "LeadOutLineLen", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "length of straight segment of toolpath that comes in at angle to last part edge"))
obj.addProperty("App::PropertyVector", "EndPoint", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The end point of this path"))
# Contour Properties
obj.addProperty("App::PropertyEnumeration", "Direction", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW"))
@@ -96,10 +89,10 @@ class ObjectContour:
obj.Side = ['Left', 'Right', 'On'] # side of profile that cutter is on in relation to direction of profile
obj.setEditorMode('Side', 2) # hide
obj.addProperty("App::PropertyDistance", "RollRadius", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "Radius at start and end"))
obj.addProperty("App::PropertyDistance", "OffsetExtra", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extra value to stay away from final Contour- good for roughing toolpath"))
obj.Proxy = self
self.endVector = None
def __getstate__(self):
return None
@@ -109,6 +102,8 @@ class ObjectContour:
def onChanged(self, obj, prop):
pass
# if prop in ['ClearanceHeight', 'StartPoint']:
# obj.StartPoint.z = obj.ClearanceHeight.Value
def setDepths(proxy, obj):
PathLog.track()
@@ -131,65 +126,67 @@ class ObjectContour:
obj.ClearanceHeight = 10.0
obj.SafeHeight = 8.0
def _buildPathLibarea(self, obj, edgelist):
import PathScripts.PathKurveUtils as PathKurveUtils
@waiting_effects
def _buildPathArea(self, obj, baseobject, start=None):
PathLog.track()
# import math
# import area
output = ""
if obj.Comment != "":
output += '(' + str(obj.Comment)+')\n'
profile = Path.Area()
profile.setPlane(makeWorkplane(baseobject))
profile.add(baseobject)
if obj.StartPoint and obj.UseStartPoint:
startpoint = obj.StartPoint
else:
startpoint = None
if obj.EndPoint and obj.UseEndPoint:
endpoint = obj.EndPoint
else:
endpoint = None
PathKurveUtils.output('mem')
PathKurveUtils.feedrate_hv(self.horizFeed, self.vertFeed)
output = ""
output += "G0 Z" + str(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
curve = PathKurveUtils.makeAreaCurve(edgelist, obj.Direction, startpoint, endpoint)
roll_radius = 2.0
extend_at_start = 0.0
extend_at_end = 0.0
lead_in_line_len = 0.0
lead_out_line_len = 0.0
profileparams = {'Fill': 0,
'Coplanar': 0}
if obj.UseComp is False:
obj.Side = 'On'
profileparams['Offset'] = 0.0
else:
if obj.Direction == 'CW':
obj.Side = 'Left'
else:
obj.Side = 'Right'
profileparams['Offset'] = self.radius+obj.OffsetExtra.Value
depthparams = depth_params(
obj.ClearanceHeight.Value,
obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown.Value, 0.0,
obj.FinalDepth.Value, None)
clearance_height=obj.ClearanceHeight.Value,
rapid_safety_space=obj.SafeHeight.Value,
start_depth=obj.StartDepth.Value,
step_down=obj.StepDown.Value,
z_finish_step=0.0,
final_depth=obj.FinalDepth.Value,
user_depths=None)
PathKurveUtils.profile2(
curve, obj.Side, self.radius, self.vertFeed, self.horizFeed,
self.vertRapid, self.horizRapid, obj.OffsetExtra.Value, roll_radius,
None, None, depthparams, extend_at_start, extend_at_end,
lead_in_line_len, lead_out_line_len)
PathLog.debug('depths: {}'.format(depthparams.get_depths()))
profile.setParams(**profileparams)
PathLog.debug("Contour with params: {}".format(profile.getParams()))
output += PathKurveUtils.retrieve_gcode()
return output
sections = profile.makeSections(mode=0, project=True, heights=depthparams.get_depths())
shapelist = [sec.getShape() for sec in sections]
params = {'shapes': shapelist,
'feedrate': self.horizFeed,
'feedrate_v': self.vertFeed,
'verbose': True,
'resume_height': obj.StepDown.Value,
'retraction': obj.ClearanceHeight.Value,
'return_end': True}
if obj.Direction == 'CCW':
params['orientation'] = 1
else:
params['orientation'] = 0
if self.endVector is not None:
params['start'] = self.endVector
elif start is not None:
params['start'] = start
(pp, end_vector) = Path.fromShapes(**params)
PathLog.debug("Generating Path with params: {}".format(params))
PathLog.debug('pp: {}, end vector: {}'.format(pp, end_vector))
self.endVector = end_vector
return pp
def execute(self, obj):
PathLog.track()
import Part # math #DraftGeomUtils
output = ""
self.endVector = None
commandlist = []
toolLoad = obj.ToolController
if toolLoad is None or toolLoad.ToolNumber == 0:
@@ -200,20 +197,19 @@ class ObjectContour:
self.horizFeed = toolLoad.HorizFeed.Value
self.vertRapid = toolLoad.VertRapid.Value
self.horizRapid = toolLoad.HorizRapid.Value
tool = toolLoad.Proxy.getTool(toolLoad) #PathUtils.getTool(obj, toolLoad.ToolNumber)
tool = toolLoad.Proxy.getTool(toolLoad)
if not tool or tool.Diameter == 0:
FreeCAD.Console.PrintError("No Tool found or diameter is zero. We need a tool to build a Path.")
return
#self.radius = 0.25
else:
self.radius = tool.Diameter/2
output += "(" + obj.Label + ")"
if not obj.UseComp:
output += "(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")"
commandlist.append(Path.Command("(" + obj.Label + ")"))
if obj.UseComp:
commandlist.append(Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")"))
else:
output += "(Uncompensated Tool Path)"
commandlist.append(Path.Command("(Uncompensated Tool Path)"))
parentJob = PathUtils.findParentJob(obj)
if parentJob is None:
@@ -222,33 +218,33 @@ class ObjectContour:
if baseobject is None:
return
# Let's always start by rapid to clearance...just for safety
commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value}))
if hasattr(baseobject, "Proxy"):
if isinstance(baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet
baseobject.Proxy.execute(baseobject)
for subobj in baseobject.Group: # process the group of panels
if isinstance(subobj.Proxy, ArchPanel.PanelCut):
shapes = baseobject.Proxy.getOutlines(baseobject, transform=True)
for shape in shapes:
for wire in shape.Wires:
edgelist = wire.Edges
edgelist = Part.__sortEdges__(edgelist)
PathLog.debug("Processing panel perimeter. edges found: {}".format(len(edgelist))) # subobj.Proxy.execute(subobj)
try:
output += self._buildPathLibarea(obj, edgelist)
except:
FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
shapes = baseobject.Proxy.getOutlines(baseobject, transform=True)
for shape in shapes:
f = Part.makeFace([shape], 'Part::FaceMakerSimple')
thickness = baseobject.Group[0].Source.Thickness
contourshape = f.extrude(FreeCAD.Vector(0, 0, thickness))
try:
commandlist.extend(self._buildPathArea(obj, contourshape, start=obj.StartPoint).Commands)
except Exception as e:
print(e)
FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
else:
contourwire = TechDraw.findShapeOutline(baseobject.Shape, 1, Vector(0, 0, 1))
edgelist = contourwire.Edges
edgelist = Part.__sortEdges__(edgelist)
bb = baseobject.Shape.BoundBox
env = PathUtils.getEnvelope(baseobject.Shape, bb.ZLength + (obj.StartDepth.Value-bb.ZMax))
try:
output += self._buildPathLibarea(obj, edgelist)
except:
commandlist.extend(self._buildPathArea(obj, env, start=obj.StartPoint).Commands)
except Exception as e:
print(e)
FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
if obj.Active:
path = Path.Path(output)
path = Path.Path(commandlist)
obj.Path = path
if obj.ViewObject:
obj.ViewObject.Visibility = True
@@ -298,28 +294,9 @@ class _CommandSetStartPoint:
obj = FreeCADGui.Selection.getSelection()[0]
obj.StartPoint.x = point.x
obj.StartPoint.y = point.y
obj.StartPoint.z = obj.ClearanceHeight.Value
def Activated(self):
FreeCADGui.Snapper.getPoint(callback=self.setpoint)
class _CommandSetEndPoint:
def GetResources(self):
return {'Pixmap': 'Path-EndPoint',
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Contour", "Pick End Point"),
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Contour", "Pick End Point")}
def IsActive(self):
return FreeCAD.ActiveDocument is not None
def setpoint(self, point, o):
obj = FreeCADGui.Selection.getSelection()[0]
obj.EndPoint.x = point.x
obj.EndPoint.y = point.y
def Activated(self):
FreeCADGui.Snapper.getPoint(callback=self.setpoint)
@@ -402,14 +379,10 @@ class TaskPanel:
self.obj.StepDown = FreeCAD.Units.Quantity(self.form.stepDown.text()).Value
if hasattr(self.obj, "OffsetExtra"):
self.obj.OffsetExtra = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value
if hasattr(self.obj, "RollRadius"):
self.obj.RollRadius = FreeCAD.Units.Quantity(self.form.rollRadius.text()).Value
if hasattr(self.obj, "UseComp"):
self.obj.UseComp = self.form.useCompensation.isChecked()
if hasattr(self.obj, "UseStartPoint"):
self.obj.UseStartPoint = self.form.useStartPoint.isChecked()
if hasattr(self.obj, "UseEndPoint"):
self.obj.UseEndPoint = self.form.useEndPoint.isChecked()
# if hasattr(self.obj, "UseStartPoint"):
# self.obj.UseStartPoint = self.form.useStartPoint.isChecked()
if hasattr(self.obj, "Direction"):
self.obj.Direction = str(self.form.direction.currentText())
if hasattr(self.obj, "ToolController"):
@@ -425,10 +398,8 @@ class TaskPanel:
self.form.clearanceHeight.setText(FreeCAD.Units.Quantity(self.obj.ClearanceHeight.Value, FreeCAD.Units.Length).UserString)
self.form.stepDown.setText(FreeCAD.Units.Quantity(self.obj.StepDown.Value, FreeCAD.Units.Length).UserString)
self.form.extraOffset.setText(FreeCAD.Units.Quantity(self.obj.OffsetExtra.Value, FreeCAD.Units.Length).UserString)
self.form.rollRadius.setText(FreeCAD.Units.Quantity(self.obj.RollRadius.Value, FreeCAD.Units.Length).UserString)
self.form.useCompensation.setChecked(self.obj.UseComp)
self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
self.form.useEndPoint.setChecked(self.obj.UseEndPoint)
# self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
index = self.form.direction.findText(
self.obj.Direction, QtCore.Qt.MatchFixedString)
@@ -481,9 +452,7 @@ class TaskPanel:
self.form.uiToolController.currentIndexChanged.connect(self.getFields)
self.form.useCompensation.clicked.connect(self.getFields)
self.form.useStartPoint.clicked.connect(self.getFields)
self.form.useEndPoint.clicked.connect(self.getFields)
self.form.extraOffset.editingFinished.connect(self.getFields)
self.form.rollRadius.editingFinished.connect(self.getFields)
self.setFields()
@@ -506,6 +475,5 @@ if FreeCAD.GuiUp:
# register the FreeCAD command
FreeCADGui.addCommand('Path_Contour', CommandPathContour())
FreeCADGui.addCommand('Set_StartPoint', _CommandSetStartPoint())
FreeCADGui.addCommand('Set_EndPoint', _CommandSetEndPoint())
FreeCAD.Console.PrintLog("Loading PathContour... done\n")

View File

@@ -25,14 +25,11 @@
from __future__ import print_function
import sys
import FreeCAD
# from FreeCAD import Vector
import Path
import PathScripts.PathLog as PathLog
# import Part
from PySide import QtCore, QtGui
from PathScripts import PathUtils
from PathScripts.PathUtils import fmt
# from math import pi
from PathScripts.PathUtils import fmt, waiting_effects
import ArchPanel
@@ -105,9 +102,8 @@ class ObjectDrilling:
def onChanged(self, obj, prop):
pass
# if prop == "UserLabel":
# obj.Label = obj.UserLabel + " :" + obj.ToolDescription
@waiting_effects
def execute(self, obj):
PathLog.track()
output = ""

View File

@@ -118,7 +118,8 @@ class ObjectPathEngrave:
return
try:
if baseobject.isDerivedFrom('Sketcher::SketchObject') or \
baseobject.isDerivedFrom('Part::Part2DObject'):
baseobject.isDerivedFrom('Part::Part2DObject') or \
hasattr(baseobject, 'ArrayType'):
output += "G0 Z" + PathUtils.fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
@@ -181,7 +182,8 @@ class ObjectPathEngrave:
if not last:
# we set the first move to our first point
last = edge.Vertexes[0].Point
output += "G0" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(obj.SafeHeight.Value) + "F " + PathUtils.fmt(self.horizRapid) # Rapid sto starting position
output += "G0" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.horizRapid) # Rapid to starting position
output += "G0" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(obj.SafeHeight.Value) + "F " + PathUtils.fmt(self.horizRapid) # Rapid to safe height
output += "G1" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(obj.FinalDepth.Value) + "F " + PathUtils.fmt(self.vertFeed) + "\n" # Vertical feed to depth
if isinstance(edge.Curve, Part.Circle):
point = edge.Vertexes[-1].Point
@@ -208,7 +210,7 @@ class ObjectPathEngrave:
output += " F " + PathUtils.fmt(self.horizFeed)
output += "\n"
last = point
output += "G0 Z " + PathUtils.fmt(obj.SafeHeight.Value)
output += "G0 Z " + PathUtils.fmt(obj.ClearanceHeight.Value)
return output

View File

@@ -28,15 +28,15 @@ import Path
from PySide import QtCore, QtGui
from PathScripts import PathUtils
import Part
import PathScripts.PathKurveUtils
import area
import TechDraw
from FreeCAD import Vector
import PathScripts.PathLog as PathLog
from PathScripts.PathUtils import waiting_effects
from PathScripts.PathUtils import makeWorkplane
LOG_MODULE = 'PathMillFace'
PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
PathLog.trackModule()
FreeCAD.setLogLevel('Path.Area', 0)
FreeCADGui = None
if FreeCAD.GuiUp:
@@ -88,6 +88,8 @@ class ObjectFace:
obj.addProperty("App::PropertyFloat", "ZigZagAngle", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Angle of the zigzag pattern"))
obj.addProperty("App::PropertyEnumeration", "BoundaryShape", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Shape to use for calculating Boundary"))
obj.BoundaryShape = ['Perimeter', 'Boundbox']
obj.addProperty("App::PropertyEnumeration", "OffsetPattern", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "clearing pattern to use"))
obj.OffsetPattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle']
# Start Point Properties
obj.addProperty("App::PropertyVector", "StartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The start point of this path"))
@@ -159,60 +161,63 @@ class ObjectFace:
return self.getStock(o)
return None
def buildpathlibarea(self, obj, a):
"""Build the face path using libarea algorithm"""
import PathScripts.PathAreaUtils as PathAreaUtils
@waiting_effects
def _buildPathArea(self, obj, baseobject):
"""build the face path using PathArea"""
from PathScripts.PathUtils import depth_params
PathLog.track()
for p in a.getCurves():
PathLog.debug(p.text())
boundary = Path.Area()
boundary.setPlane(makeWorkplane(baseobject))
boundary.add(baseobject)
FreeCAD.Console.PrintMessage(translate("PathMillFace", "Generating toolpath with libarea offsets.\n"))
stepover = (self.radius * 2) * (float(obj.StepOver)/100)
pocketparams = {'Fill': 0,
'Coplanar': 0,
'PocketMode': 1,
'SectionCount': -1,
'Angle': obj.ZigZagAngle,
'FromCenter': (obj.StartAt == "Center"),
'PocketStepover': stepover,
'PocketExtraOffset': obj.PassExtension.Value}
Pattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle']
pocketparams['PocketMode'] = Pattern.index(obj.OffsetPattern) + 1
depthparams = depth_params(
obj.ClearanceHeight.Value,
obj.SafeHeight.Value,
obj.StartDepth.Value,
obj.StepDown,
obj.FinishDepth.Value,
obj.FinalDepth.Value)
clearance_height=obj.ClearanceHeight.Value,
rapid_safety_space=obj.SafeHeight.Value,
start_depth=obj.StartDepth.Value,
step_down=obj.StepDown,
z_finish_step=obj.FinishDepth.Value,
final_depth=obj.FinalDepth.Value,
user_depths=None)
extraoffset = - obj.PassExtension.Value
stepover = (self.radius * 2) * (float(obj.StepOver)/100)
use_zig_zag = obj.UseZigZag
zig_angle = obj.ZigZagAngle
from_center = (obj.StartAt == "Center")
keep_tool_down = obj.KeepToolDown
zig_unidirectional = obj.ZigUnidirectional
start_point = None
cut_mode = obj.CutMode
boundary.setParams(**pocketparams)
sections = boundary.makeSections(mode=0, project=False, heights=depthparams.get_depths())
shapelist = [sec.getShape() for sec in sections]
PathAreaUtils.flush_nc()
PathAreaUtils.output('mem')
PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed)
if obj.UseStartPoint:
start_point = (obj.StartPoint.x, obj.StartPoint.y)
params = {'shapes': shapelist,
'feedrate': self.horizFeed,
'feedrate_v': self.vertFeed,
'verbose': True,
'resume_height': obj.StepDown,
'retraction': obj.ClearanceHeight.Value}
PathAreaUtils.pocket(
a,
self.radius,
extraoffset,
stepover,
depthparams,
from_center,
keep_tool_down,
use_zig_zag,
zig_angle,
zig_unidirectional,
start_point,
cut_mode)
return PathAreaUtils.retrieve_gcode()
# if obj.Direction == 'CCW':
# params['orientation'] = 1
# else:
# params['orientation'] = 0
PathLog.debug("Generating Path with params: {}".format(params))
pp = Path.fromShapes(**params)
return pp
def execute(self, obj):
PathLog.track()
output = ""
commandlist = []
toolLoad = obj.ToolController
@@ -232,9 +237,7 @@ class ObjectFace:
else:
self.radius = tool.Diameter/2
# Build preliminary comments
output = ""
output += "(" + obj.Label + ")"
commandlist.append(Path.Command("(" + obj.Label + ")"))
# Facing is done either against base objects
if obj.Base:
@@ -266,36 +269,28 @@ class ObjectFace:
# if user wants the boundbox, calculate that
PathLog.info("Boundary Shape: {}".format(obj.BoundaryShape))
bb = planeshape.BoundBox
if obj.BoundaryShape == 'Boundbox':
bb = planeshape.BoundBox
bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, Vector(bb.XMin, bb.YMin, bb.ZMin), Vector(0, 0, 1))
contourwire = TechDraw.findShapeOutline(bbperim, 1, Vector(0, 0, 1))
env = PathUtils.getEnvelope(bbperim, bb.ZLength + (obj.StartDepth.Value-bb.ZMax))
else:
contourwire = TechDraw.findShapeOutline(planeshape, 1, Vector(0, 0, 1))
env = PathUtils.getEnvelope(planeshape, bb.ZLength + (obj.StartDepth.Value-bb.ZMax))
# pocket = Path.Area(PocketMode=4,SectionCount=-1,SectionMode=1,Stepdown=0.499)
# pocket.setParams(PocketExtraOffset = obj.PassExtension.Value, ToolRadius = self.radius)
# pocket.add(planeshape, op=1)
# #Part.show(contourwire)
# path = Path.fromShapes(pocket.getShape())
try:
commandlist.extend(self._buildPathArea(obj, env).Commands)
except Exception as e:
print(e)
FreeCAD.Console.PrintWarning(translate("PathMillFace", "The selected settings did not produce a valid path.\n"))
edgelist = contourwire.Edges
edgelist = Part.__sortEdges__(edgelist)
# use libarea to build the pattern
a = area.Area()
c = PathScripts.PathKurveUtils.makeAreaCurve(edgelist, 'CW')
PathLog.debug(c.text())
a.append(c)
a.Reorder()
output += self.buildpathlibarea(obj, a)
path = Path.Path(output)
if len(path.Commands) == 0:
FreeCAD.Console.PrintMessage(translate("PathMillFace", "The selected settings did not produce a valid path.\n"))
obj.Path = path
obj.ViewObject.Visibility = True
if obj.Active:
path = Path.Path(commandlist)
obj.Path = path
if obj.ViewObject:
obj.ViewObject.Visibility = True
else:
path = Path.Path("(inactive operation)")
obj.Path = path
obj.ViewObject.Visibility = False
class _CommandSetFaceStartPoint:
@@ -375,8 +370,7 @@ class CommandPathMillFace:
FreeCADGui.doCommand('obj.StepDown = 1.0')
FreeCADGui.doCommand('obj.StartDepth = ' + str(ztop + 1))
FreeCADGui.doCommand('obj.FinalDepth =' + str(ztop))
FreeCADGui.doCommand('obj.ZigZagAngle = 0.0')
FreeCADGui.doCommand('obj.UseZigZag = True')
FreeCADGui.doCommand('obj.ZigZagAngle = 45.0')
FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
FreeCADGui.doCommand('obj.ToolController = PathScripts.PathUtils.findToolController(obj)')
snippet = '''
@@ -436,16 +430,15 @@ class TaskPanel:
self.obj.PassExtension = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value
if hasattr(self.obj, "CutMode"):
self.obj.CutMode = str(self.form.cutMode.currentText())
if hasattr(self.obj, "UseZigZag"):
self.obj.UseZigZag = self.form.useZigZag.isChecked()
if hasattr(self.obj, "ZigUnidirectional"):
self.obj.ZigUnidirectional = self.form.zigZagUnidirectional.isChecked()
if hasattr(self.obj, "ZigZagAngle"):
self.obj.ZigZagAngle = FreeCAD.Units.Quantity(self.form.zigZagAngle.text()).Value
if hasattr(self.obj, "StepOver"):
self.obj.StepOver = self.form.stepOverPercent.value()
if hasattr(self.obj, "BoundaryShape"):
self.obj.BoundaryShape = str(self.form.boundaryShape.currentText())
if hasattr(self.obj, "OffsetPattern"):
self.obj.OffsetPattern = str(self.form.offsetpattern.currentText())
if hasattr(self.obj, "ToolController"):
tc = PathUtils.findToolController(self.obj, self.form.uiToolController.currentText())
self.obj.ToolController = tc
@@ -460,8 +453,6 @@ class TaskPanel:
self.form.clearanceHeight.setText(FreeCAD.Units.Quantity(self.obj.ClearanceHeight.Value, FreeCAD.Units.Length).UserString)
self.form.stepOverPercent.setValue(self.obj.StepOver)
self.form.useZigZag.setChecked(self.obj.UseZigZag)
self.form.zigZagUnidirectional.setChecked(self.obj.ZigUnidirectional)
self.form.zigZagAngle.setValue(FreeCAD.Units.Quantity(self.obj.ZigZagAngle, FreeCAD.Units.Angle))
self.form.extraOffset.setValue(self.obj.PassExtension.Value)
@@ -480,6 +471,13 @@ class TaskPanel:
self.form.boundaryShape.setCurrentIndex(index)
self.form.boundaryShape.blockSignals(False)
index = self.form.offsetpattern.findText(
self.obj.OffsetPattern, QtCore.Qt.MatchFixedString)
if index >= 0:
self.form.offsetpattern.blockSignals(True)
self.form.offsetpattern.setCurrentIndex(index)
self.form.offsetpattern.blockSignals(False)
for i in self.obj.Base:
for sub in i[1]:
self.form.baseList.addItem(i[0].Name + "." + sub)
@@ -616,8 +614,7 @@ class TaskPanel:
self.form.extraOffset.editingFinished.connect(self.getFields)
self.form.boundaryShape.currentIndexChanged.connect(self.getFields)
self.form.stepOverPercent.editingFinished.connect(self.getFields)
self.form.useZigZag.clicked.connect(self.getFields)
self.form.zigZagUnidirectional.clicked.connect(self.getFields)
self.form.offsetpattern.currentIndexChanged.connect(self.getFields)
self.form.zigZagAngle.editingFinished.connect(self.getFields)
self.form.uiToolController.currentIndexChanged.connect(self.getFields)

View File

@@ -109,9 +109,6 @@ class ObjectPocket:
obj.setEditorMode('RampAngle', 2) # make this hidden
obj.setEditorMode('RampSize', 2) # make this hidden
if prop == "UserLabel":
obj.Label = obj.UserLabel + " :" + obj.ToolDescription
def __getstate__(self):
return None

View File

@@ -28,6 +28,7 @@ from PathScripts import PathUtils
from PathScripts.PathUtils import depth_params
from DraftGeomUtils import findWires
import PathScripts.PathLog as PathLog
from PathScripts.PathUtils import waiting_effects
"""Path Profile from Edges Object and Command"""
@@ -107,8 +108,7 @@ class ObjectProfile:
return None
def onChanged(self, obj, prop):
if prop == "UserLabel":
obj.Label = obj.UserLabel + " :" + obj.ToolDescription
pass
def addprofilebase(self, obj, ss, sub=""):
baselist = obj.Base
@@ -144,6 +144,7 @@ class ObjectProfile:
obj.Base = baselist
self.execute(obj)
@waiting_effects
def _buildPathLibarea(self, obj, edgelist):
import PathScripts.PathKurveUtils as PathKurveUtils
# import math

View File

@@ -27,6 +27,7 @@ import FreeCAD
import Path
from PathScripts import PathUtils
import PathScripts.PathLog as PathLog
from PathScripts.PathUtils import waiting_effects
import sys
# xrange is not available in python3
@@ -129,8 +130,7 @@ class ObjectSurface:
return None
def onChanged(self, obj, prop):
if prop == "UserLabel":
obj.Label = obj.UserLabel + " :" + obj.ToolDescription
pass
def _waterline(self, obj, s, bb):
import ocl
@@ -262,6 +262,7 @@ class ObjectSurface:
return output
@waiting_effects
def execute(self, obj):
import MeshPart
FreeCAD.Console.PrintWarning(

View File

@@ -31,16 +31,38 @@ import PathScripts
from PathScripts import PathJob
import numpy
import PathLog
#from math import pi
from FreeCAD import Vector
import Path
from PySide import QtCore
from PySide import QtGui
LOG_MODULE = 'PathUtils'
PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
#PathLog.trackModule('PathUtils')
# PathLog.trackModule('PathUtils')
FreeCAD.setLogLevel('Path.Area', 0)
def waiting_effects(function):
def new_function(*args, **kwargs):
if not FreeCAD.GuiUp:
return function(*args, **kwargs)
QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
res = None
try:
res = function(*args, **kwargs)
except Exception as e:
raise e
print("Error {}".format(e.args[0]))
finally:
QtGui.QApplication.restoreOverrideCursor()
return res
return new_function
def cleanedges(splines, precision):
'''cleanedges([splines],precision). Convert BSpline curves, Beziers, to arcs that can be used for cnc paths.
Returns Lines as is. Filters Circle and Arcs for over 180 degrees. Discretizes Ellipses. Ignores other geometry. '''
PathLog.track()
edges = []
for spline in splines:
if geomType(spline) == "BSplineCurve":
@@ -73,8 +95,11 @@ def cleanedges(splines, precision):
return edges
def curvetowire(obj, steps):
'''adapted from DraftGeomUtils, because the discretize function changed a bit '''
PathLog.track()
points = obj.copy().discretize(Distance=eval('steps'))
p0 = points[0]
edgelist = []
@@ -84,6 +109,7 @@ def curvetowire(obj, steps):
p0 = p
return edgelist
def isDrillable(obj, candidate, tooldiameter=None):
PathLog.track('obj: {} candidate: {} tooldiameter {}'.format(obj, candidate, tooldiameter))
drillable = False
@@ -98,13 +124,13 @@ def isDrillable(obj, candidate, tooldiameter=None):
v1 = edge.Vertexes[1].Point
if (v1.sub(v0).x == 0) and (v1.sub(v0).y == 0):
# vector of top center
lsp = Vector(face.BoundBox.Center.x,face.BoundBox.Center.y, face.BoundBox.ZMax)
lsp = Vector(face.BoundBox.Center.x, face.BoundBox.Center.y, face.BoundBox.ZMax)
# vector of bottom center
lep = Vector(face.BoundBox.Center.x,face.BoundBox.Center.y, face.BoundBox.ZMin)
lep = Vector(face.BoundBox.Center.x, face.BoundBox.Center.y, face.BoundBox.ZMin)
if obj.isInside(lsp, 0, False) or obj.isInside(lep, 0, False):
drillable = False
# eliminate elliptical holes
elif not hasattr(face.Surface, "Radius"): #abs(face.BoundBox.XLength - face.BoundBox.YLength) > 0.05:
elif not hasattr(face.Surface, "Radius"):
drillable = False
else:
if tooldiameter is not None:
@@ -115,7 +141,7 @@ def isDrillable(obj, candidate, tooldiameter=None):
for edge in candidate.Edges:
if isinstance(edge.Curve, Part.Circle) and edge.isClosed():
PathLog.debug("candidate is a circle or ellipse")
if not hasattr(edge.Curve, "Radius"): #bbdiff > 0.05:
if not hasattr(edge.Curve, "Radius"):
PathLog.debug("No radius. Ellipse.")
drillable = False
else:
@@ -127,13 +153,16 @@ def isDrillable(obj, candidate, tooldiameter=None):
PathLog.debug("candidate is drillable: {}".format(drillable))
return drillable
# fixme set at 4 decimal places for testing
def fmt(val): return format(val, '.4f')
def segments(poly):
''' A sequence of (x,y) numeric coordinates pairs '''
return zip(poly, poly[1:] + [poly[0]])
def loopdetect(obj, edge1, edge2):
'''
Returns a loop wire that includes the two edges.
@@ -148,17 +177,19 @@ def loopdetect(obj, edge1, edge2):
for wire in obj.Shape.Wires:
for e in wire.Edges:
if e.hashCode() == edge1.hashCode():
candidates.append((wire.hashCode(),wire))
candidates.append((wire.hashCode(), wire))
if e.hashCode() == edge2.hashCode():
candidates.append((wire.hashCode(),wire))
loop = set([x for x in candidates if candidates.count(x) > 1]) #return the duplicate item
candidates.append((wire.hashCode(), wire))
loop = set([x for x in candidates if candidates.count(x) > 1]) # return the duplicate item
if len(loop) != 1:
return None
loopwire = next(x for x in loop)[1]
return loopwire
def filterArcs(arcEdge):
'''filterArcs(Edge) -used to split arcs that over 180 degrees. Returns list '''
PathLog.track()
s = arcEdge
if isinstance(s.Curve, Part.Circle):
splitlist = []
@@ -174,10 +205,10 @@ def filterArcs(arcEdge):
arcstpt = s.valueAt(s.FirstParameter)
arcmid = s.valueAt(
(s.LastParameter - s.FirstParameter) * 0.5 + s.FirstParameter)
arcquad1 = s.valueAt((s.LastParameter - s.FirstParameter)
* 0.25 + s.FirstParameter) # future midpt for arc1
arcquad2 = s.valueAt((s.LastParameter - s.FirstParameter)
* 0.75 + s.FirstParameter) # future midpt for arc2
arcquad1 = s.valueAt((s.LastParameter - s.FirstParameter) *
0.25 + s.FirstParameter) # future midpt for arc1
arcquad2 = s.valueAt((s.LastParameter - s.FirstParameter) *
0.75 + s.FirstParameter) # future midpt for arc2
arcendpt = s.valueAt(s.LastParameter)
# reconstruct with 2 arcs
arcseg1 = Part.ArcOfCircle(arcstpt, arcquad1, arcmid)
@@ -194,6 +225,40 @@ def filterArcs(arcEdge):
return splitlist
def makeWorkplane(shape):
"""
Creates a workplane circle at the ZMin level.
"""
PathLog.track()
loc = FreeCAD.Vector(shape.BoundBox.Center.x,
shape.BoundBox.Center.y,
shape.BoundBox.ZMin)
c = Part.makeCircle(10, loc)
return c
def getEnvelope(partshape, stockheight=None):
'''
getEnvelop(partshape, stockheight=None)
returns a shape corresponding to the partshape silhouette extruded to height.
if stockheight is given, the returned shape is extruded to that height otherwise the returned shape
is the height of the original shape boundbox
partshape = solid object
stockheight = float
'''
PathLog.track(partshape, stockheight)
area = Path.Area(Fill=1, Coplanar=0).add(partshape)
# loc = FreeCAD.Vector(partshape.BoundBox.Center.x,
# partshape.BoundBox.Center.y,
# partshape.BoundBox.ZMin)
area.setPlane(makeWorkplane(partshape))
sec = area.makeSections(heights=[1.0], project=True)[0].getShape()
if stockheight is not None:
return sec.extrude(FreeCAD.Vector(0, 0, stockheight))
else:
return sec.extrude(FreeCAD.Vector(0, 0, partshape.BoundBox.ZLength))
def reverseEdge(e):
if geomType(e) == "Circle":
arcstpt = e.valueAt(e.FirstParameter)
@@ -208,6 +273,7 @@ def reverseEdge(e):
return newedge
def changeTool(obj, job):
tlnum = 0
for p in job.Group:
@@ -223,6 +289,7 @@ def changeTool(obj, job):
if g == obj:
return tlnum
def getToolControllers(obj):
'''returns all the tool controllers'''
controllers = []
@@ -238,6 +305,7 @@ def getToolControllers(obj):
controllers.append(g)
return controllers
def findToolController(obj, name=None):
'''returns a tool controller with a given name.
If no name is specified, returns the first controller.
@@ -245,7 +313,7 @@ def findToolController(obj, name=None):
PathLog.track('name: {}'.format(name))
c = None
#First check if a user has selected a tool controller in the tree. Return the first one and remove all from selection
# First check if a user has selected a tool controller in the tree. Return the first one and remove all from selection
for sel in FreeCADGui.Selection.getSelectionEx():
if hasattr(sel.Object, 'Proxy'):
if isinstance(sel.Object.Proxy, PathScripts.PathLoadTool.LoadTool):
@@ -260,16 +328,16 @@ def findToolController(obj, name=None):
if len(controllers) == 0:
return None
#If there's only one in the job, use it.
# If there's only one in the job, use it.
if len(controllers) == 1:
if name is None or name == controllers[0].Label:
tc = controllers[0]
else:
tc = None
elif name is not None: #More than one, make the user choose.
elif name is not None: # More than one, make the user choose.
tc = [i for i in controllers if i.Label == name][0]
else:
#form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgTCChooser.ui")
# form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgTCChooser.ui")
form = FreeCADGui.PySideUic.loadUi(":/panels/DlgTCChooser.ui")
mylist = [i.Label for i in controllers]
form.uiToolController.addItems(mylist)
@@ -280,6 +348,7 @@ def findToolController(obj, name=None):
tc = [i for i in controllers if i.Label == form.uiToolController.currentText()][0]
return tc
def findParentJob(obj):
'''retrieves a parent job object for an operation or other Path object'''
PathLog.track()
@@ -292,7 +361,8 @@ def findParentJob(obj):
return grandParent
return None
def GetJobs(jobname = None):
def GetJobs(jobname=None):
'''returns all jobs in the current document. If name is given, returns that job'''
PathLog.track()
jobs = []
@@ -306,7 +376,8 @@ def GetJobs(jobname = None):
jobs.append(o)
return jobs
def addToJob(obj, jobname = None):
def addToJob(obj, jobname=None):
'''adds a path object to a job
obj = obj
jobname = None'''
@@ -326,7 +397,7 @@ def addToJob(obj, jobname = None):
elif len(jobs) == 1:
job = jobs[0]
else:
#form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgJobChooser.ui")
# form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgJobChooser.ui")
form = FreeCADGui.PySideUic.loadUi(":/panels/DlgJobChooser.ui")
mylist = [i.Name for i in jobs]
form.cboProject.addItems(mylist)
@@ -342,6 +413,7 @@ def addToJob(obj, jobname = None):
job.Group = g
return job
def rapid(x=None, y=None, z=None):
""" Returns gcode string to perform a rapid move."""
retstr = "G00"
@@ -356,6 +428,7 @@ def rapid(x=None, y=None, z=None):
return ""
return retstr + "\n"
def feed(x=None, y=None, z=None, horizFeed=0, vertFeed=0):
""" Return gcode string to perform a linear feed."""
global feedxy
@@ -376,6 +449,7 @@ def feed(x=None, y=None, z=None, horizFeed=0, vertFeed=0):
return ""
return retstr + "\n"
def arc(cx, cy, sx, sy, ex, ey, horizFeed=0, ez=None, ccw=False):
"""
Return gcode string to perform an arc.
@@ -412,6 +486,7 @@ def arc(cx, cy, sx, sy, ex, ey, horizFeed=0, ez=None, ccw=False):
return retstr + "\n"
def helicalPlunge(plungePos, rampangle, destZ, startZ, toold, plungeR, horizFeed):
"""
Return gcode string to perform helical entry move.
@@ -458,6 +533,7 @@ def helicalPlunge(plungePos, rampangle, destZ, startZ, toold, plungeR, horizFeed
return helixCmds
def rampPlunge(edge, rampangle, destZ, startZ):
"""
Return gcode string to linearly ramp down to milling level.
@@ -562,6 +638,7 @@ def sort_jobs(locations, keys, attractors=[]):
return out
class depth_params:
'''calculates the intermediate depth values for various operations given the starting, ending, and stepdown parameters
(self, clearance_height, rapid_safety_space, start_depth, step_down, z_finish_depth, final_depth, [user_depths=None])

View File

@@ -39,6 +39,7 @@ Arguments for linuxcnc:
--comments,--no-comments ... output comments (--comments)
--line-numbers,--no-line-numbers ... prefix with line numbers (--no-lin-numbers)
--show-editor, --no-show-editor ... pop up editor before writing output(--show-editor)
--output-precision=4 ... number of digits of precision. Default=4
'''
import FreeCAD
from FreeCAD import Units
@@ -66,6 +67,7 @@ UNIT_FORMAT = 'mm/min'
MACHINE_NAME = "LinuxCNC"
CORNER_MIN = {'x': 0, 'y': 0, 'z': 0}
CORNER_MAX = {'x': 500, 'y': 300, 'z': 300}
PRECISION=4
# Preamble text will appear at the beginning of the GCODE output file.
PREAMBLE = '''G17 G90
@@ -98,6 +100,8 @@ def processArguments(argstring):
global OUTPUT_COMMENTS
global OUTPUT_LINE_NUMBERS
global SHOW_EDITOR
global PRECISION
for arg in argstring.split():
if arg == '--header':
OUTPUT_HEADER = True
@@ -115,6 +119,8 @@ def processArguments(argstring):
SHOW_EDITOR = True
elif arg == '--no-show-editor':
SHOW_EDITOR = False
elif arg.split('=')[0] == '--output-precision':
PRECISION = arg.split('=')[1]
def export(objectslist, filename, argstring):
processArguments(argstring)
@@ -211,8 +217,10 @@ def linenumber():
return ""
def parse(pathobj):
global PRECISION
out = ""
lastcommand = None
precision_string = '.' + str(PRECISION) +'f'
# params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control
# the order of parameters
@@ -251,12 +259,12 @@ def parse(pathobj):
if c.Name not in ["G0", "G00"]: #linuxcnc doesn't use rapid speeds
speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity)
outstring.append(
param + format(float(speed.getValueAs(UNIT_FORMAT)), '.2f') )
param + format(float(speed.getValueAs(UNIT_FORMAT)), precision_string) )
elif param == 'T':
outstring.append(param + str(c.Parameters['T']))
else:
outstring.append(
param + format(c.Parameters[param], '.4f'))
param + format(c.Parameters[param], precision_string))
# store the latest command
lastcommand = command

View File

@@ -56,6 +56,8 @@ OUTPUT_LINE_NUMBERS = False
IP_ADDR = None
VERBOSE = False
SPINDLE_SPEED = 0.0
if FreeCAD.GuiUp:
SHOW_EDITOR = True
else:
@@ -298,6 +300,7 @@ def linenumber():
def parse(pathobj):
out = ""
lastcommand = None
global SPINDLE_SPEED
# params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control
# the order of parameters
@@ -339,9 +342,14 @@ def parse(pathobj):
param + format(float(speed.getValueAs(UNIT_FORMAT)), '.2f') )
elif param == 'T':
outstring.append(param + str(c.Parameters['T']))
elif param == 'S':
outstring.append(param + str(c.Parameters['S']))
SPINDLE_SPEED = c.Parameters['S']
else:
outstring.append(
param + format(c.Parameters[param], '.4f'))
if command in ['G1', 'G01', 'G2', 'G02', 'G3', 'G03']:
outstring.append('S' + str(SPINDLE_SPEED))
# store the latest command
lastcommand = command
@@ -366,7 +374,7 @@ def parse(pathobj):
# append the line to the final output
for w in outstring:
out += w + COMMAND_SPACE
out += w + COMMAND_SPACE
out = out.strip() + "\n"
return out

View File

@@ -109,6 +109,7 @@ G1X-0.5905Y-0.3937
G0Z0.5
'''
output = '''G0 S3000.000000 X-0.590500 Y-0.393700
M03
G0 Z0.125000
@@ -119,6 +120,8 @@ G1 X-0.590500 Y0.433000
G1 X-0.590500 Y-0.393700
G0 Z0.500000
'''
#create a path directly form a piece of gcode.
p = Path.Path()
p.setFromGCode(lines)

View File

@@ -78,13 +78,13 @@ class PathPostTestCases(unittest.TestCase):
contour.OffsetExtra = 0.0
contour.Direction = 'CW'
contour.ToolController = tc
contour.UseComp = True
contour.UseComp = False
PathScripts.PathUtils.addToJob(contour)
PathScripts.PathContour.ObjectContour.setDepths(contour.Proxy, contour)
self.doc.recompute()
job.PostProcessor = 'linuxcnc'
job.PostProcessorArgs = '--no-header --no-line-numbers --no-comments --no-show-editor'
job.PostProcessorArgs = '--no-header --no-line-numbers --no-comments --no-show-editor --output-precision=2'
post = PathScripts.PathPost.CommandPathPost()
(fail, gcode) = post.exportObjectsWith([job], job, False)

View File

@@ -2,106 +2,68 @@ G17 G90
G21
(Default_Tool)
M6 T1.0
M3 S0.0000
M3 S0.00
(TC)
M6 T2.0
M3 S0.0000
M3 S0.00
(Contour)
(Uncompensated Tool Path)
G0 Z15.0000
G00 X-2.5000 Y0.0000
G00 Z23.0000
G01 X-2.5000 Y0.0000 Z9.0000 F0.00
G01 X-2.5000 Y10.0000 Z9.0000 F0.00
G02 X0.0000 Y12.5000 Z9.0000 I2.5000 J0.0000 F0.00
G01 X10.0000 Y12.5000 Z9.0000 F0.00
G02 X12.5000 Y10.0000 Z9.0000 I0.0000 J-2.5000 F0.00
G01 X12.5000 Y0.0000 Z9.0000 F0.00
G02 X10.0000 Y-2.5000 Z9.0000 I-2.5000 J0.0000 F0.00
G01 X0.0000 Y-2.5000 Z9.0000 F0.00
G02 X-2.5000 Y0.0000 Z9.0000 I0.0000 J2.5000 F0.00
G01 X-2.5000 Y0.0000 Z8.0000 F0.00
G01 X-2.5000 Y10.0000 Z8.0000 F0.00
G02 X0.0000 Y12.5000 Z8.0000 I2.5000 J0.0000 F0.00
G01 X10.0000 Y12.5000 Z8.0000 F0.00
G02 X12.5000 Y10.0000 Z8.0000 I0.0000 J-2.5000 F0.00
G01 X12.5000 Y0.0000 Z8.0000 F0.00
G02 X10.0000 Y-2.5000 Z8.0000 I-2.5000 J0.0000 F0.00
G01 X0.0000 Y-2.5000 Z8.0000 F0.00
G02 X-2.5000 Y0.0000 Z8.0000 I0.0000 J2.5000 F0.00
G01 X-2.5000 Y0.0000 Z7.0000 F0.00
G01 X-2.5000 Y10.0000 Z7.0000 F0.00
G02 X0.0000 Y12.5000 Z7.0000 I2.5000 J0.0000 F0.00
G01 X10.0000 Y12.5000 Z7.0000 F0.00
G02 X12.5000 Y10.0000 Z7.0000 I0.0000 J-2.5000 F0.00
G01 X12.5000 Y0.0000 Z7.0000 F0.00
G02 X10.0000 Y-2.5000 Z7.0000 I-2.5000 J0.0000 F0.00
G01 X0.0000 Y-2.5000 Z7.0000 F0.00
G02 X-2.5000 Y0.0000 Z7.0000 I0.0000 J2.5000 F0.00
G01 X-2.5000 Y0.0000 Z6.0000 F0.00
G01 X-2.5000 Y10.0000 Z6.0000 F0.00
G02 X0.0000 Y12.5000 Z6.0000 I2.5000 J0.0000 F0.00
G01 X10.0000 Y12.5000 Z6.0000 F0.00
G02 X12.5000 Y10.0000 Z6.0000 I0.0000 J-2.5000 F0.00
G01 X12.5000 Y0.0000 Z6.0000 F0.00
G02 X10.0000 Y-2.5000 Z6.0000 I-2.5000 J0.0000 F0.00
G01 X0.0000 Y-2.5000 Z6.0000 F0.00
G02 X-2.5000 Y0.0000 Z6.0000 I0.0000 J2.5000 F0.00
G01 X-2.5000 Y0.0000 Z5.0000 F0.00
G01 X-2.5000 Y10.0000 Z5.0000 F0.00
G02 X0.0000 Y12.5000 Z5.0000 I2.5000 J0.0000 F0.00
G01 X10.0000 Y12.5000 Z5.0000 F0.00
G02 X12.5000 Y10.0000 Z5.0000 I0.0000 J-2.5000 F0.00
G01 X12.5000 Y0.0000 Z5.0000 F0.00
G02 X10.0000 Y-2.5000 Z5.0000 I-2.5000 J0.0000 F0.00
G01 X0.0000 Y-2.5000 Z5.0000 F0.00
G02 X-2.5000 Y0.0000 Z5.0000 I0.0000 J2.5000 F0.00
G01 X-2.5000 Y0.0000 Z4.0000 F0.00
G01 X-2.5000 Y10.0000 Z4.0000 F0.00
G02 X0.0000 Y12.5000 Z4.0000 I2.5000 J0.0000 F0.00
G01 X10.0000 Y12.5000 Z4.0000 F0.00
G02 X12.5000 Y10.0000 Z4.0000 I0.0000 J-2.5000 F0.00
G01 X12.5000 Y0.0000 Z4.0000 F0.00
G02 X10.0000 Y-2.5000 Z4.0000 I-2.5000 J0.0000 F0.00
G01 X0.0000 Y-2.5000 Z4.0000 F0.00
G02 X-2.5000 Y0.0000 Z4.0000 I0.0000 J2.5000 F0.00
G01 X-2.5000 Y0.0000 Z3.0000 F0.00
G01 X-2.5000 Y10.0000 Z3.0000 F0.00
G02 X0.0000 Y12.5000 Z3.0000 I2.5000 J0.0000 F0.00
G01 X10.0000 Y12.5000 Z3.0000 F0.00
G02 X12.5000 Y10.0000 Z3.0000 I0.0000 J-2.5000 F0.00
G01 X12.5000 Y0.0000 Z3.0000 F0.00
G02 X10.0000 Y-2.5000 Z3.0000 I-2.5000 J0.0000 F0.00
G01 X0.0000 Y-2.5000 Z3.0000 F0.00
G02 X-2.5000 Y0.0000 Z3.0000 I0.0000 J2.5000 F0.00
G01 X-2.5000 Y0.0000 Z2.0000 F0.00
G01 X-2.5000 Y10.0000 Z2.0000 F0.00
G02 X0.0000 Y12.5000 Z2.0000 I2.5000 J0.0000 F0.00
G01 X10.0000 Y12.5000 Z2.0000 F0.00
G02 X12.5000 Y10.0000 Z2.0000 I0.0000 J-2.5000 F0.00
G01 X12.5000 Y0.0000 Z2.0000 F0.00
G02 X10.0000 Y-2.5000 Z2.0000 I-2.5000 J0.0000 F0.00
G01 X0.0000 Y-2.5000 Z2.0000 F0.00
G02 X-2.5000 Y0.0000 Z2.0000 I0.0000 J2.5000 F0.00
G01 X-2.5000 Y0.0000 Z1.0000 F0.00
G01 X-2.5000 Y10.0000 Z1.0000 F0.00
G02 X0.0000 Y12.5000 Z1.0000 I2.5000 J0.0000 F0.00
G01 X10.0000 Y12.5000 Z1.0000 F0.00
G02 X12.5000 Y10.0000 Z1.0000 I0.0000 J-2.5000 F0.00
G01 X12.5000 Y0.0000 Z1.0000 F0.00
G02 X10.0000 Y-2.5000 Z1.0000 I-2.5000 J0.0000 F0.00
G01 X0.0000 Y-2.5000 Z1.0000 F0.00
G02 X-2.5000 Y0.0000 Z1.0000 I0.0000 J2.5000 F0.00
G01 X-2.5000 Y0.0000 Z0.0000 F0.00
G01 X-2.5000 Y10.0000 Z0.0000 F0.00
G02 X0.0000 Y12.5000 Z0.0000 I2.5000 J0.0000 F0.00
G01 X10.0000 Y12.5000 Z0.0000 F0.00
G02 X12.5000 Y10.0000 Z0.0000 I0.0000 J-2.5000 F0.00
G01 X12.5000 Y0.0000 Z0.0000 F0.00
G02 X10.0000 Y-2.5000 Z0.0000 I-2.5000 J0.0000 F0.00
G01 X0.0000 Y-2.5000 Z0.0000 F0.00
G02 X-2.5000 Y0.0000 Z0.0000 I0.0000 J2.5000 F0.00
G00 Z15.0000
G0 Z15.00
G90
G17
G0 X0.00 Y0.00 Z15.00
G0 X10.00 Y10.00 Z15.00
G0 X10.00 Y10.00 Z10.00
G1 X10.00 Y10.00 Z9.00
G1 X10.00 Y0.00 Z9.00
G1 X0.00 Y0.00 Z9.00
G1 X0.00 Y10.00 Z9.00
G1 X10.00 Y10.00 Z9.00
G1 X10.00 Y10.00 Z8.00
G1 X10.00 Y0.00 Z8.00
G1 X0.00 Y0.00 Z8.00
G1 X0.00 Y10.00 Z8.00
G1 X10.00 Y10.00 Z8.00
G1 X10.00 Y10.00 Z7.00
G1 X10.00 Y0.00 Z7.00
G1 X0.00 Y0.00 Z7.00
G1 X0.00 Y10.00 Z7.00
G1 X10.00 Y10.00 Z7.00
G1 X10.00 Y10.00 Z6.00
G1 X10.00 Y0.00 Z6.00
G1 X0.00 Y0.00 Z6.00
G1 X0.00 Y10.00 Z6.00
G1 X10.00 Y10.00 Z6.00
G1 X10.00 Y10.00 Z5.00
G1 X10.00 Y0.00 Z5.00
G1 X0.00 Y0.00 Z5.00
G1 X0.00 Y10.00 Z5.00
G1 X10.00 Y10.00 Z5.00
G1 X10.00 Y10.00 Z4.00
G1 X10.00 Y0.00 Z4.00
G1 X0.00 Y0.00 Z4.00
G1 X0.00 Y10.00 Z4.00
G1 X10.00 Y10.00 Z4.00
G1 X10.00 Y10.00 Z3.00
G1 X10.00 Y0.00 Z3.00
G1 X0.00 Y0.00 Z3.00
G1 X0.00 Y10.00 Z3.00
G1 X10.00 Y10.00 Z3.00
G1 X10.00 Y10.00 Z2.00
G1 X10.00 Y0.00 Z2.00
G1 X0.00 Y0.00 Z2.00
G1 X0.00 Y10.00 Z2.00
G1 X10.00 Y10.00 Z2.00
G1 X10.00 Y10.00 Z1.00
G1 X10.00 Y0.00 Z1.00
G1 X0.00 Y0.00 Z1.00
G1 X0.00 Y10.00 Z1.00
G1 X10.00 Y10.00 Z1.00
G1 X10.00 Y10.00 Z0.00
G1 X10.00 Y0.00 Z0.00
G1 X0.00 Y0.00 Z0.00
G1 X0.00 Y10.00 Z0.00
G1 X10.00 Y10.00 Z0.00
M05
G00 X-1.0 Y1.0
G17 G90

View File

@@ -960,7 +960,8 @@ int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toG
std::vector<Constraint *> changed;
for (int i=0; i < int(newVals.size()); i++) {
if (vals[i]->First == fromGeoId && vals[i]->FirstPos == fromPosId &&
!(vals[i]->Second == toGeoId && vals[i]->SecondPos == toPosId)) {
!(vals[i]->Second == toGeoId && vals[i]->SecondPos == toPosId) &&
!(toGeoId < 0 && vals[i]->Second <0) ) {
Constraint *constNew = newVals[i]->clone();
constNew->First = toGeoId;
constNew->FirstPos = toPosId;
@@ -968,7 +969,8 @@ int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toG
changed.push_back(constNew);
}
else if (vals[i]->Second == fromGeoId && vals[i]->SecondPos == fromPosId &&
!(vals[i]->First == toGeoId && vals[i]->FirstPos == toPosId)) {
!(vals[i]->First == toGeoId && vals[i]->FirstPos == toPosId) &&
!(toGeoId < 0 && vals[i]->First< 0)) {
Constraint *constNew = newVals[i]->clone();
constNew->Second = toGeoId;
constNew->SecondPos = toPosId;