Files
create/src/Mod/Path/PathScripts/PathEngrave.py

196 lines
9.7 KiB
Python

# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
import ArchPanel
import DraftGeomUtils
import FreeCAD
import Part
import Path
import PathScripts.PathLog as PathLog
import PathScripts.PathOp as PathOp
import PathScripts.PathUtils as PathUtils
from PySide import QtCore
"""Path Engrave object and FreeCAD command"""
if False:
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
PathLog.trackModule(PathLog.thisModule())
else:
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
# Qt tanslation handling
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
class ObjectEngrave(PathOp.ObjectOp):
def opFeatures(self, obj):
return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureHeights | PathOp.FeatureStepDown | PathOp.FeatureBaseEdges;
def initOperation(self, obj):
obj.addProperty("App::PropertyInteger", "StartVertex", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "The vertex index to start the path from"))
def opExecute(self, obj):
PathLog.track()
zValues = []
if obj.StepDown.Value != 0:
z = obj.StartDepth.Value - obj.StepDown.Value
while z > obj.FinalDepth.Value:
zValues.append(z)
z -= obj.StepDown.Value
zValues.append(obj.FinalDepth.Value)
self.zValues = zValues
output = ''
try:
if self.baseobject.isDerivedFrom('Sketcher::SketchObject') or \
self.baseobject.isDerivedFrom('Part::Part2DObject') or \
hasattr(self.baseobject, 'ArrayType'):
output += "G0 Z" + PathUtils.fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
# we only consider the outer wire if this is a Face
wires = []
for w in self.baseobject.Shape.Wires:
tempedges = PathUtils.cleanedges(w.Edges, 0.5)
wires.append(Part.Wire(tempedges))
output += self.buildpathocc(obj, wires, zValues)
self.wires = wires
elif isinstance(self.baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet
wires = []
for tag in self.baseobject.Proxy.getTags(self.baseobject, transform=True):
output += "G0 Z" + PathUtils.fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
tagWires = []
for w in tag.Wires:
tempedges = PathUtils.cleanedges(w.Edges, 0.5)
tagWires.append(Part.Wire(tempedges))
output += self.buildpathocc(obj, tagWires, zValues)
wires.extend(tagWires)
self.wires = wires
else:
raise ValueError('Unknown baseobject type for engraving')
output += "G0 Z" + PathUtils.fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
except Exception as e:
#PathLog.error("Exception: %s" % e)
PathLog.error(translate("Path", "The Job Base Object has no engraveable element. Engraving operation will produce no output."))
def buildpathocc(self, obj, wires, zValues):
PathLog.track()
output = ""
for wire in wires:
offset = wire
# reorder the wire
offset = DraftGeomUtils.rebaseWire(offset, obj.StartVertex)
last = None
for z in zValues:
if last:
self.commandlist.append(Path.Command('G1', {'X': last.x, 'Y': last.y, 'Z': z, 'F': self.vertFeed}))
for edge in offset.Edges:
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.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.horizRapid) # Rapid to starting position
self.commandlist.append(Path.Command('G0', {'X': last.x, 'Y': last.y, 'Z': obj.ClearanceHeight.Value, 'F': self.horizRapid}))
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
self.commandlist.append(Path.Command('G0', {'X': last.x, 'Y': last.y, 'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
output += "G1" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(z) + "F " + PathUtils.fmt(self.vertFeed) + "\n" # Vertical feed to depth
self.commandlist.append(Path.Command('G0', {'X': last.x, 'Y': last.y, 'Z': z, 'F': self.vertFeed}))
if isinstance(edge.Curve, Part.Circle):
point = edge.Vertexes[-1].Point
if point == last: # edges can come flipped
point = edge.Vertexes[0].Point
center = edge.Curve.Center
relcenter = center.sub(last)
v1 = last.sub(center)
v2 = point.sub(center)
if v1.cross(v2).z < 0:
output += "G2"
else:
output += "G3"
output += " X" + PathUtils.fmt(point.x) + " Y" + PathUtils.fmt(point.y) + " Z" + PathUtils.fmt(z)
output += " I" + PathUtils.fmt(relcenter.x) + " J" + PathUtils.fmt(relcenter.y) + " K" + PathUtils.fmt(relcenter.z)
output += " F " + PathUtils.fmt(self.horizFeed)
output += "\n"
cmd = 'G2' if v1.cross(v2).z < 0 else 'G3'
args = {}
args['X'] = point.x
args['Y'] = point.y
args['Z'] = z
args['I'] = relcenter.x
args['J'] = relcenter.y
args['K'] = relcenter.z
args['F'] = self.horizFeed
self.commandlist.append(Path.Command(cmd, args))
last = point
else:
point = edge.Vertexes[-1].Point
if point == last: # edges can come flipped
point = edge.Vertexes[0].Point
output += "G1 X" + PathUtils.fmt(point.x) + " Y" + PathUtils.fmt(point.y) + " Z" + PathUtils.fmt(z)
output += " F " + PathUtils.fmt(self.horizFeed)
output += "\n"
self.commandlist.append(Path.Command('G1', {'X': point.x, 'Y': point.y, 'Z': z, 'F': self.horizFeed}))
last = point
output += "G0 Z " + PathUtils.fmt(obj.ClearanceHeight.Value)
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
return output
def opSetDefaultValues(self, obj):
job = PathUtils.findParentJob(obj)
if job and job.Base:
bb = job.Base.Shape.BoundBox
obj.StartDepth = bb.ZMax
obj.FinalDepth = bb.ZMin
obj.ClearanceHeight = bb.ZMax + 5.0
obj.SafeHeight = bb.ZMax + 3.0
else:
obj.FinalDepth = -0.1
if obj.StartDepth.Value != obj.FinalDepth.Value:
# maintain behaviour of a single run
obj.StepDown = obj.StartDepth.Value - obj.FinalDepth.Value
def Create(name):
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
proxy = ObjectEngrave(obj)
return obj