Split Contour op into generic PathArea part and contour specifics.
This commit is contained in:
committed by
Yorik van Havre
parent
5f40fb735c
commit
3b2bc562cb
@@ -18,6 +18,7 @@ INSTALL(
|
||||
|
||||
SET(PathScripts_SRCS
|
||||
PathCommands.py
|
||||
PathScripts/PathAreaOp.py
|
||||
PathScripts/PathArray.py
|
||||
PathScripts/PathComment.py
|
||||
PathScripts/PathCompoundExtended.py
|
||||
|
||||
257
src/Mod/Path/PathScripts/PathAreaOp.py
Normal file
257
src/Mod/Path/PathScripts/PathAreaOp.py
Normal file
@@ -0,0 +1,257 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2017 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * 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 FreeCAD
|
||||
import Path
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import Path
|
||||
|
||||
from PathScripts.PathUtils import depth_params
|
||||
from PathScripts.PathUtils import makeWorkplane
|
||||
from PathScripts.PathUtils import waiting_effects
|
||||
from PySide import QtCore
|
||||
|
||||
__title__ = "Base class for PathArea based operations."
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule()
|
||||
|
||||
# Qt tanslation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
class ObjectOp(object):
|
||||
FeatureTool = 0x01
|
||||
FeatureDepths = 0x02
|
||||
FeatureHeights = 0x04
|
||||
FeatureStartPoint = 0x08
|
||||
|
||||
def __init__(self, obj):
|
||||
PathLog.track()
|
||||
|
||||
obj.addProperty("App::PropertyBool", "Active", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Make False, to prevent operation from generating code"))
|
||||
obj.addProperty("App::PropertyString", "Comment", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "An optional comment for this Contour"))
|
||||
|
||||
if self.FeatureTool & self.opFeatures(obj):
|
||||
obj.addProperty("App::PropertyLink", "ToolController", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "The tool controller that will be used to calculate the path"))
|
||||
|
||||
if self.FeatureDepths & self.opFeatures(obj):
|
||||
obj.addProperty("App::PropertyDistance", "StepDown", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "Incremental Step Down of Tool"))
|
||||
obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "Starting Depth of Tool- first cut depth in Z"))
|
||||
obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "Final Depth of Tool- lowest value in Z"))
|
||||
|
||||
if self.FeatureHeights & self.opFeatures(obj):
|
||||
obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "The height needed to clear clamps and obstructions"))
|
||||
obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "Rapid Safety Height between locations."))
|
||||
|
||||
if self.FeatureStartPoint & self.opFeatures(obj):
|
||||
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"))
|
||||
|
||||
# Debugging
|
||||
obj.addProperty("App::PropertyString", "AreaParams", "Path")
|
||||
obj.setEditorMode('AreaParams', 2) # hide
|
||||
obj.addProperty("App::PropertyString", "PathParams", "Path")
|
||||
obj.setEditorMode('PathParams', 2) # hide
|
||||
obj.addProperty("Part::PropertyPartShape", "removalshape", "Path")
|
||||
obj.setEditorMode('removalshape', 2) # hide
|
||||
|
||||
self.initOperation(obj)
|
||||
obj.Proxy = self
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
return None
|
||||
|
||||
def opFeatures(self, obj):
|
||||
return self.FeatureTool | self.FeatureDepths | self.FeatureHeights | self.FeatureStartPoint
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
if prop in ['AreaParams', 'PathParams', 'removalshape']:
|
||||
obj.setEditorMode(prop, 2)
|
||||
self.opOnChanged(obj, prop)
|
||||
|
||||
def setDefaultValues(self, obj):
|
||||
PathUtils.addToJob(obj)
|
||||
|
||||
obj.Active = True
|
||||
|
||||
if self.FeatureTool & self.opFeatures(obj):
|
||||
obj.ToolController = PathUtils.findToolController(obj)
|
||||
|
||||
if self.FeatureDepths & self.opFeatures(obj):
|
||||
try:
|
||||
shape = self.opShapeForDepths(obj)
|
||||
except:
|
||||
shape = None
|
||||
|
||||
if shape:
|
||||
bb = shape.BoundBox
|
||||
obj.StartDepth = bb.ZMax
|
||||
obj.FinalDepth = bb.ZMin
|
||||
obj.StepDown = 1.0
|
||||
else:
|
||||
obj.StartDepth = 1.0
|
||||
obj.FinalDepth = 0.0
|
||||
obj.StepDown = 1.0
|
||||
|
||||
if self.FeatureHeights & self.opFeatures(obj):
|
||||
try:
|
||||
shape = self.opShapeForDepths(obj)
|
||||
except:
|
||||
shape = None
|
||||
|
||||
if shape:
|
||||
bb = shape.BoundBox
|
||||
obj.ClearanceHeight = bb.ZMax + 5.0
|
||||
obj.SafeHeight = bb.ZMax + 3.0
|
||||
else:
|
||||
obj.ClearanceHeight = 10.0
|
||||
obj.SafeHeight = 8.0
|
||||
|
||||
if self.FeatureStartPoint & self.opFeatures(obj):
|
||||
obj.UseStartPoint = False
|
||||
|
||||
self.opSetDefaultValues(obj)
|
||||
|
||||
@waiting_effects
|
||||
def _buildPathArea(self, obj, baseobject, start=None, getsim=False):
|
||||
PathLog.track()
|
||||
area = Path.Area()
|
||||
area.setPlane(makeWorkplane(baseobject))
|
||||
area.add(baseobject)
|
||||
|
||||
areaParams = {'Fill': 0, 'Coplanar': 2}
|
||||
|
||||
areaParams = self.opAreaParams(obj)
|
||||
|
||||
heights = [i for i in self.depthparams]
|
||||
PathLog.debug('depths: {}'.format(heights))
|
||||
area.setParams(**areaParams)
|
||||
obj.AreaParams = str(area.getParams())
|
||||
|
||||
PathLog.debug("Area with params: {}".format(area.getParams()))
|
||||
|
||||
sections = area.makeSections(mode=0, project=True, heights=heights)
|
||||
shapelist = [sec.getShape() for sec in sections]
|
||||
|
||||
pathParams = self.opPathParams(obj)
|
||||
pathParams['shapes'] = shapelist
|
||||
pathParams['feedrate'] = self.horizFeed
|
||||
pathParams['feedrate_v'] = self.vertFeed
|
||||
pathParams['verbose'] = True
|
||||
pathParams['resume_height'] = obj.StepDown.Value
|
||||
pathParams['retraction'] = obj.ClearanceHeight.Value
|
||||
pathParams['return_end'] = True
|
||||
|
||||
if self.endVector is not None:
|
||||
pathParams['start'] = self.endVector
|
||||
elif obj.UseStartPoint:
|
||||
pathParams['start'] = obj.StartPoint
|
||||
|
||||
obj.PathParams = str({key: value for key, value in pathParams.items() if key != 'shapes'})
|
||||
|
||||
(pp, end_vector) = Path.fromShapes(**pathParams)
|
||||
PathLog.debug('pp: {}, end vector: {}'.format(pp, end_vector))
|
||||
self.endVector = end_vector
|
||||
|
||||
simobj = None
|
||||
if getsim:
|
||||
areaParams['Thicken'] = True
|
||||
areaParams['ToolRadius'] = self.radius - self.radius * .005
|
||||
area.setParams(**areaParams)
|
||||
sec = area.makeSections(mode=0, project=False, heights=heights)[-1].getShape()
|
||||
simobj = sec.extrude(FreeCAD.Vector(0, 0, baseobject.BoundBox.ZMax))
|
||||
|
||||
return pp, simobj
|
||||
|
||||
def execute(self, obj, getsim=False):
|
||||
PathLog.track()
|
||||
self.endVector = None
|
||||
|
||||
if not obj.Active:
|
||||
path = Path.Path("(inactive operation)")
|
||||
obj.Path = path
|
||||
if obj.ViewObject:
|
||||
obj.ViewObject.Visibility = False
|
||||
return
|
||||
|
||||
self.depthparams = depth_params(
|
||||
clearance_height=obj.ClearanceHeight.Value,
|
||||
safe_height=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)
|
||||
|
||||
toolLoad = obj.ToolController
|
||||
if toolLoad is None or toolLoad.ToolNumber == 0:
|
||||
|
||||
FreeCAD.Console.PrintError("No Tool Controller is selected. We need a tool to build a Path.")
|
||||
return
|
||||
else:
|
||||
self.vertFeed = toolLoad.VertFeed.Value
|
||||
self.horizFeed = toolLoad.HorizFeed.Value
|
||||
self.vertRapid = toolLoad.VertRapid.Value
|
||||
self.horizRapid = toolLoad.HorizRapid.Value
|
||||
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
|
||||
else:
|
||||
self.radius = tool.Diameter/2
|
||||
|
||||
commandlist = []
|
||||
commandlist.append(Path.Command("(" + obj.Label + ")"))
|
||||
|
||||
shape = self.opShape(obj, commandlist)
|
||||
|
||||
if self.FeatureStartPoint and obj.UseStartPoint:
|
||||
start = obj.StartPoint
|
||||
else:
|
||||
start = FreeCAD.Vector()
|
||||
|
||||
try:
|
||||
(pp, sim) = self._buildPathArea(obj, shape, start, getsim)
|
||||
commandlist.extend(pp.Commands)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(e)
|
||||
FreeCAD.Console.PrintError("Something unexpected happened. Check project and tool config.")
|
||||
sim = None
|
||||
|
||||
|
||||
# Let's finish by rapid to clearance...just for safety
|
||||
commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value}))
|
||||
|
||||
PathLog.track()
|
||||
path = Path.Path(commandlist)
|
||||
obj.Path = path
|
||||
return sim
|
||||
|
||||
@@ -1,431 +0,0 @@
|
||||
import area
|
||||
from nc.nc import *
|
||||
import PathScripts.nc.iso
|
||||
import math
|
||||
import PathKurveUtils
|
||||
|
||||
# some globals, to save passing variables as parameters too much
|
||||
area_for_feed_possible = None
|
||||
tool_radius_for_pocket = None
|
||||
|
||||
def cut_curve(curve, need_rapid, p, rapid_safety_space, current_start_depth, final_depth):
|
||||
prev_p = p
|
||||
first = True
|
||||
#comment("cut_curve:14 rss:" + str(rapid_safety_space) + " current start depth :" + str(current_start_depth) + " final depth :" + str(final_depth) + " need rapid: " + str(need_rapid))
|
||||
for vertex in curve.getVertices():
|
||||
if need_rapid and first:
|
||||
# rapid across
|
||||
rapid(vertex.p.x, vertex.p.y)
|
||||
##rapid down
|
||||
rapid(z = current_start_depth + rapid_safety_space)
|
||||
#feed down
|
||||
feed(z = final_depth)
|
||||
first = False
|
||||
else:
|
||||
if vertex.type == 1:
|
||||
arc_ccw(vertex.p.x, vertex.p.y, i = vertex.c.x, j = vertex.c.y)
|
||||
elif vertex.type == -1:
|
||||
arc_cw(vertex.p.x, vertex.p.y, i = vertex.c.x, j = vertex.c.y)
|
||||
else:
|
||||
feed(vertex.p.x, vertex.p.y)
|
||||
prev_p = vertex.p
|
||||
return prev_p
|
||||
|
||||
def area_distance(a, old_area):
|
||||
best_dist = None
|
||||
|
||||
for curve in a.getCurves():
|
||||
for vertex in curve.getVertices():
|
||||
c = old_area.NearestPoint(vertex.p)
|
||||
d = c.dist(vertex.p)
|
||||
if best_dist == None or d < best_dist:
|
||||
best_dist = d
|
||||
|
||||
for curve in old_area.getCurves():
|
||||
for vertex in curve.getVertices():
|
||||
c = a.NearestPoint(vertex.p)
|
||||
d = c.dist(vertex.p)
|
||||
if best_dist == None or d < best_dist:
|
||||
best_dist = d
|
||||
|
||||
return best_dist
|
||||
|
||||
def make_obround(p0, p1, radius):
|
||||
dir = p1 - p0
|
||||
d = dir.length()
|
||||
dir.normalize()
|
||||
right = area.Point(dir.y, -dir.x)
|
||||
obround = area.Area()
|
||||
c = area.Curve()
|
||||
vt0 = p0 + right * radius
|
||||
vt1 = p1 + right * radius
|
||||
vt2 = p1 - right * radius
|
||||
vt3 = p0 - right * radius
|
||||
c.append(area.Vertex(0, vt0, area.Point(0, 0)))
|
||||
c.append(area.Vertex(0, vt1, area.Point(0, 0)))
|
||||
c.append(area.Vertex(1, vt2, p1))
|
||||
c.append(area.Vertex(0, vt3, area.Point(0, 0)))
|
||||
c.append(area.Vertex(1, vt0, p0))
|
||||
obround.append(c)
|
||||
return obround
|
||||
|
||||
def feed_possible(p0, p1):
|
||||
if p0 == p1:
|
||||
return True
|
||||
obround = make_obround(p0, p1, tool_radius_for_pocket)
|
||||
a = area.Area(area_for_feed_possible)
|
||||
obround.Subtract(a)
|
||||
if obround.num_curves() > 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
def cut_curvelist1(curve_list, rapid_safety_space, current_start_depth, depth, clearance_height, keep_tool_down_if_poss):
|
||||
p = area.Point(0, 0)
|
||||
first = True
|
||||
for curve in curve_list:
|
||||
need_rapid = True
|
||||
if first == False:
|
||||
s = curve.FirstVertex().p
|
||||
if keep_tool_down_if_poss == True:
|
||||
# see if we can feed across
|
||||
if feed_possible(p, s):
|
||||
need_rapid = False
|
||||
elif s.x == p.x and s.y == p.y:
|
||||
need_rapid = False
|
||||
if need_rapid:
|
||||
rapid(z = clearance_height)
|
||||
p = cut_curve(curve, need_rapid, p, rapid_safety_space, current_start_depth, depth)
|
||||
first = False
|
||||
|
||||
rapid(z = clearance_height)
|
||||
|
||||
def cut_curvelist2(curve_list, rapid_safety_space, current_start_depth, depth, clearance_height, keep_tool_down_if_poss,start_point):
|
||||
p = area.Point(0, 0)
|
||||
start_x,start_y=start_point
|
||||
first = True
|
||||
for curve in curve_list:
|
||||
need_rapid = True
|
||||
if first == True:
|
||||
direction = "On";radius = 0.0;offset_extra = 0.0; roll_radius = 0.0;roll_on = 0.0; roll_off = 0.0; rapid_safety_space; step_down = math.fabs(depth);extend_at_start = 0.0;extend_at_end = 0.0
|
||||
PathKurveUtils.make_smaller( curve, start = area.Point(start_x,start_y))
|
||||
PathKurveUtils.profile(curve, direction, radius , offset_extra, roll_radius, roll_on, roll_off, rapid_safety_space , clearance_height, current_start_depth, step_down , depth, extend_at_start, extend_at_end)
|
||||
else:
|
||||
s = curve.FirstVertex().p
|
||||
if keep_tool_down_if_poss == True:
|
||||
|
||||
# see if we can feed across
|
||||
if feed_possible(p, s):
|
||||
need_rapid = False
|
||||
elif s.x == p.x and s.y == p.y:
|
||||
need_rapid = False
|
||||
|
||||
cut_curve(curve, need_rapid, p, rapid_safety_space, current_start_depth, depth)
|
||||
first = False #change to True if you want to rapid back to start side before zigging again with unidirectional set
|
||||
rapid(z = clearance_height)
|
||||
|
||||
def recur(arealist, a1, stepover, from_center):
|
||||
# this makes arealist by recursively offsetting a1 inwards
|
||||
|
||||
if a1.num_curves() == 0:
|
||||
return
|
||||
|
||||
if from_center:
|
||||
arealist.insert(0, a1)
|
||||
else:
|
||||
arealist.append(a1)
|
||||
|
||||
a_offset = area.Area(a1)
|
||||
a_offset.Offset(stepover)
|
||||
|
||||
# split curves into new areas
|
||||
if area.holes_linked():
|
||||
for curve in a_offset.getCurves():
|
||||
a2 = area.Area()
|
||||
a2.append(curve)
|
||||
recur(arealist, a2, stepover, from_center)
|
||||
|
||||
else:
|
||||
# split curves into new areas
|
||||
a_offset.Reorder()
|
||||
a2 = None
|
||||
|
||||
for curve in a_offset.getCurves():
|
||||
if curve.IsClockwise():
|
||||
if a2 != None:
|
||||
a2.append(curve)
|
||||
else:
|
||||
if a2 != None:
|
||||
recur(arealist, a2, stepover, from_center)
|
||||
a2 = area.Area()
|
||||
a2.append(curve)
|
||||
|
||||
if a2 != None:
|
||||
recur(arealist, a2, stepover, from_center)
|
||||
|
||||
def get_curve_list(arealist, reverse_curves = False):
|
||||
curve_list = list()
|
||||
for a in arealist:
|
||||
for curve in a.getCurves():
|
||||
if reverse_curves == True:
|
||||
curve.Reverse()
|
||||
curve_list.append(curve)
|
||||
return curve_list
|
||||
|
||||
curve_list_for_zigs = []
|
||||
rightward_for_zigs = True
|
||||
sin_angle_for_zigs = 0.0
|
||||
cos_angle_for_zigs = 1.0
|
||||
sin_minus_angle_for_zigs = 0.0
|
||||
cos_minus_angle_for_zigs = 1.0
|
||||
one_over_units = 1.0
|
||||
|
||||
def make_zig_curve(curve, y0, y, zig_unidirectional):
|
||||
if rightward_for_zigs:
|
||||
curve.Reverse()
|
||||
|
||||
# find a high point to start looking from
|
||||
high_point = None
|
||||
for vertex in curve.getVertices():
|
||||
if high_point == None:
|
||||
high_point = vertex.p
|
||||
elif vertex.p.y > high_point.y:
|
||||
# use this as the new high point
|
||||
high_point = vertex.p
|
||||
elif math.fabs(vertex.p.y - high_point.y) < 0.002 * one_over_units:
|
||||
# equal high point
|
||||
if rightward_for_zigs:
|
||||
# use the furthest left point
|
||||
if vertex.p.x < high_point.x:
|
||||
high_point = vertex.p
|
||||
else:
|
||||
# use the furthest right point
|
||||
if vertex.p.x > high_point.x:
|
||||
high_point = vertex.p
|
||||
|
||||
zig = area.Curve()
|
||||
|
||||
high_point_found = False
|
||||
zig_started = False
|
||||
zag_found = False
|
||||
|
||||
for i in range(0, 2): # process the curve twice because we don't know where it will start
|
||||
prev_p = None
|
||||
for vertex in curve.getVertices():
|
||||
if zag_found: break
|
||||
if prev_p != None:
|
||||
if zig_started:
|
||||
zig.append(unrotated_vertex(vertex))
|
||||
if math.fabs(vertex.p.y - y) < 0.002 * one_over_units:
|
||||
zag_found = True
|
||||
break
|
||||
elif high_point_found:
|
||||
if math.fabs(vertex.p.y - y0) < 0.002 * one_over_units:
|
||||
if zig_started:
|
||||
zig.append(unrotated_vertex(vertex))
|
||||
elif math.fabs(prev_p.y - y0) < 0.002 * one_over_units and vertex.type == 0:
|
||||
zig.append(area.Vertex(0, unrotated_point(prev_p), area.Point(0, 0)))
|
||||
zig.append(unrotated_vertex(vertex))
|
||||
zig_started = True
|
||||
elif vertex.p.x == high_point.x and vertex.p.y == high_point.y:
|
||||
high_point_found = True
|
||||
prev_p = vertex.p
|
||||
|
||||
if zig_started:
|
||||
|
||||
if zig_unidirectional == True:
|
||||
# remove the last bit of zig
|
||||
if math.fabs(zig.LastVertex().p.y - y) < 0.002 * one_over_units:
|
||||
vertices = zig.getVertices()
|
||||
while len(vertices) > 0:
|
||||
v = vertices[len(vertices)-1]
|
||||
if math.fabs(v.p.y - y0) < 0.002 * one_over_units:
|
||||
break
|
||||
else:
|
||||
vertices.pop()
|
||||
zig = area.Curve()
|
||||
for v in vertices:
|
||||
zig.append(v)
|
||||
|
||||
curve_list_for_zigs.append(zig)
|
||||
|
||||
def make_zig(a, y0, y, zig_unidirectional):
|
||||
for curve in a.getCurves():
|
||||
make_zig_curve(curve, y0, y, zig_unidirectional)
|
||||
|
||||
reorder_zig_list_list = []
|
||||
|
||||
def add_reorder_zig(curve):
|
||||
global reorder_zig_list_list
|
||||
|
||||
# look in existing lists
|
||||
s = curve.FirstVertex().p
|
||||
for curve_list in reorder_zig_list_list:
|
||||
last_curve = curve_list[len(curve_list) - 1]
|
||||
e = last_curve.LastVertex().p
|
||||
if math.fabs(s.x - e.x) < 0.002 * one_over_units and math.fabs(s.y - e.y) < 0.002 * one_over_units:
|
||||
curve_list.append(curve)
|
||||
return
|
||||
|
||||
# else add a new list
|
||||
curve_list = []
|
||||
curve_list.append(curve)
|
||||
reorder_zig_list_list.append(curve_list)
|
||||
|
||||
def reorder_zigs():
|
||||
global curve_list_for_zigs
|
||||
global reorder_zig_list_list
|
||||
reorder_zig_list_list = []
|
||||
for curve in curve_list_for_zigs:
|
||||
add_reorder_zig(curve)
|
||||
|
||||
curve_list_for_zigs = []
|
||||
for curve_list in reorder_zig_list_list:
|
||||
for curve in curve_list:
|
||||
curve_list_for_zigs.append(curve)
|
||||
|
||||
def rotated_point(p):
|
||||
return area.Point(p.x * cos_angle_for_zigs - p.y * sin_angle_for_zigs, p.x * sin_angle_for_zigs + p.y * cos_angle_for_zigs)
|
||||
|
||||
def unrotated_point(p):
|
||||
return area.Point(p.x * cos_minus_angle_for_zigs - p.y * sin_minus_angle_for_zigs, p.x * sin_minus_angle_for_zigs + p.y * cos_minus_angle_for_zigs)
|
||||
|
||||
def rotated_vertex(v):
|
||||
if v.type:
|
||||
return area.Vertex(v.type, rotated_point(v.p), rotated_point(v.c))
|
||||
return area.Vertex(v.type, rotated_point(v.p), area.Point(0, 0))
|
||||
|
||||
def unrotated_vertex(v):
|
||||
if v.type:
|
||||
return area.Vertex(v.type, unrotated_point(v.p), unrotated_point(v.c))
|
||||
return area.Vertex(v.type, unrotated_point(v.p), area.Point(0, 0))
|
||||
|
||||
def rotated_area(a):
|
||||
an = area.Area()
|
||||
for curve in a.getCurves():
|
||||
curve_new = area.Curve()
|
||||
for v in curve.getVertices():
|
||||
curve_new.append(rotated_vertex(v))
|
||||
an.append(curve_new)
|
||||
return an
|
||||
|
||||
def zigzag(a, stepover, zig_unidirectional):
|
||||
if a.num_curves() == 0:
|
||||
return
|
||||
|
||||
global rightward_for_zigs
|
||||
global curve_list_for_zigs
|
||||
global sin_angle_for_zigs
|
||||
global cos_angle_for_zigs
|
||||
global sin_minus_angle_for_zigs
|
||||
global cos_minus_angle_for_zigs
|
||||
global one_over_units
|
||||
|
||||
one_over_units = 1 / area.get_units()
|
||||
|
||||
a = rotated_area(a)
|
||||
|
||||
b = area.Box()
|
||||
a.GetBox(b)
|
||||
|
||||
x0 = b.MinX() - 1.0
|
||||
x1 = b.MaxX() + 1.0
|
||||
|
||||
height = b.MaxY() - b.MinY()
|
||||
num_steps = int(height / stepover + 1)
|
||||
y = b.MinY() + 0.1 * one_over_units
|
||||
null_point = area.Point(0, 0)
|
||||
rightward_for_zigs = True
|
||||
curve_list_for_zigs = []
|
||||
|
||||
for i in range(0, num_steps):
|
||||
y0 = y
|
||||
y = y + stepover
|
||||
p0 = area.Point(x0, y0)
|
||||
p1 = area.Point(x0, y)
|
||||
p2 = area.Point(x1, y)
|
||||
p3 = area.Point(x1, y0)
|
||||
c = area.Curve()
|
||||
c.append(area.Vertex(0, p0, null_point, 0))
|
||||
c.append(area.Vertex(0, p1, null_point, 0))
|
||||
c.append(area.Vertex(0, p2, null_point, 1))
|
||||
c.append(area.Vertex(0, p3, null_point, 0))
|
||||
c.append(area.Vertex(0, p0, null_point, 1))
|
||||
a2 = area.Area()
|
||||
a2.append(c)
|
||||
a2.Intersect(a)
|
||||
make_zig(a2, y0, y, zig_unidirectional)
|
||||
if zig_unidirectional == False:
|
||||
rightward_for_zigs = (rightward_for_zigs == False)
|
||||
|
||||
reorder_zigs()
|
||||
|
||||
def pocket(a,tool_radius, extra_offset, stepover, depthparams, from_center, keep_tool_down_if_poss, use_zig_zag, zig_angle, zig_unidirectional = False,start_point=None, cut_mode = 'conventional'):
|
||||
global tool_radius_for_pocket
|
||||
global area_for_feed_possible
|
||||
#if len(a.getCurves()) > 1:
|
||||
# for crv in a.getCurves():
|
||||
# ar = area.Area()
|
||||
# ar.append(crv)
|
||||
# pocket(ar, tool_radius, extra_offset, rapid_safety_space, start_depth, final_depth, stepover, stepdown, clearance_height, from_center, keep_tool_down_if_poss, use_zig_zag, zig_angle, zig_unidirectional)
|
||||
# return
|
||||
|
||||
tool_radius_for_pocket = tool_radius
|
||||
|
||||
if keep_tool_down_if_poss:
|
||||
area_for_feed_possible = area.Area(a)
|
||||
area_for_feed_possible.Offset(extra_offset - 0.01)
|
||||
|
||||
use_internal_function = False #(area.holes_linked() == False) # use internal function, if area module is the Clipper library
|
||||
|
||||
if use_internal_function:
|
||||
curve_list = a.MakePocketToolpath(tool_radius, extra_offset, stepover, from_center, use_zig_zag, zig_angle)
|
||||
|
||||
else:
|
||||
global sin_angle_for_zigs
|
||||
global cos_angle_for_zigs
|
||||
global sin_minus_angle_for_zigs
|
||||
global cos_minus_angle_for_zigs
|
||||
radians_angle = zig_angle * math.pi / 180
|
||||
sin_angle_for_zigs = math.sin(-radians_angle)
|
||||
cos_angle_for_zigs = math.cos(-radians_angle)
|
||||
sin_minus_angle_for_zigs = math.sin(radians_angle)
|
||||
cos_minus_angle_for_zigs = math.cos(radians_angle)
|
||||
|
||||
arealist = list()
|
||||
|
||||
a_offset = area.Area(a)
|
||||
current_offset = tool_radius + extra_offset
|
||||
a_offset.Offset(current_offset)
|
||||
|
||||
do_recursive = True
|
||||
|
||||
if use_zig_zag:
|
||||
zigzag(a_offset, stepover, zig_unidirectional)
|
||||
curve_list = curve_list_for_zigs
|
||||
else:
|
||||
if do_recursive:
|
||||
recur(arealist, a_offset, stepover, from_center)
|
||||
else:
|
||||
while(a_offset.num_curves() > 0):
|
||||
if from_center:
|
||||
arealist.insert(0, a_offset)
|
||||
else:
|
||||
arealist.append(a_offset)
|
||||
current_offset = current_offset + stepover
|
||||
a_offset = area.Area(a)
|
||||
a_offset.Offset(current_offset)
|
||||
curve_list = get_curve_list(arealist, cut_mode == 'climb')
|
||||
|
||||
depths = depthparams.get_depths()
|
||||
current_start_depth = depthparams.start_depth
|
||||
if start_point==None:
|
||||
for depth in depths:
|
||||
cut_curvelist1(curve_list, depthparams.rapid_safety_space, current_start_depth, depth, depthparams.clearance_height, keep_tool_down_if_poss)
|
||||
current_start_depth = depth
|
||||
|
||||
else:
|
||||
for depth in depths:
|
||||
cut_curvelist2(curve_list, depthparams.rapid_safety_space, current_start_depth, depth, depthparams.clearance_height, keep_tool_down_if_poss, start_point)
|
||||
current_start_depth = depth
|
||||
|
||||
|
||||
@@ -23,16 +23,19 @@
|
||||
# ***************************************************************************
|
||||
|
||||
from __future__ import print_function
|
||||
import FreeCAD
|
||||
import Path
|
||||
import PathScripts.PathLog as PathLog
|
||||
from PySide import QtCore, QtGui
|
||||
from PathScripts import PathUtils
|
||||
|
||||
import ArchPanel
|
||||
import FreeCAD
|
||||
import Part
|
||||
from PathScripts.PathUtils import waiting_effects
|
||||
from PathScripts.PathUtils import makeWorkplane
|
||||
import Path
|
||||
import PathScripts.PathAreaOp as PathAreaOp
|
||||
import PathScripts.PathLog as PathLog
|
||||
|
||||
from PathScripts import PathUtils
|
||||
from PathScripts.PathUtils import depth_params
|
||||
from PathScripts.PathUtils import makeWorkplane
|
||||
from PathScripts.PathUtils import waiting_effects
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
FreeCAD.setLogLevel('Path.Area', 0)
|
||||
|
||||
@@ -57,26 +60,10 @@ __url__ = "http://www.freecadweb.org"
|
||||
"""Path Contour object and FreeCAD command"""
|
||||
|
||||
|
||||
class ObjectContour:
|
||||
class ObjectContour(PathAreaOp.ObjectOp):
|
||||
|
||||
def __init__(self, obj):
|
||||
def initOperation(self, obj):
|
||||
PathLog.track()
|
||||
obj.addProperty("App::PropertyBool", "Active", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Make False, to prevent operation from generating code"))
|
||||
obj.addProperty("App::PropertyString", "Comment", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "An optional comment for this Contour"))
|
||||
|
||||
# Tool Properties
|
||||
obj.addProperty("App::PropertyLink", "ToolController", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "The tool controller that will be used to calculate the path"))
|
||||
|
||||
# Depth Properties
|
||||
obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "The height needed to clear clamps and obstructions"))
|
||||
obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "Rapid Safety Height between locations."))
|
||||
obj.addProperty("App::PropertyDistance", "StepDown", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "Incremental Step Down of Tool"))
|
||||
obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "Starting Depth of Tool- first cut depth in Z"))
|
||||
obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "Final Depth of Tool- lowest value in Z"))
|
||||
|
||||
# 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"))
|
||||
|
||||
# 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"))
|
||||
@@ -90,172 +77,44 @@ class ObjectContour:
|
||||
obj.addProperty("App::PropertyFloat", "MiterLimit", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "Maximum distance before a miter join is truncated"))
|
||||
obj.setEditorMode('MiterLimit', 2)
|
||||
|
||||
# Debug Parameters
|
||||
obj.addProperty("App::PropertyString", "AreaParams", "Path")
|
||||
obj.setEditorMode('AreaParams', 2) # hide
|
||||
obj.addProperty("App::PropertyString", "PathParams", "Path")
|
||||
obj.setEditorMode('PathParams', 2) # hide
|
||||
obj.addProperty("Part::PropertyPartShape", "removalshape", "Path")
|
||||
obj.setEditorMode('removalshape', 2) # hide
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
_ViewProviderContour(obj.ViewObject)
|
||||
|
||||
obj.Proxy = self
|
||||
self.endVector = None
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
def opOnChanged(self, obj, prop):
|
||||
PathLog.track('prop: {} state: {}'.format(prop, obj.State))
|
||||
if prop in ['AreaParams', 'PathParams', 'removalshape']:
|
||||
obj.setEditorMode(prop, 2)
|
||||
|
||||
obj.setEditorMode('MiterLimit', 2)
|
||||
if obj.JoinType == 'Miter':
|
||||
obj.setEditorMode('MiterLimit', 0)
|
||||
|
||||
def __getstate__(self):
|
||||
PathLog.track()
|
||||
def opShapeForDepths(self, obj):
|
||||
job = PathUtils.findParentJob(obj)
|
||||
if job and job.Base:
|
||||
PathLog.info("job=%s base=%s shape=%s" % (job, job.Base, job.Base.Shape))
|
||||
return job.Base.Shape
|
||||
PathLog.warning("No job object found (%s), or job has no Base." % job)
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
PathLog.track(state)
|
||||
return None
|
||||
def opSetDefaultValues(self, obj):
|
||||
obj.Direction = "CW"
|
||||
obj.UseComp = True
|
||||
obj.OffsetExtra = 0.0
|
||||
obj.JoinType = "Round"
|
||||
obj.MiterLimit = 0.1
|
||||
|
||||
def setDepths(self, obj):
|
||||
PathLog.track()
|
||||
parentJob = PathUtils.findParentJob(obj)
|
||||
if parentJob is None:
|
||||
return
|
||||
baseobject = parentJob.Base
|
||||
if baseobject is None:
|
||||
return
|
||||
|
||||
try:
|
||||
bb = baseobject.Shape.BoundBox # parent boundbox
|
||||
obj.StartDepth = bb.ZMax
|
||||
obj.ClearanceHeight = bb.ZMax + 5.0
|
||||
obj.SafeHeight = bb.ZMax + 3.0
|
||||
obj.FinalDepth = bb.ZMin
|
||||
|
||||
except:
|
||||
obj.StartDepth = 5.0
|
||||
obj.ClearanceHeight = 10.0
|
||||
obj.SafeHeight = 8.0
|
||||
|
||||
@waiting_effects
|
||||
def _buildPathArea(self, obj, baseobject, start=None, getsim=False):
|
||||
PathLog.track()
|
||||
profile = Path.Area()
|
||||
profile.setPlane(makeWorkplane(baseobject))
|
||||
profile.add(baseobject)
|
||||
|
||||
profileparams = {'Fill': 0,
|
||||
'Coplanar': 2}
|
||||
|
||||
if obj.UseComp is False:
|
||||
profileparams['Offset'] = 0.0
|
||||
else:
|
||||
profileparams['Offset'] = self.radius+obj.OffsetExtra.Value
|
||||
|
||||
jointype = ['Round', 'Square', 'Miter']
|
||||
profileparams['JoinType'] = jointype.index(obj.JoinType)
|
||||
|
||||
if obj.JoinType == 'Miter':
|
||||
profileparams['MiterLimit'] = obj.MiterLimit
|
||||
|
||||
heights = [i for i in self.depthparams]
|
||||
PathLog.debug('depths: {}'.format(heights))
|
||||
profile.setParams(**profileparams)
|
||||
obj.AreaParams = str(profile.getParams())
|
||||
|
||||
PathLog.debug("Contour with params: {}".format(profile.getParams()))
|
||||
sections = profile.makeSections(mode=0, project=True, heights=heights)
|
||||
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'] = 0
|
||||
else:
|
||||
params['orientation'] = 1
|
||||
|
||||
if self.endVector is not None:
|
||||
params['start'] = self.endVector
|
||||
elif obj.UseStartPoint:
|
||||
params['start'] = obj.StartPoint
|
||||
|
||||
obj.PathParams = str({key: value for key, value in params.items() if key != 'shapes'})
|
||||
|
||||
(pp, end_vector) = Path.fromShapes(**params)
|
||||
PathLog.debug('pp: {}, end vector: {}'.format(pp, end_vector))
|
||||
self.endVector = end_vector
|
||||
|
||||
simobj = None
|
||||
if getsim:
|
||||
profileparams['Thicken'] = True
|
||||
profileparams['ToolRadius'] = self.radius - self.radius * .005
|
||||
profile.setParams(**profileparams)
|
||||
sec = profile.makeSections(mode=0, project=False, heights=heights)[-1].getShape()
|
||||
simobj = sec.extrude(FreeCAD.Vector(0, 0, baseobject.BoundBox.ZMax))
|
||||
|
||||
return pp, simobj
|
||||
|
||||
def execute(self, obj, getsim=False):
|
||||
PathLog.track()
|
||||
self.endVector = None
|
||||
|
||||
if not obj.Active:
|
||||
path = Path.Path("(inactive operation)")
|
||||
obj.Path = path
|
||||
obj.ViewObject.Visibility = False
|
||||
return
|
||||
|
||||
commandlist = []
|
||||
toolLoad = obj.ToolController
|
||||
|
||||
self.depthparams = depth_params(
|
||||
clearance_height=obj.ClearanceHeight.Value,
|
||||
safe_height=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)
|
||||
|
||||
if toolLoad is None or toolLoad.ToolNumber == 0:
|
||||
|
||||
FreeCAD.Console.PrintError("No Tool Controller is selected. We need a tool to build a Path.")
|
||||
return
|
||||
else:
|
||||
self.vertFeed = toolLoad.VertFeed.Value
|
||||
self.horizFeed = toolLoad.HorizFeed.Value
|
||||
self.vertRapid = toolLoad.VertRapid.Value
|
||||
self.horizRapid = toolLoad.HorizRapid.Value
|
||||
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
|
||||
else:
|
||||
self.radius = tool.Diameter/2
|
||||
|
||||
commandlist.append(Path.Command("(" + obj.Label + ")"))
|
||||
|
||||
def opShape(self, obj, commandlist):
|
||||
if obj.UseComp:
|
||||
commandlist.append(Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")"))
|
||||
else:
|
||||
commandlist.append(Path.Command("(Uncompensated Tool Path)"))
|
||||
|
||||
parentJob = PathUtils.findParentJob(obj)
|
||||
job = PathUtils.findParentJob(obj)
|
||||
|
||||
if parentJob is None:
|
||||
if job is None:
|
||||
return
|
||||
baseobject = parentJob.Base
|
||||
baseobject = job.Base
|
||||
if baseobject is None:
|
||||
return
|
||||
|
||||
@@ -268,31 +127,33 @@ class ObjectContour:
|
||||
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:
|
||||
(pp, sim) = self._buildPathArea(obj, contourshape, start=obj.StartPoint, getsim=getsim)
|
||||
commandlist.extend(pp.Commands)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(e)
|
||||
FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
|
||||
return f.extrude(FreeCAD.Vector(0, 0, thickness))
|
||||
|
||||
if hasattr(baseobject, "Shape") and not isPanel:
|
||||
env = PathUtils.getEnvelope(partshape=baseobject.Shape, subshape=None, depthparams=self.depthparams)
|
||||
try:
|
||||
(pp, sim) = self._buildPathArea(obj, env, start=obj.StartPoint, getsim=getsim)
|
||||
commandlist.extend(pp.Commands)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(e)
|
||||
FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
|
||||
return PathUtils.getEnvelope(partshape=baseobject.Shape, subshape=None, depthparams=self.depthparams)
|
||||
|
||||
# Let's finish by rapid to clearance...just for safety
|
||||
commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value}))
|
||||
def opAreaParams(self, obj):
|
||||
params = {'Fill': 0, 'Coplanar': 2}
|
||||
|
||||
PathLog.track()
|
||||
path = Path.Path(commandlist)
|
||||
obj.Path = path
|
||||
return sim
|
||||
if obj.UseComp is False:
|
||||
params['Offset'] = 0.0
|
||||
else:
|
||||
params['Offset'] = self.radius+obj.OffsetExtra.Value
|
||||
|
||||
jointype = ['Round', 'Square', 'Miter']
|
||||
params['JoinType'] = jointype.index(obj.JoinType)
|
||||
|
||||
if obj.JoinType == 'Miter':
|
||||
params['MiterLimit'] = obj.MiterLimit
|
||||
return params
|
||||
|
||||
def opPathParams(self, obj):
|
||||
params = {}
|
||||
if obj.Direction == 'CCW':
|
||||
params['orientation'] = 0
|
||||
else:
|
||||
params['orientation'] = 1
|
||||
return params
|
||||
|
||||
class _ViewProviderContour:
|
||||
|
||||
@@ -349,6 +210,16 @@ class _CommandSetStartPoint:
|
||||
def Activated(self):
|
||||
FreeCADGui.Snapper.getPoint(callback=self.setpoint)
|
||||
|
||||
def Create(name):
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Path", "Create a Contour"))
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
proxy = ObjectContour(obj)
|
||||
proxy.setDefaultValues(obj)
|
||||
|
||||
obj.ViewObject.Proxy.deleteOnReject = True
|
||||
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
obj.ViewObject.startEditing()
|
||||
|
||||
class CommandPathContour:
|
||||
def GetResources(self):
|
||||
@@ -365,36 +236,7 @@ class CommandPathContour:
|
||||
return False
|
||||
|
||||
def Activated(self):
|
||||
ztop = 10.0
|
||||
zbottom = 0.0
|
||||
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Path", "Create a Contour"))
|
||||
FreeCADGui.addModule("PathScripts.PathContour")
|
||||
FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Contour")')
|
||||
FreeCADGui.doCommand('PathScripts.PathContour.ObjectContour(obj)')
|
||||
FreeCADGui.doCommand('obj.ViewObject.Proxy.deleteOnReject = True')
|
||||
|
||||
FreeCADGui.doCommand('obj.Active = True')
|
||||
|
||||
FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(ztop + 10.0))
|
||||
FreeCADGui.doCommand('obj.StepDown = 1.0')
|
||||
FreeCADGui.doCommand('obj.StartDepth= ' + str(ztop))
|
||||
FreeCADGui.doCommand('obj.FinalDepth=' + str(zbottom))
|
||||
|
||||
FreeCADGui.doCommand('obj.SafeHeight = ' + str(ztop + 2.0))
|
||||
FreeCADGui.doCommand('obj.OffsetExtra = 0.0')
|
||||
FreeCADGui.doCommand('obj.Direction = "CW"')
|
||||
FreeCADGui.doCommand('obj.UseComp = True')
|
||||
FreeCADGui.doCommand('obj.JoinType = "Round"')
|
||||
FreeCADGui.doCommand('obj.MiterLimit =' + str(0.1))
|
||||
|
||||
FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
|
||||
FreeCADGui.doCommand('PathScripts.PathContour.ObjectContour.setDepths(obj.Proxy, obj)')
|
||||
FreeCADGui.doCommand('obj.ToolController = PathScripts.PathUtils.findToolController(obj)')
|
||||
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCADGui.doCommand('obj.ViewObject.startEditing()')
|
||||
|
||||
return Create("Contour")
|
||||
|
||||
class TaskPanel:
|
||||
def __init__(self, obj, deleteOnReject):
|
||||
|
||||
Reference in New Issue
Block a user