# -*- coding: utf-8 -*-
"""
$Id$
Copyright 2011 Lars Kruse <devel@sumpfralle.de>
This file is part of PyCAM.
PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PyCAM 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
# gtk is imported later (on demand)
#import gtk
import pycam.Plugins
import pycam.Geometry.Model
class ModelSupportGrid(pycam.Plugins.PluginBase):
UI_FILE = "model_support_grid.ui"
DEPENDS = ["Models", "ModelSupport"]
CATEGORIES = ["Model", "Support bridges"]
def setup(self):
if self.gui:
import gtk
grid_box = self.gui.get_object("SupportModelGridBox")
grid_box.unparent()
self.core.register_ui("support_model_type_selector", "Grid",
"grid", weight=-10)
self.core.register_ui("support_model_settings", "Grid settings",
grid_box)
support_model_changed = lambda widget=None: self.core.emit_event(
"support-model-changed")
self._gtk_handlers = []
# support grid
self.grid_adjustments_x = []
self.grid_adjustments_y = []
self.grid_adjustment_axis_x_last = True
self._block_manual_adjust_update = False
grid_distance_x = self.gui.get_object("SupportGridDistanceX")
self._gtk_handlers.append((grid_distance_x, "value-changed",
support_model_changed))
self.core.add_item("support_grid_distance_x",
grid_distance_x.get_value, grid_distance_x.set_value)
grid_distance_square = self.gui.get_object("SupportGridDistanceSquare")
self._gtk_handlers.append((grid_distance_square, "clicked",
self.update_support_controls))
grid_distance_y = self.gui.get_object("SupportGridDistanceY")
self._gtk_handlers.append((grid_distance_y, "value-changed",
support_model_changed))
def get_support_grid_distance_y():
if grid_distance_square.get_active():
return self.core.get("support_grid_distance_x")
else:
return grid_distance_y.get_value()
self.core.add_item("support_grid_distance_y",
get_support_grid_distance_y, grid_distance_y.set_value)
grid_offset_x = self.gui.get_object("SupportGridOffsetX")
self._gtk_handlers.append((grid_offset_x, "value-changed",
support_model_changed))
self.core.add_item("support_grid_offset_x",
grid_offset_x.get_value, grid_offset_x.set_value)
grid_offset_y = self.gui.get_object("SupportGridOffsetY")
self._gtk_handlers.append((grid_offset_y, "value-changed",
support_model_changed))
self.core.add_item("support_grid_offset_y",
grid_offset_y.get_value, grid_offset_y.set_value)
# manual grid adjustments
self.grid_adjustment_axis_x = self.gui.get_object(
"SupportGridPositionManualAxisX")
self._gtk_handlers.extend((
(self.grid_adjustment_axis_x, "toggled",
self.switch_support_grid_manual_selector),
(self.gui.get_object("SupportGridPositionManualResetOne"),
"clicked", lambda *args: \
self.reset_support_grid_manual(reset_all=False)),
(self.gui.get_object("SupportGridPositionManualResetAll"),
"clicked", lambda *args: \
self.reset_support_grid_manual(True))))
self.grid_adjustment_model = self.gui.get_object(
"SupportGridPositionManualList")
self.grid_adjustment_selector = self.gui.get_object(
"SupportGridPositionManualSelector")
self._gtk_handlers.append((self.grid_adjustment_selector,
"changed", self.switch_support_grid_manual_selector))
self.grid_adjustment_value = self.gui.get_object(
"SupportGridPositionManualAdjustment")
self.grid_adjustment_value_control = self.gui.get_object(
"SupportGridPositionManualShiftControl")
self.grid_adjustment_value_control.set_update_policy(
gtk.UPDATE_DISCONTINUOUS)
self._gtk_handlers.extend((
(self.grid_adjustment_value_control, "move-slider",
self.update_support_grid_manual_adjust),
(self.grid_adjustment_value_control, "value-changed",
self.update_support_grid_manual_adjust),
(self.gui.get_object("SupportGridPositionManualShiftControl2"),
"value-changed", self.update_support_grid_manual_adjust)))
def get_set_grid_adjustment_value(value=None):
if self.grid_adjustment_axis_x.get_active():
adjustments = self.grid_adjustments_x
else:
adjustments = self.grid_adjustments_y
index = self.grid_adjustment_selector.get_active()
if value is None:
if 0 <= index < len(adjustments):
return adjustments[index]
else:
return 0
else:
while len(adjustments) <= index:
adjustments.append(0)
adjustments[index] = value
# TODO: remove these public settings
self.core.add_item("support_grid_adjustment_value",
get_set_grid_adjustment_value,
get_set_grid_adjustment_value)
grid_distance_square.set_active(True)
self.core.set("support_grid_distance_x", 10.0)
self.core.register_chain("get_support_models",
self._get_support_models)
# handlers
self._event_handlers = (
("support-model-changed", self.update_support_controls), )
self.register_gtk_handlers(self._gtk_handlers)
self.register_event_handlers(self._event_handlers)
return True
def teardown(self):
if self.gui:
self.core.unregister_chain("get_support_models",
self._get_support_models)
self.core.unregister_ui("support_model_type_selector", "grid")
self.core.unregister_ui("support_model_settings",
self.gui.get_object("SupportModelGridBox"))
self.unregister_gtk_handlers(self._gtk_handlers)
self.unregister_event_handlers(self._event_handlers)
def _get_support_models(self, models, support_models):
grid_type = self.core.get("support_model_type")
if (grid_type == "grid") and models:
# we create exactly one support model for all input models
s = self.core
support_grid = None
low, high = self._get_bounds(models)
if (s.get("support_grid_thickness") > 0) \
and ((s.get("support_grid_distance_x") > 0) \
or (s.get("support_grid_distance_y") > 0)) \
and ((s.get("support_grid_distance_x") == 0) \
or (s.get("support_grid_distance_x") \
> s.get("support_grid_thickness"))) \
and ((s.get("support_grid_distance_y") == 0) \
or (s.get("support_grid_distance_y") \
> s.get("support_grid_thickness"))) \
and (s.get("support_grid_height") > 0):
support_grid = pycam.Toolpath.SupportGrid.get_support_grid(
low[0], high[0], low[1], high[1], low[2],
s.get("support_grid_distance_x"),
s.get("support_grid_distance_y"),
s.get("support_grid_thickness"),
s.get("support_grid_height"),
offset_x=s.get("support_grid_offset_x"),
offset_y=s.get("support_grid_offset_y"),
adjustments_x=self.grid_adjustments_x,
adjustments_y=self.grid_adjustments_y)
# all models are processed -> wipe the input list
while models:
models.pop()
support_models.append(support_grid)
def update_support_controls(self, widget=None):
grid_type = self.core.get("support_model_type")
if grid_type == "grid":
grid_square = self.gui.get_object("SupportGridDistanceSquare")
distance_y = self.gui.get_object("SupportGridDistanceYControl")
distance_y.set_sensitive(not grid_square.get_active())
if grid_square.get_active():
# We let "distance_y" track the value of "distance_x".
self.core.set("support_grid_distance_y",
self.core.get("support_grid_distance_x"))
self.update_support_grid_manual_model()
self.switch_support_grid_manual_selector()
self.gui.get_object("SupportModelGridBox").show()
else:
self.gui.get_object("SupportModelGridBox").hide()
def switch_support_grid_manual_selector(self, widget=None):
""" Event handler for a switch between the x and y axis selector for
manual adjustment. Final goal: update the adjustment combobox with the
current values for that axis.
"""
old_axis_was_x = self.grid_adjustment_axis_x_last
self.grid_adjustment_axis_x_last = \
self.grid_adjustment_axis_x.get_active()
if self.grid_adjustment_axis_x.get_active():
# x axis is selected
if not old_axis_was_x:
self.update_support_grid_manual_model()
max_distance = self.core.get("support_grid_distance_x")
else:
# y axis
if old_axis_was_x:
self.update_support_grid_manual_model()
max_distance = self.core.get("support_grid_distance_y")
# we allow an individual adjustment of 66% of the distance
max_distance /= 1.5
if hasattr(self.grid_adjustment_value, "set_lower"):
# gtk 2.14 is required for "set_lower" and "set_upper"
self.grid_adjustment_value.set_lower(-max_distance)
self.grid_adjustment_value.set_upper(max_distance)
if self.grid_adjustment_value.get_value() \
!= self.core.get("support_grid_adjustment_value"):
self.grid_adjustment_value.set_value(self.core.get(
"support_grid_adjustment_value"))
self.gui.get_object("SupportGridPositionManualShiftBox").set_sensitive(
self.grid_adjustment_selector.get_active() >= 0)
def update_support_grid_manual_adjust(self, widget=None, data1=None,
data2=None):
""" Update the current entry in the manual adjustment combobox after
a manual change. Additionally the slider and the numeric control are
synched.
"""
if self._block_manual_adjust_update:
return
self._block_manual_adjust_update = True
new_value = self.grid_adjustment_value.get_value()
self.core.set("support_grid_adjustment_value", new_value)
tree_iter = self.grid_adjustment_selector.get_active_iter()
if not tree_iter is None:
value_string = "(%+.1f)" % new_value
self.grid_adjustment_model.set(tree_iter, 1, value_string)
self.core.emit_event("support-model-changed")
self._block_manual_adjust_update = False
def reset_support_grid_manual(self, widget=None, reset_all=False):
if reset_all:
self.grid_adjustments_x = []
self.grid_adjustments_y = []
else:
self.core.set("support_grid_adjustment_value", 0)
self.update_support_grid_manual_model()
self.switch_support_grid_manual_selector()
self.core.emit_event("support-model-changed")
def update_support_grid_manual_model(self):
old_index = self.grid_adjustment_selector.get_active()
model = self.grid_adjustment_model
model.clear()
s = self.core
# get the toolpath without adjustments
low, high = self._get_bounds()
base_x, base_y = pycam.Toolpath.SupportGrid.get_support_grid_locations(
low[0], high[0], low[1], high[1],
s.get("support_grid_distance_x"),
s.get("support_grid_distance_y"),
offset_x=s.get("support_grid_offset_x"),
offset_y=s.get("support_grid_offset_y"))
# fill the adjustment lists
while len(self.grid_adjustments_x) < len(base_x):
self.grid_adjustments_x.append(0)
while len(self.grid_adjustments_y) < len(base_y):
self.grid_adjustments_y.append(0)
# select the currently active list
if self.grid_adjustment_axis_x.get_active():
base = base_x
adjustments = self.grid_adjustments_x
else:
base = base_y
adjustments = self.grid_adjustments_y
# generate the model content
for index, base_value in enumerate(base):
position = "%.2f%s" % (base_value, s.get("unit"))
if (0 <= index < len(adjustments)) and (adjustments[index] != 0):
diff = "(%+.1f)" % adjustments[index]
else:
diff = ""
model.append((position, diff))
if old_index < len(base):
self.grid_adjustment_selector.set_active(old_index)
else:
self.grid_adjustment_selector.set_active(-1)
def _get_bounds(self, models=None):
if not models:
models = self.core.get("models").get_selected()
models = [m.model for m in models]
low, high = pycam.Geometry.Model.get_combined_bounds(models)
if None in low or None in high:
return [0, 0, 0], [0, 0, 0]
else:
# TODO: the x/y offset should be configurable via a control
for index in range(2):
low[index] -= 5
high[index] += 5
return low, high