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):
self._input_converter = set_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):
......@@ -93,14 +88,14 @@ class InputNumber(InputBaseClass):
class InputChoice(InputBaseClass):
def __init__(self, choices, force_type=None, change_handler=None):
def __init__(self, choices, change_handler=None):
import gtk
import gobject
g_type = self._get_column_type(choices, force_type=force_type)
self.model = gtk.ListStore(gobject.TYPE_STRING, g_type)
self.model = gtk.ListStore(gobject.TYPE_STRING)
self._values = []
for label, value in choices:
self.model.append((label,
self._get_input_conversion_result(value)))
self.model.append((label, ))
self._values.append(value)
renderer = gtk.CellRendererText()
self.control = gtk.ComboBox(self.model)
self.control.pack_start(renderer)
......@@ -109,63 +104,37 @@ class InputChoice(InputBaseClass):
if 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):
index = self.control.get_active()
if index < 0:
return None
else:
return self.model[index][1]
return self._values[index]
@_input_conversion
def set_value(self, value):
if value is None:
self.control.set_active(-1)
else:
for index, row in enumerate(self.model):
if row[1] == value:
self.control.set_active(index)
break
if value in self._values:
self.control.set_active(self._values.index(value))
else:
_log.debug("Unknown value: %s" % str(value))
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()
for choice_index, (label, value) in enumerate(choices):
for index, row in enumerate(self.model):
if row[1] == value:
break
else:
if not value in self._values:
# this choice is new
self.model.insert(choice_index, (label,
self._get_input_conversion_result(value)))
self.model.insert(choice_index, (label, ))
self._values.insert(choice_index, value)
continue
index = self._values.index(value)
row = self.model[index]
# the current choice is preceded by some obsolete items
while index > choice_index:
m_iter = self.model.get_iter((index,))
self.model.remove(m_iter)
self._values.pop(index)
index -= 1
# update the label column
row[0] = label
......@@ -173,19 +142,20 @@ class InputChoice(InputBaseClass):
while len(self.model) > len(choices):
m_iter = self.model.get_iter((len(choices),))
self.model.remove(m_iter)
self._values.pop(-1)
self.set_value(selected)
class InputTable(InputChoice):
def __init__(self, choices, force_type=None, change_handler=None):
def __init__(self, choices, change_handler=None):
import gtk
import gobject
g_type = self._get_column_type(choices, force_type=force_type)
self.model = gtk.ListStore(gobject.TYPE_STRING, g_type)
self.model = gtk.ListStore(gobject.TYPE_STRING)
self._values = []
for label, value in choices:
self.model.append((label,
self._get_input_conversion_result(value)))
self.model.append((label,))
self._values.append(value)
renderer = gtk.CellRendererText()
self.control = gtk.ScrolledWindow()
self._treeview = gtk.TreeView(self.model)
......@@ -203,25 +173,19 @@ class InputTable(InputChoice):
if 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):
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):
self._selection.unselect_all()
selection = self._selection
if items is None:
items = []
for item in items:
for index, row in enumerate(self.model):
if row[1] == item:
self._selection.select_path((index, ))
break
for index, value in enumerate(self._values):
if value in items:
selection.select_path((index, ))
else:
selection.unselect_path((index, ))
class InputCheckBox(InputBaseClass):
......
......@@ -126,6 +126,25 @@ class PathParamGridDirection(pycam.Plugins.PluginBase):
"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):
DEPENDS = ["Processes"]
......@@ -151,7 +170,7 @@ class PathParamTraceModel(pycam.Plugins.PluginBase):
def setup(self):
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"))
# configure the input/output converter
def get_converter(model_refs):
......
......@@ -31,7 +31,8 @@ class ProcessStrategySlicing(pycam.Plugins.PluginBase):
DEPENDS = ["ParameterGroupManager", "PathParamOverlap",
"PathParamStepDown", "PathParamMaterialAllowance",
"PathParamMillingStyle", "PathParamGridDirection"]
"PathParamMillingStyle", "PathParamGridDirection",
"PathParamPattern"]
CATEGORIES = ["Process"]
def setup(self):
......@@ -40,6 +41,7 @@ class ProcessStrategySlicing(pycam.Plugins.PluginBase):
"material_allowance": 0,
"milling_style": pycam.Toolpath.MotionGrid.MILLING_STYLE_IGNORE,
"grid_direction": pycam.Toolpath.MotionGrid.GRID_DIRECTION_X,
"path_pattern": "grid",
}
self.core.get("register_parameter_set")("process", "slicing",
"Slice removal", self.run_process, parameters=parameters,
......@@ -58,7 +60,9 @@ class ProcessStrategySlicing(pycam.Plugins.PluginBase):
(1.0 - process["parameters"]["overlap"])
path_generator = pycam.PathGenerators.PushCutter.PushCutter(
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"],
line_distance=line_distance,
grid_direction=process["parameters"]["grid_direction"],
......
......@@ -32,21 +32,8 @@ class TaskParamCollisionModels(pycam.Plugins.PluginBase):
def setup(self):
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"))
# 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.core.get("register_parameter")("task", "models",
"collision_models", "", self.input_control,
......@@ -75,11 +62,8 @@ class TaskParamTool(pycam.Plugins.PluginBase):
def setup(self):
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.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",
"Tool", self.input_control, weight=10)
self.core.register_event("tool-list-changed", self._update_tools)
......@@ -104,11 +88,8 @@ class TaskParamProcess(pycam.Plugins.PluginBase):
def setup(self):
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.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",
"Process", self.input_control, weight=20)
self.core.register_event("process-list-changed", self._update_processes)
......@@ -133,11 +114,8 @@ class TaskParamBounds(pycam.Plugins.PluginBase):
def setup(self):
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.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",
"Bounds", self.input_control, weight=30)
self.core.register_event("bounds-list-changed", self._update_bounds)
......
......@@ -48,7 +48,7 @@ class ToolpathCrop(pycam.Plugins.PluginBase):
"clicked", self.crop_toolpath))
# model selector
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
def get_converter(model_refs):
models_dict = {}
......
......@@ -158,9 +158,9 @@ def get_fixed_grid_layer(minx, maxx, miny, maxy, z, line_distance,
return result, end_position
return get_lines(start, end, end_position)
def get_fixed_grid((low, high), layer_distance, line_distance=None, step_width=None,
grid_direction=GRID_DIRECTION_X, milling_style=MILLING_STYLE_IGNORE,
start_position=START_Z):
def get_fixed_grid((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):
......@@ -185,6 +185,99 @@ def get_fixed_grid((low, high), layer_distance, line_distance=None, step_width=N
start_position=start_position)
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,
milling_style=MILLING_STYLE_CONVENTIONAL):
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