Commit f481228e authored by sumpfralle's avatar sumpfralle

added spiral path pattern

simplified value handling in ControlsGTK.InputChoice and InputTable


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@1177 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent 7cc0e33a
...@@ -64,11 +64,6 @@ class InputBaseClass(object): ...@@ -64,11 +64,6 @@ class InputBaseClass(object):
self._input_converter = set_conv self._input_converter = set_conv
self._output_converter = get_conv self._output_converter = get_conv
@_input_conversion
def _get_input_conversion_result(self, value):
# a simple dummy replicating the behaviour of _input_conversion
return value
class InputNumber(InputBaseClass): class InputNumber(InputBaseClass):
...@@ -93,14 +88,14 @@ class InputNumber(InputBaseClass): ...@@ -93,14 +88,14 @@ class InputNumber(InputBaseClass):
class InputChoice(InputBaseClass): class InputChoice(InputBaseClass):
def __init__(self, choices, force_type=None, change_handler=None): def __init__(self, choices, change_handler=None):
import gtk import gtk
import gobject import gobject
g_type = self._get_column_type(choices, force_type=force_type) self.model = gtk.ListStore(gobject.TYPE_STRING)
self.model = gtk.ListStore(gobject.TYPE_STRING, g_type) self._values = []
for label, value in choices: for label, value in choices:
self.model.append((label, self.model.append((label, ))
self._get_input_conversion_result(value))) self._values.append(value)
renderer = gtk.CellRendererText() renderer = gtk.CellRendererText()
self.control = gtk.ComboBox(self.model) self.control = gtk.ComboBox(self.model)
self.control.pack_start(renderer) self.control.pack_start(renderer)
...@@ -109,63 +104,37 @@ class InputChoice(InputBaseClass): ...@@ -109,63 +104,37 @@ class InputChoice(InputBaseClass):
if change_handler: if change_handler:
self.control.connect("changed", change_handler) self.control.connect("changed", change_handler)
def _get_column_type(self, choices, force_type=None):
import gobject
type_mapper = {int: gobject.TYPE_INT,
long: gobject.TYPE_INT64,
basestring: gobject.TYPE_STRING,
bool: gobject.TYPE_BOOLEAN
}
if force_type is None:
value_sample = choices[0][1]
for key, g_type in type_mapper.iteritems():
if isinstance(value_sample, key):
break
else:
raise TypeError("Invalid sample type give: %s - %s" % \
(sample_value, type(sample_value)))
elif force_type in type_mapper:
g_type = type_mapper[force_type]
else:
raise TypeError("Invalid type forced: %s" % str(force_type))
return g_type
@_output_conversion
def get_value(self): def get_value(self):
index = self.control.get_active() index = self.control.get_active()
if index < 0: if index < 0:
return None return None
else: else:
return self.model[index][1] return self._values[index]
@_input_conversion
def set_value(self, value): def set_value(self, value):
if value is None: if value is None:
self.control.set_active(-1) self.control.set_active(-1)
else: else:
for index, row in enumerate(self.model): if value in self._values:
if row[1] == value: self.control.set_active(self._values.index(value))
self.control.set_active(index)
break
else: else:
_log.debug("Unknown value: %s" % str(value)) _log.debug("Unknown value: %s" % str(value))
def update_choices(self, choices): def update_choices(self, choices):
# TODO: selection restore does not work currently; there seems to be a glitch during "delete model"
selected = self.get_value() selected = self.get_value()
for choice_index, (label, value) in enumerate(choices): for choice_index, (label, value) in enumerate(choices):
for index, row in enumerate(self.model): if not value in self._values:
if row[1] == value:
break
else:
# this choice is new # this choice is new
self.model.insert(choice_index, (label, self.model.insert(choice_index, (label, ))
self._get_input_conversion_result(value))) self._values.insert(choice_index, value)
continue continue
index = self._values.index(value)
row = self.model[index]
# the current choice is preceded by some obsolete items # the current choice is preceded by some obsolete items
while index > choice_index: while index > choice_index:
m_iter = self.model.get_iter((index,)) m_iter = self.model.get_iter((index,))
self.model.remove(m_iter) self.model.remove(m_iter)
self._values.pop(index)
index -= 1 index -= 1
# update the label column # update the label column
row[0] = label row[0] = label
...@@ -173,19 +142,20 @@ class InputChoice(InputBaseClass): ...@@ -173,19 +142,20 @@ class InputChoice(InputBaseClass):
while len(self.model) > len(choices): while len(self.model) > len(choices):
m_iter = self.model.get_iter((len(choices),)) m_iter = self.model.get_iter((len(choices),))
self.model.remove(m_iter) self.model.remove(m_iter)
self._values.pop(-1)
self.set_value(selected) self.set_value(selected)
class InputTable(InputChoice): class InputTable(InputChoice):
def __init__(self, choices, force_type=None, change_handler=None): def __init__(self, choices, change_handler=None):
import gtk import gtk
import gobject import gobject
g_type = self._get_column_type(choices, force_type=force_type) self.model = gtk.ListStore(gobject.TYPE_STRING)
self.model = gtk.ListStore(gobject.TYPE_STRING, g_type) self._values = []
for label, value in choices: for label, value in choices:
self.model.append((label, self.model.append((label,))
self._get_input_conversion_result(value))) self._values.append(value)
renderer = gtk.CellRendererText() renderer = gtk.CellRendererText()
self.control = gtk.ScrolledWindow() self.control = gtk.ScrolledWindow()
self._treeview = gtk.TreeView(self.model) self._treeview = gtk.TreeView(self.model)
...@@ -203,25 +173,19 @@ class InputTable(InputChoice): ...@@ -203,25 +173,19 @@ class InputTable(InputChoice):
if change_handler: if change_handler:
self._selection.connect("changed", change_handler) self._selection.connect("changed", change_handler)
def _get_input_conversion_result(self, value):
# handle a list instead of single items
return super(InputTable, self)._get_input_conversion_result([value])[0]
@_output_conversion
def get_value(self): def get_value(self):
model, rows = self._selection.get_selected_rows() model, rows = self._selection.get_selected_rows()
return [self.model[path[0]][1] for path in rows] return [self._values[path[0]] for path in rows]
@_input_conversion
def set_value(self, items): def set_value(self, items):
self._selection.unselect_all() selection = self._selection
if items is None: if items is None:
items = [] items = []
for item in items: for index, value in enumerate(self._values):
for index, row in enumerate(self.model): if value in items:
if row[1] == item: selection.select_path((index, ))
self._selection.select_path((index, )) else:
break selection.unselect_path((index, ))
class InputCheckBox(InputBaseClass): class InputCheckBox(InputBaseClass):
......
...@@ -126,6 +126,25 @@ class PathParamGridDirection(pycam.Plugins.PluginBase): ...@@ -126,6 +126,25 @@ class PathParamGridDirection(pycam.Plugins.PluginBase):
"grid_direction") "grid_direction")
class PathParamPattern(pycam.Plugins.PluginBase):
DEPENDS = ["Processes"]
CATEGORIES = ["Process", "Parameter"]
def setup(self):
widget = pycam.Gui.ControlsGTK.InputChoice(
(("grid", "grid"), ("spiral", "spiral")),
change_handler=lambda widget=None: self.core.emit_event(
"process-changed"))
self.core.get("register_parameter")("process", "pathgenerator",
"path_pattern", "Pattern", widget, weight=5)
return True
def teardown(self):
self.core.get("unregister_parameter")("process", "pathgenerator",
"path_pattern")
class PathParamRadiusCompensation(pycam.Plugins.PluginBase): class PathParamRadiusCompensation(pycam.Plugins.PluginBase):
DEPENDS = ["Processes"] DEPENDS = ["Processes"]
...@@ -151,7 +170,7 @@ class PathParamTraceModel(pycam.Plugins.PluginBase): ...@@ -151,7 +170,7 @@ class PathParamTraceModel(pycam.Plugins.PluginBase):
def setup(self): def setup(self):
self.input_control = pycam.Gui.ControlsGTK.InputTable([], self.input_control = pycam.Gui.ControlsGTK.InputTable([],
force_type=long, change_handler=lambda widget=None: \ change_handler=lambda widget=None: \
self.core.emit_event("process-changed")) self.core.emit_event("process-changed"))
# configure the input/output converter # configure the input/output converter
def get_converter(model_refs): def get_converter(model_refs):
......
...@@ -31,7 +31,8 @@ class ProcessStrategySlicing(pycam.Plugins.PluginBase): ...@@ -31,7 +31,8 @@ class ProcessStrategySlicing(pycam.Plugins.PluginBase):
DEPENDS = ["ParameterGroupManager", "PathParamOverlap", DEPENDS = ["ParameterGroupManager", "PathParamOverlap",
"PathParamStepDown", "PathParamMaterialAllowance", "PathParamStepDown", "PathParamMaterialAllowance",
"PathParamMillingStyle", "PathParamGridDirection"] "PathParamMillingStyle", "PathParamGridDirection",
"PathParamPattern"]
CATEGORIES = ["Process"] CATEGORIES = ["Process"]
def setup(self): def setup(self):
...@@ -40,6 +41,7 @@ class ProcessStrategySlicing(pycam.Plugins.PluginBase): ...@@ -40,6 +41,7 @@ class ProcessStrategySlicing(pycam.Plugins.PluginBase):
"material_allowance": 0, "material_allowance": 0,
"milling_style": pycam.Toolpath.MotionGrid.MILLING_STYLE_IGNORE, "milling_style": pycam.Toolpath.MotionGrid.MILLING_STYLE_IGNORE,
"grid_direction": pycam.Toolpath.MotionGrid.GRID_DIRECTION_X, "grid_direction": pycam.Toolpath.MotionGrid.GRID_DIRECTION_X,
"path_pattern": "grid",
} }
self.core.get("register_parameter_set")("process", "slicing", self.core.get("register_parameter_set")("process", "slicing",
"Slice removal", self.run_process, parameters=parameters, "Slice removal", self.run_process, parameters=parameters,
...@@ -58,7 +60,9 @@ class ProcessStrategySlicing(pycam.Plugins.PluginBase): ...@@ -58,7 +60,9 @@ class ProcessStrategySlicing(pycam.Plugins.PluginBase):
(1.0 - process["parameters"]["overlap"]) (1.0 - process["parameters"]["overlap"])
path_generator = pycam.PathGenerators.PushCutter.PushCutter( path_generator = pycam.PathGenerators.PushCutter.PushCutter(
pycam.PathProcessors.PathAccumulator.PathAccumulator()) pycam.PathProcessors.PathAccumulator.PathAccumulator())
motion_grid = pycam.Toolpath.MotionGrid.get_fixed_grid( grid_func = {"grid": pycam.Toolpath.MotionGrid.get_fixed_grid,
"spiral": pycam.Toolpath.MotionGrid.get_spiral}
motion_grid = grid_func[process["parameters"]["path_pattern"]](
(low, high), process["parameters"]["step_down"], (low, high), process["parameters"]["step_down"],
line_distance=line_distance, line_distance=line_distance,
grid_direction=process["parameters"]["grid_direction"], grid_direction=process["parameters"]["grid_direction"],
......
...@@ -32,21 +32,8 @@ class TaskParamCollisionModels(pycam.Plugins.PluginBase): ...@@ -32,21 +32,8 @@ class TaskParamCollisionModels(pycam.Plugins.PluginBase):
def setup(self): def setup(self):
self.input_control = pycam.Gui.ControlsGTK.InputTable([], self.input_control = pycam.Gui.ControlsGTK.InputTable([],
force_type=long, change_handler=lambda widget=None: \ change_handler=lambda widget=None: \
self.core.emit_event("task-changed")) self.core.emit_event("task-changed"))
# configure the input/output converter
def get_converter(model_refs):
models_dict = {}
for model in self.core.get("models"):
models_dict[id(model)] = model
models = []
for model_ref in model_refs:
models.append(models_dict[model_ref])
return models
def set_converter(models):
return [id(model) for model in models]
self.input_control.set_conversion(set_conv=set_converter,
get_conv=get_converter)
self.input_control.get_widget().set_size_request(240, -1) self.input_control.get_widget().set_size_request(240, -1)
self.core.get("register_parameter")("task", "models", self.core.get("register_parameter")("task", "models",
"collision_models", "", self.input_control, "collision_models", "", self.input_control,
...@@ -75,11 +62,8 @@ class TaskParamTool(pycam.Plugins.PluginBase): ...@@ -75,11 +62,8 @@ class TaskParamTool(pycam.Plugins.PluginBase):
def setup(self): def setup(self):
self.input_control = pycam.Gui.ControlsGTK.InputChoice([], self.input_control = pycam.Gui.ControlsGTK.InputChoice([],
force_type=long, change_handler=lambda widget=None: \ change_handler=lambda widget=None: \
self.core.emit_event("task-changed")) self.core.emit_event("task-changed"))
self.input_control.set_conversion(
get_conv=lambda ref: ([tool for tool in self.core.get("tools") if id(tool) == ref] + [None])[0],
set_conv=lambda tool: id(tool))
self.core.get("register_parameter")("task", "components", "tool", self.core.get("register_parameter")("task", "components", "tool",
"Tool", self.input_control, weight=10) "Tool", self.input_control, weight=10)
self.core.register_event("tool-list-changed", self._update_tools) self.core.register_event("tool-list-changed", self._update_tools)
...@@ -104,11 +88,8 @@ class TaskParamProcess(pycam.Plugins.PluginBase): ...@@ -104,11 +88,8 @@ class TaskParamProcess(pycam.Plugins.PluginBase):
def setup(self): def setup(self):
self.input_control = pycam.Gui.ControlsGTK.InputChoice([], self.input_control = pycam.Gui.ControlsGTK.InputChoice([],
force_type=long, change_handler=lambda widget=None: \ change_handler=lambda widget=None: \
self.core.emit_event("task-changed")) self.core.emit_event("task-changed"))
self.input_control.set_conversion(
get_conv=lambda ref: ([process for process in self.core.get("processes") if id(process) == ref] + [None])[0],
set_conv=lambda process: id(process))
self.core.get("register_parameter")("task", "components", "process", self.core.get("register_parameter")("task", "components", "process",
"Process", self.input_control, weight=20) "Process", self.input_control, weight=20)
self.core.register_event("process-list-changed", self._update_processes) self.core.register_event("process-list-changed", self._update_processes)
...@@ -133,11 +114,8 @@ class TaskParamBounds(pycam.Plugins.PluginBase): ...@@ -133,11 +114,8 @@ class TaskParamBounds(pycam.Plugins.PluginBase):
def setup(self): def setup(self):
self.input_control = pycam.Gui.ControlsGTK.InputChoice([], self.input_control = pycam.Gui.ControlsGTK.InputChoice([],
force_type=long, change_handler=lambda widget=None: \ change_handler=lambda widget=None: \
self.core.emit_event("task-changed")) self.core.emit_event("task-changed"))
self.input_control.set_conversion(
get_conv=lambda ref: ([bounds for bounds in self.core.get("bounds") if id(bounds) == ref] + [None])[0],
set_conv=lambda bounds: id(bounds))
self.core.get("register_parameter")("task", "components", "bounds", self.core.get("register_parameter")("task", "components", "bounds",
"Bounds", self.input_control, weight=30) "Bounds", self.input_control, weight=30)
self.core.register_event("bounds-list-changed", self._update_bounds) self.core.register_event("bounds-list-changed", self._update_bounds)
......
...@@ -48,7 +48,7 @@ class ToolpathCrop(pycam.Plugins.PluginBase): ...@@ -48,7 +48,7 @@ class ToolpathCrop(pycam.Plugins.PluginBase):
"clicked", self.crop_toolpath)) "clicked", self.crop_toolpath))
# model selector # model selector
self.models_widget = pycam.Gui.ControlsGTK.InputTable([], self.models_widget = pycam.Gui.ControlsGTK.InputTable([],
force_type=long, change_handler=self._update_widgets) change_handler=self._update_widgets)
# configure the input/output converter # configure the input/output converter
def get_converter(model_refs): def get_converter(model_refs):
models_dict = {} models_dict = {}
......
...@@ -158,9 +158,9 @@ def get_fixed_grid_layer(minx, maxx, miny, maxy, z, line_distance, ...@@ -158,9 +158,9 @@ def get_fixed_grid_layer(minx, maxx, miny, maxy, z, line_distance,
return result, end_position return result, end_position
return get_lines(start, end, end_position) return get_lines(start, end, end_position)
def get_fixed_grid((low, high), layer_distance, line_distance=None, step_width=None, def get_fixed_grid((low, high), layer_distance, line_distance=None,
grid_direction=GRID_DIRECTION_X, milling_style=MILLING_STYLE_IGNORE, step_width=None, grid_direction=GRID_DIRECTION_X,
start_position=START_Z): milling_style=MILLING_STYLE_IGNORE, start_position=START_Z):
""" Calculate the grid positions for toolpath moves """ Calculate the grid positions for toolpath moves
""" """
if isiterable(layer_distance): if isiterable(layer_distance):
...@@ -185,6 +185,99 @@ def get_fixed_grid((low, high), layer_distance, line_distance=None, step_width=N ...@@ -185,6 +185,99 @@ def get_fixed_grid((low, high), layer_distance, line_distance=None, step_width=N
start_position=start_position) start_position=start_position)
yield result yield result
def _get_position(minx, maxx, miny, maxy, z, position):
if position & START_X > 0:
x = minx
else:
x = maxx
if position & START_Y > 0:
y = miny
else:
y = maxy
return Point(x, y, z)
def get_spiral_layer_lines(minx, maxx, miny, maxy, z, line_distance_x,
line_distance_y, grid_direction, start_position,
current_location):
xor_map = {GRID_DIRECTION_X: START_X, GRID_DIRECTION_Y: START_Y}
end_position = start_position ^ xor_map[grid_direction]
end_location = _get_position(minx, maxx, miny, maxy, z, end_position)
if grid_direction == GRID_DIRECTION_X:
next_grid_direction = GRID_DIRECTION_Y
if start_position & START_Y > 0:
miny += line_distance_y
else:
maxy -= line_distance_y
else:
next_grid_direction = GRID_DIRECTION_X
if start_position & START_X > 0:
minx += line_distance_x
else:
maxx -= line_distance_x
lines = [(current_location, end_location)]
if (minx - epsilon <= maxx ) and (miny - epsilon <= maxy):
# recursively compute the next lines
lines.extend(get_spiral_layer_lines(minx, maxx, miny, maxy, z,
line_distance_x, line_distance_y, next_grid_direction,
end_position, end_location))
return lines
def get_spiral_layer(minx, maxx, miny, maxy, z, line_distance, step_width,
grid_direction, start_position=START_Z):
current_location = _get_position(minx, maxx, miny, maxy, z,
start_position)
if line_distance > 0:
line_steps_x = math.ceil((float(maxx - minx) / line_distance))
line_steps_y = math.ceil((float(maxy - miny) / line_distance))
line_distance_x = (maxx - minx) / line_steps_x
line_distance_y = (maxy - miny) / line_steps_y
lines = get_spiral_layer_lines(minx, maxx, miny, maxy, z,
line_distance_x, line_distance_y, grid_direction,
start_position, current_location)
# turn the lines into steps
for start, end in lines:
points = []
if step_width is None:
points.append(start)
points.append(end)
else:
line = Line(start, end)
if isiterable(step_width):
steps = step_width
else:
steps = floatrange(0.0, line.len, inc=step_width)
for step in steps:
next_point = line.p1.add(line.dir.mul(step))
points.append(next_point)
yield points
def get_spiral((low, high), layer_distance, line_distance=None,
step_width=None, grid_direction=GRID_DIRECTION_X,
milling_style=MILLING_STYLE_IGNORE, start_position=START_Z):
""" Calculate the grid positions for toolpath moves
"""
if isiterable(layer_distance):
layers = layer_distance
elif layer_distance is None:
# useful for DropCutter
layers = [low[2]]
else:
layers = floatrange(low[2], high[2], inc=layer_distance,
reverse=bool(start_position & START_Z))
# only X _or_ Y are allowed
if grid_direction != GRID_DIRECTION_X:
grid_direction = GRID_DIRECTION_Y
if ((milling_style == MILLING_STYLE_CLIMB),
(grid_direction == GRID_DIRECTION_X),
(start_position & START_X > 0)).count(True) % 2 == 0:
start_direction = GRID_DIRECTION_X
else:
start_direction = GRID_DIRECTION_Y
for z in layers:
yield get_spiral_layer(low[0], high[0], low[1], high[1], z,
line_distance, step_width=step_width,
grid_direction=start_direction, start_position=start_position)
def get_lines_layer(lines, z, last_z=None, step_width=None, def get_lines_layer(lines, z, last_z=None, step_width=None,
milling_style=MILLING_STYLE_CONVENTIONAL): milling_style=MILLING_STYLE_CONVENTIONAL):
get_proj_point = lambda proj_point: Point(proj_point.x, proj_point.y, z) get_proj_point = lambda proj_point: Point(proj_point.x, proj_point.y, z)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment