Commit fc0b6c0d authored by sumpfralle's avatar sumpfralle

r1064@erker: lars | 2010-07-06 20:15:04 +0200

 moved the "bounds" handling to a separate class


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@435 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent 07e44989
...@@ -30,9 +30,9 @@ import pycam.Gui.Settings ...@@ -30,9 +30,9 @@ import pycam.Gui.Settings
import pycam.Cutters import pycam.Cutters
import pycam.Toolpath.Generator import pycam.Toolpath.Generator
import pycam.Toolpath import pycam.Toolpath
import pycam.Geometry.utils as utils
import pycam.Utils.log import pycam.Utils.log
from pycam.Gui.OpenGLTools import ModelViewWindowGL from pycam.Gui.OpenGLTools import ModelViewWindowGL
from pycam.Toolpath import Bounds
from pycam import VERSION from pycam import VERSION
import pycam.Physics.ode_physics import pycam.Physics.ode_physics
# this requires ODE - we import it later, if necessary # this requires ODE - we import it later, if necessary
...@@ -42,6 +42,7 @@ import webbrowser ...@@ -42,6 +42,7 @@ import webbrowser
import ConfigParser import ConfigParser
import math import math
import time import time
import logging
import datetime import datetime
import re import re
import os import os
...@@ -107,14 +108,6 @@ def get_data_file_location(filename): ...@@ -107,14 +108,6 @@ def get_data_file_location(filename):
log.error(os.linesep.join(lines)) log.error(os.linesep.join(lines))
return None return None
def show_error_dialog(window, message):
# TODO: move this to the log handler
warn_window = gtk.MessageDialog(window, type=gtk.MESSAGE_ERROR,
buttons=gtk.BUTTONS_OK, message_format=str(message))
warn_window.set_title("Error")
warn_window.run()
warn_window.destroy()
class ProjectGui: class ProjectGui:
...@@ -122,10 +115,11 @@ class ProjectGui: ...@@ -122,10 +115,11 @@ class ProjectGui:
"inside": -1, "inside": -1,
"along": 0, "along": 0,
"around": 1} "around": 1}
# mapping of boundary types and GUI control elements
BOUNDARY_TYPES = { BOUNDARY_TYPES = {
"relative_margin": "BoundsTypeRelativeMargin", Bounds.TYPE_RELATIVE_MARGIN: "BoundsTypeRelativeMargin",
"fixed_margin": "BoundsTypeFixedMargin", Bounds.TYPE_FIXED_MARGIN: "BoundsTypeFixedMargin",
"custom": "BoundsTypeCustom"} Bounds.TYPE_CUSTOM: "BoundsTypeCustom"}
META_DATA_PREFIX = "PYCAM-META-DATA:" META_DATA_PREFIX = "PYCAM-META-DATA:"
...@@ -264,12 +258,16 @@ class ProjectGui: ...@@ -264,12 +258,16 @@ class ProjectGui:
bounds = self.settings.get("current_bounds") bounds = self.settings.get("current_bounds")
if bounds is None: if bounds is None:
return getattr(self.model, key) return getattr(self.model, key)
mapping = {"minx": "x_low", "maxx": "x_high", "miny": "y_low", low, high = bounds.get_absolute_limits()
"maxy": "y_high", "minz": "z_low", "maxz": "z_high"} index = "xyz".index(key[-1])
return self._get_bounds_limits_absolute(bounds)[mapping[key]] if key.startswith("min"):
return low[index]
else:
return high[index]
for key in ("minx", "maxx", "miny", "maxy", "minz", "maxz"): for key in ("minx", "maxx", "miny", "maxy", "minz", "maxz"):
# use a new variable "j" to avoid re-using the same object "key" # create a new variable "key" to avoid re-using the same object "key"
self.settings.add_item(key, lambda j=key: get_absolute_limit(j)) # (due to the lambda name scope)
self.settings.add_item(key, lambda key=key: get_absolute_limit(key))
# Transformations # Transformations
self.gui.get_object("Rotate").connect("clicked", self.transform_model) self.gui.get_object("Rotate").connect("clicked", self.transform_model)
self.gui.get_object("Flip").connect("clicked", self.transform_model) self.gui.get_object("Flip").connect("clicked", self.transform_model)
...@@ -512,6 +510,8 @@ class ProjectGui: ...@@ -512,6 +510,8 @@ class ProjectGui:
self.update_all_controls() self.update_all_controls()
self.no_dialog = no_dialog self.no_dialog = no_dialog
if not self.no_dialog: if not self.no_dialog:
# register a logging handler for displaying error messages
pycam.Utils.log.add_gtk_gui(self.window, logging.ERROR)
self.window.show() self.window.show()
def update_all_controls(self): def update_all_controls(self):
...@@ -612,7 +612,7 @@ class ProjectGui: ...@@ -612,7 +612,7 @@ class ProjectGui:
@gui_activity_guard @gui_activity_guard
def adjust_bounds(self, widget, axis, change): def adjust_bounds(self, widget, axis, change):
bounds = self.settings.get("current_bounds") bounds = self.settings.get("current_bounds")
abs_bounds = self._get_bounds_limits_absolute(bounds) abs_bounds_low, abs_bounds_high = bounds.get_absolute_limits()
# calculate the "change" for +/- (10% of this axis' model dimension) # calculate the "change" for +/- (10% of this axis' model dimension)
if bounds is None: if bounds is None:
return return
...@@ -626,104 +626,40 @@ class ProjectGui: ...@@ -626,104 +626,40 @@ class ProjectGui:
# not allowed # not allowed
return return
# calculate the new bounds # calculate the new bounds
axis_index = "xyz".index(axis)
if change == "0": if change == "0":
abs_bounds["%s_low" % axis] = getattr(self.model, "min%s" % axis) abs_bounds_low[axis_index] = getattr(self.model, "min%s" % axis)
abs_bounds["%s_high" % axis] = getattr(self.model, "max%s" % axis) abs_bounds_high[axis_index] = getattr(self.model, "max%s" % axis)
elif change == "+": elif change == "+":
abs_bounds["%s_low" % axis] -= change_value abs_bounds_low[axis_index] -= change_value
abs_bounds["%s_high" % axis] += change_value abs_bounds_high[axis_index] += change_value
elif change == "-": elif change == "-":
abs_bounds["%s_low" % axis] += change_value abs_bounds_low[axis_index] += change_value
abs_bounds["%s_high" % axis] -= change_value abs_bounds_high[axis_index] -= change_value
else: else:
# not allowed # not allowed
return return
# transfer the new bounds values to the old settings # transfer the new bounds values to the old settings
self._change_bounds_by_absolute_values(bounds, abs_bounds) bounds.adjust_bounds_to_absolute_limits(abs_bounds_low, abs_bounds_high)
# update the controls # update the controls
self._put_bounds_settings_to_gui(bounds) self._put_bounds_settings_to_gui(bounds)
# update the visualization # update the visualization
self.append_to_queue(self.update_boundary_limits) self.append_to_queue(self.update_boundary_limits)
def _get_bounds_limits_absolute(self, bounds):
minx, maxx, miny, maxy, minz, maxz = self.model.minx, self.model.maxx, \
self.model.miny, self.model.maxy, self.model.minz, \
self.model.maxz
# copy the original dict
abs_bounds = dict(bounds.items())
# "custom" is used for absolute limits
abs_bounds["type"] = "custom"
# calculate the absolute limits
if bounds["type"] == "relative_margin":
abs_bounds["x_low"] = minx - bounds["x_low"] / 100.0 * (maxx - minx)
abs_bounds["x_high"] = maxx + bounds["x_high"] / 100.0 * (maxx - minx)
abs_bounds["y_low"] = miny - bounds["y_low"] / 100.0 * (maxy - miny)
abs_bounds["y_high"] = maxy + bounds["y_high"] / 100.0 * (maxy - miny)
abs_bounds["z_low"] = minz - bounds["z_low"] / 100.0 * (maxz - minz)
abs_bounds["z_high"] = maxz + bounds["z_high"] / 100.0 * (maxz - minz)
elif bounds["type"] == "fixed_margin":
abs_bounds["x_low"] = minx - bounds["x_low"]
abs_bounds["x_high"] = maxx + bounds["x_high"]
abs_bounds["y_low"] = miny - bounds["y_low"]
abs_bounds["y_high"] = maxy + bounds["y_high"]
abs_bounds["z_low"] = minz - bounds["z_low"]
abs_bounds["z_high"] = maxz + bounds["z_high"]
elif bounds["type"] == "custom":
abs_bounds["x_low"] = bounds["x_low"]
abs_bounds["x_high"] = bounds["x_high"]
abs_bounds["y_low"] = bounds["y_low"]
abs_bounds["y_high"] = bounds["y_high"]
abs_bounds["z_low"] = bounds["z_low"]
abs_bounds["z_high"] = bounds["z_high"]
else:
# this should not happen
return None
return abs_bounds
@gui_activity_guard @gui_activity_guard
def switch_bounds_type(self, widget=None): def switch_bounds_type(self, widget=None):
bounds = self.settings.get("current_bounds") bounds = self.settings.get("current_bounds")
new_type = self._load_bounds_settings_from_gui()["type"] new_type = self._load_bounds_settings_from_gui().get_type()
if new_type == bounds["type"]: if new_type == bounds.get_type():
# no change # no change
return return
# calculate the absolute bounds of the previous configuration # calculate the absolute bounds of the previous configuration
abs_bounds = self._get_bounds_limits_absolute(bounds) abs_bounds_low, abs_bounds_high = bounds.get_absolute_limits()
bounds["type"] = new_type bounds.set_type(new_type)
self._change_bounds_by_absolute_values(bounds, abs_bounds) bounds.adjust_bounds_to_absolute_limits(abs_bounds_low, abs_bounds_high)
self._put_bounds_settings_to_gui(bounds) self._put_bounds_settings_to_gui(bounds)
self.append_to_queue(self.update_boundary_limits) self.append_to_queue(self.update_boundary_limits)
def _change_bounds_by_absolute_values(self, bounds, abs_bounds):
minx, maxx, miny, maxy, minz, maxz = self.model.minx, self.model.maxx, \
self.model.miny, self.model.maxy, self.model.minz, \
self.model.maxz
# calculate the new settings
if bounds["type"] == "relative_margin":
bounds["x_low"] = (minx - abs_bounds["x_low"]) / (maxx - minx) * 100.0
bounds["x_high"] = (abs_bounds["x_high"] - maxx) / (maxx - minx) * 100.0
bounds["y_low"] = (miny - abs_bounds["y_low"]) / (maxy - miny) * 100.0
bounds["y_high"] = (abs_bounds["y_high"] - maxy) / (maxy - miny) * 100.0
bounds["z_low"] = (minz - abs_bounds["z_low"]) / (maxz - minz) * 100.0
bounds["z_high"] = (abs_bounds["z_high"] - maxz) / (maxz - minz) * 100.0
elif bounds["type"] == "fixed_margin":
bounds["x_low"] = minx - abs_bounds["x_low"]
bounds["x_high"] = abs_bounds["x_high"] - maxx
bounds["y_low"] = miny - abs_bounds["y_low"]
bounds["y_high"] = abs_bounds["y_high"] - maxy
bounds["z_low"] = minz - abs_bounds["z_low"]
bounds["z_high"] = abs_bounds["z_high"] - maxz
elif bounds["type"] == "custom":
bounds["x_low"] = abs_bounds["x_low"]
bounds["x_high"] = abs_bounds["x_high"]
bounds["y_low"] = abs_bounds["y_low"]
bounds["y_high"] = abs_bounds["y_high"]
bounds["z_low"] = abs_bounds["z_low"]
bounds["z_high"] = abs_bounds["z_high"]
else:
# this should not happen
return
@gui_activity_guard @gui_activity_guard
def update_boundary_limits(self, widget=None): def update_boundary_limits(self, widget=None):
# the support grid depends on the boundary # the support grid depends on the boundary
...@@ -993,6 +929,7 @@ class ProjectGui: ...@@ -993,6 +929,7 @@ class ProjectGui:
@gui_activity_guard @gui_activity_guard
def toggle_3d_view(self, widget=None, value=None): def toggle_3d_view(self, widget=None, value=None):
toggle_3d_checkbox = self.gui.get_object("Toggle3DView")
# no interactive mode # no interactive mode
if self.no_dialog: if self.no_dialog:
return return
...@@ -1017,14 +954,15 @@ class ProjectGui: ...@@ -1017,14 +954,15 @@ class ProjectGui:
#self.append_to_queue(self.reset_bounds) #self.append_to_queue(self.reset_bounds)
self.view3d.reset_view() self.view3d.reset_view()
# disable the "toggle" button, if the 3D view does not work # disable the "toggle" button, if the 3D view does not work
self.gui.get_object("Toggle3DView").set_sensitive(self.view3d.enabled) toggle_3d_checkbox.set_active(False)
toggle_3d_checkbox.set_sensitive(self.view3d.enabled)
else: else:
# the window is just hidden # the window is just hidden
self.view3d.show() self.view3d.show()
self.update_view() self.update_view()
else: else:
self.view3d.hide() self.view3d.hide()
self.gui.get_object("Toggle3DView").set_active(new_state) toggle_3d_checkbox.set_active(new_state)
@gui_activity_guard @gui_activity_guard
def transform_model(self, widget): def transform_model(self, widget):
...@@ -1330,8 +1268,7 @@ class ProjectGui: ...@@ -1330,8 +1268,7 @@ class ProjectGui:
pycam.Exporters.STLExporter.STLExporter(self.model, comment=self.get_meta_data()).write(fi) pycam.Exporters.STLExporter.STLExporter(self.model, comment=self.get_meta_data()).write(fi)
fi.close() fi.close()
except IOError, err_msg: except IOError, err_msg:
if not no_dialog and not self.no_dialog: log.error("Failed to save model file")
show_error_dialog(self.window, "Failed to save model file")
else: else:
self.add_to_recent_file_list(filename) self.add_to_recent_file_list(filename)
...@@ -1524,7 +1461,7 @@ class ProjectGui: ...@@ -1524,7 +1461,7 @@ class ProjectGui:
if file_type and callable(importer): if file_type and callable(importer):
self.load_model(importer(filename)) self.load_model(importer(filename))
else: else:
show_error_dialog(self.window, "Failed to detect filetype!") log.error("Failed to detect filetype!")
@gui_activity_guard @gui_activity_guard
def export_emc_tools(self, widget=None, filename=None): def export_emc_tools(self, widget=None, filename=None):
...@@ -1541,8 +1478,7 @@ class ProjectGui: ...@@ -1541,8 +1478,7 @@ class ProjectGui:
out.write(text) out.write(text)
out.close() out.close()
except IOError, err_msg: except IOError, err_msg:
if not no_dialog and not self.no_dialog: log.error("Failed to save EMC tool file")
show_error_dialog(self.window, "Failed to save EMC tool file")
else: else:
self.add_to_recent_file_list(filename) self.add_to_recent_file_list(filename)
...@@ -1584,7 +1520,7 @@ class ProjectGui: ...@@ -1584,7 +1520,7 @@ class ProjectGui:
settings.load_file(filename) settings.load_file(filename)
self.tool_list = settings.get_tools() self.tool_list = settings.get_tools()
self.process_list = settings.get_processes() self.process_list = settings.get_processes()
self.bounds_list = settings.get_bounds() self.bounds_list = settings.get_bounds(lambda: self.model)
self.task_list = settings.get_tasks() self.task_list = settings.get_tasks()
self.update_tool_table() self.update_tool_table()
self.update_process_table() self.update_process_table()
...@@ -1592,10 +1528,17 @@ class ProjectGui: ...@@ -1592,10 +1528,17 @@ class ProjectGui:
self.update_tasklist_table() self.update_tasklist_table()
def _put_bounds_settings_to_gui(self, settings): def _put_bounds_settings_to_gui(self, settings):
self.gui.get_object("BoundsName").set_text(settings["name"]) self.gui.get_object("BoundsName").set_text(settings.get_name())
self.gui.get_object(self.BOUNDARY_TYPES[settings["type"]]).set_active(True) self.gui.get_object(self.BOUNDARY_TYPES[settings.get_type()]).set_active(True)
for key in ("x_low", "x_high", "y_low", "y_high", "z_low", "z_high"): low, high = settings.get_bounds()
self.gui.get_object("boundary_%s" % key).set_value(settings[key]) # relative margins are given in percent
if settings.get_type() == pycam.Toolpath.Bounds.TYPE_RELATIVE_MARGIN:
factor = 100
else:
factor = 1
for index, axis in enumerate("xyz"):
self.gui.get_object("boundary_%s_low" % axis).set_value(low[index] * factor)
self.gui.get_object("boundary_%s_high" % axis).set_value(high[index] * factor)
def _load_bounds_settings_from_gui(self, settings=None): def _load_bounds_settings_from_gui(self, settings=None):
def get_boundary_type_from_gui(): def get_boundary_type_from_gui():
...@@ -1603,11 +1546,20 @@ class ProjectGui: ...@@ -1603,11 +1546,20 @@ class ProjectGui:
if self.gui.get_object(objname).get_active(): if self.gui.get_object(objname).get_active():
return key return key
if settings is None: if settings is None:
settings = {} settings = pycam.Toolpath.Bounds()
settings["name"] = self.gui.get_object("BoundsName").get_text() settings.set_name(self.gui.get_object("BoundsName").get_text())
settings["type"] = get_boundary_type_from_gui() settings.set_type(get_boundary_type_from_gui())
for key in ("x_low", "x_high", "y_low", "y_high", "z_low", "z_high"): low = [None] * 3
settings[key] = self.gui.get_object("boundary_%s" % key).get_value() high = [None] * 3
# relative margins are given in percent
if settings.get_type() == pycam.Toolpath.Bounds.TYPE_RELATIVE_MARGIN:
factor = 0.01
else:
factor = 1
for index, axis in enumerate("xyz"):
low[index] = self.gui.get_object("boundary_%s_low" % axis).get_value() * factor
high[index] = self.gui.get_object("boundary_%s_high" % axis).get_value() * factor
settings.set_bounds(low, high)
return settings return settings
@gui_activity_guard @gui_activity_guard
...@@ -1628,9 +1580,8 @@ class ProjectGui: ...@@ -1628,9 +1580,8 @@ class ProjectGui:
model = self.gui.get_object("BoundsList") model = self.gui.get_object("BoundsList")
model.clear() model.clear()
# columns: index, description # columns: index, description
for index in range(len(self.bounds_list)): for index, bounds in enumerate(self.bounds_list):
bounds = self.bounds_list[index] items = (index, bounds.get_name())
items = (index, bounds["name"])
model.append(items) model.append(items)
if not new_index is None: if not new_index is None:
self._treeview_set_active_index(self.bounds_editor_table, new_index) self._treeview_set_active_index(self.bounds_editor_table, new_index)
...@@ -1669,10 +1620,11 @@ class ProjectGui: ...@@ -1669,10 +1620,11 @@ class ProjectGui:
prefix = "New Bounds " prefix = "New Bounds "
index = 1 index = 1
# loop while the current name is in use # loop while the current name is in use
while [True for bounds in self.bounds_list if bounds["name"] == "%s%d" % (prefix, index)]: while [True for bounds in self.bounds_list
if bounds.get_name() == "%s%d" % (prefix, index)]:
index += 1 index += 1
new_settings = self._load_bounds_settings_from_gui() new_settings = self._load_bounds_settings_from_gui()
new_settings["name"] = "%s%d" % (prefix, index) new_settings.set_name("%s%d" % (prefix, index))
self.bounds_list.append(new_settings) self.bounds_list.append(new_settings)
self.update_bounds_table(self.bounds_list.index(new_settings)) self.update_bounds_table(self.bounds_list.index(new_settings))
self._put_bounds_settings_to_gui(new_settings) self._put_bounds_settings_to_gui(new_settings)
...@@ -1883,9 +1835,8 @@ class ProjectGui: ...@@ -1883,9 +1835,8 @@ class ProjectGui:
return return
settings = pycam.Gui.Settings.ProcessSettings() settings = pycam.Gui.Settings.ProcessSettings()
if not settings.write_to_file(filename, self.tool_list, if not settings.write_to_file(filename, self.tool_list,
self.process_list, self.bounds_list, self.task_list) \ self.process_list, self.bounds_list, self.task_list):
and not no_dialog and not self.no_dialog: log.error("Failed to save settings file")
show_error_dialog(self.window, "Failed to save settings file")
else: else:
self.add_to_recent_file_list(filename) self.add_to_recent_file_list(filename)
...@@ -2040,11 +1991,7 @@ class ProjectGui: ...@@ -2040,11 +1991,7 @@ class ProjectGui:
if isinstance(toolpath, basestring): if isinstance(toolpath, basestring):
# an error occoured - "toolpath" contains the error message # an error occoured - "toolpath" contains the error message
message = "Failed to generate toolpath: %s" % toolpath log.error("Failed to generate toolpath: %s" % toolpath)
if not self.no_dialog:
show_error_dialog(self.window, message)
else:
log.warn("Toolpath generation failed: %s" % str(message))
# we were not successful (similar to a "cancel" request) # we were not successful (similar to a "cancel" request)
return False return False
else: else:
...@@ -2081,21 +2028,20 @@ class ProjectGui: ...@@ -2081,21 +2028,20 @@ class ProjectGui:
# this should never happen # this should never happen
log.error("Assertion failed: invalid boundary_mode (%s)" % str(self.settings.get("boundary_mode"))) log.error("Assertion failed: invalid boundary_mode (%s)" % str(self.settings.get("boundary_mode")))
abs_bounds = self._get_bounds_limits_absolute(bounds) abs_bounds_low, abs_bounds_high = bounds.get_absolute_limits()
minx = float(abs_bounds["x_low"])-offset minx = float(abs_bounds_low[0])-offset
maxx = float(abs_bounds["x_high"])+offset miny = float(abs_bounds_low[1])-offset
miny = float(abs_bounds["y_low"])-offset minz = float(abs_bounds_low[2])
maxy = float(abs_bounds["y_high"])+offset maxx = float(abs_bounds_high[0])+offset
minz = float(abs_bounds["z_low"]) maxy = float(abs_bounds_high[1])+offset
maxz = float(abs_bounds["z_high"]) maxz = float(abs_bounds_high[2])
toolpath_settings.set_bounds(minx, maxx, miny, maxy, minz, maxz) toolpath_settings.set_bounds(minx, maxx, miny, maxy, minz, maxz)
# check if the boundary limits are valid # check if the boundary limits are valid
if (minx > maxx) or (miny > maxy) or (minz > maxz): if (minx > maxx) or (miny > maxy) or (minz > maxz):
# don't generate a toolpath if the area is too small (e.g. due to the tool size) # don't generate a toolpath if the area is too small (e.g. due to the tool size)
if not self.no_dialog: log.error("Processing boundaries are too small for this tool size.")
show_error_dialog(self.window, "Processing boundaries are too small for this tool size.")
return None return None
# put the tool settings together # put the tool settings together
...@@ -2260,8 +2206,7 @@ class ProjectGui: ...@@ -2260,8 +2206,7 @@ class ProjectGui:
destination.close() destination.close()
log.info("GCode file successfully written: %s" % str(filename)) log.info("GCode file successfully written: %s" % str(filename))
except IOError, err_msg: except IOError, err_msg:
if not no_dialog and not self.no_dialog: log.error("Failed to save toolpath file")
show_error_dialog(self.window, "Failed to save toolpath file")
else: else:
self.add_to_recent_file_list(filename) self.add_to_recent_file_list(filename)
......
...@@ -20,8 +20,10 @@ You should have received a copy of the GNU General Public License ...@@ -20,8 +20,10 @@ You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>. along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
""" """
from pycam.Toolpath import Bounds
import pycam.Cutters import pycam.Cutters
import pycam.Utils.log import pycam.Utils.log
import pycam.Toolpath
import ConfigParser import ConfigParser
import StringIO import StringIO
import os import os
...@@ -186,10 +188,10 @@ name: Minimum ...@@ -186,10 +188,10 @@ name: Minimum
[Bounds1] [Bounds1]
name: 10% margin name: 10% margin
x_low: 10.0 x_low: 0.10
x_high: 10.0 x_high: 0.10
y_low: 10.0 y_low: 0.10
y_high: 10.0 y_high: 0.10
[TaskDefault] [TaskDefault]
enabled: yes enabled: yes
...@@ -317,6 +319,25 @@ process: 3 ...@@ -317,6 +319,25 @@ process: 3
def get_processes(self): def get_processes(self):
return self._get_category_items("process") return self._get_category_items("process")
def _get_bounds_instance_from_dict(self):
""" get Bounds instances for each bounds definition
@value model: the model that should be used for relative margins
@type model: pycam.Geometry.Model.Model or callable
@returns: list of Bounds instances
@rtype: list(Bounds)
"""
low_bounds = (indict["x_low"], indict["y_low"], indict["z_low"])
high_bounds = (indict["x_high"], indict["y_high"], indict["z_high"])
if indict["type"] == "relative_margin":
bounds_type = Bounds.TYPE_RELATIVE_MARGIN
elif indict["type"] == "fixed_margin":
bounds_type = Bounds.TYPE_FIXED_MARGIN
else:
bounds_type = Bounds.TYPE_CUSTOM
new_bound = Bounds(bounds_type, low_bounds, high_bounds)
new_bound.set_name(indict["name"])
return new_bound
def get_bounds(self): def get_bounds(self):
return self._get_category_items("bounds") return self._get_category_items("bounds")
...@@ -358,7 +379,11 @@ process: 3 ...@@ -358,7 +379,11 @@ process: 3
value = None value = None
if not value is None: if not value is None:
item[key] = value item[key] = value
item_list.append(item) if type_name == "bounds":
# don't add the pure dictionary, but the "bounds" instance
item_list.append(self.get_bounds_instance_from_dict(item))
else:
item_list.append(item)
index += 1 index += 1
current_section_name = "%s%d" % (prefix, index) current_section_name = "%s%d" % (prefix, index)
self._cache[type_name] = item_list self._cache[type_name] = item_list
...@@ -380,6 +405,24 @@ process: 3 ...@@ -380,6 +405,24 @@ process: 3
return str(value_type(value)) return str(value_type(value))
def get_config_text(self, tools=None, processes=None, bounds=None, tasks=None): def get_config_text(self, tools=None, processes=None, bounds=None, tasks=None):
def get_dictinary_of_bounds(b):
""" this function should be the reverse operation of "get_bounds"
"""
result = {}
result["name"] = b.get_name()
bounds_type_num = b.get_type()
if bounds_type_num == Bounds.TYPE_RELATIVE_MARGIN:
bounds_type_name = "relative_margin"
elif bounds_type_num == Bounds.TYPE_FIXED_MARGIN:
bounds_type_name = "fixed_margin"
else:
bounds_type_name = "custom"
result["type"] = bounds_type_name
low, high = b.get_bounds()
for index, axis in enumerate("xyz"):
result["%s_low"] = low[index]
result["%s_high"] = high[index]
return result
result = [] result = []
if tools is None: if tools is None:
tools = [] tools = []
...@@ -392,7 +435,7 @@ process: 3 ...@@ -392,7 +435,7 @@ process: 3
lists = {} lists = {}
lists["tool"] = tools lists["tool"] = tools
lists["process"] = processes lists["process"] = processes
lists["bounds"] = bounds lists["bounds"] = [get_dictionary_of_bounds(b) for b in bounds]
lists["task"] = tasks lists["task"] = tasks
for type_name in lists.keys(): for type_name in lists.keys():
type_list = lists[type_name] type_list = lists[type_name]
......
...@@ -113,3 +113,169 @@ class ToolPath: ...@@ -113,3 +113,169 @@ class ToolPath:
move(Point(path.points[-1].x, path.points[-1].y, safety_height)) move(Point(path.points[-1].x, path.points[-1].y, safety_height))
return move.result_time return move.result_time
class Bounds:
TYPE_RELATIVE_MARGIN = 0
TYPE_FIXED_MARGIN = 1
TYPE_CUSTOM = 2
def __init__(self, bounds_type=None, bounds_low=None, bounds_high=None,
ref_low=None, ref_high=None):
""" create a new Bounds instance
@value bounds_type: any of TYPE_RELATIVE_MARGIN | TYPE_FIXED_MARGIN | TYPE_CUSTOM
@type bounds_type: int
@value bounds_low: the lower margin of the boundary compared to the
reference object (for TYPE_RELATIVE_MARGIN | TYPE_FIXED_MARGIN) or
the specific boundary values (for TYPE_CUSTOM). Only the lower
values of the three axes (x, y and z) are given.
@type bounds_low: (tuple|list) of float
@value bounds_high: see 'bounds_low'
@type bounds_high: (tuple|list) of float
@value ref_low: a reference object described by a tuple (or list) of
three item. These three values describe only the lower boundary of
this object (for the x, y and z axes). Each item must be callable
or a float value. In case of a callable reference, the up-to-date
result of this callable is used whenever a value is calculated.
Thus there is no need to trigger a boundary update manually when
using callables as references. A mixed tuple of float values and
callables is allowed.
This argument is ignored for the boundary type "TYPE_CUSTOM".
@type ref_low: (tuple|list) of (float|callable)
@value ref_high: see 'ref_low'
@type ref_high: (tuple|list) of (float|callable)
"""
self.name = "No name"
if ref_low is None or ref_high is None:
# only the "custom" bounds model does not depend on a reference
bounds_type = self.TYPE_CUSTOM
# set type
if bounds_type is None:
self.set_type(self.TYPE_RELATIVE_MARGIN)
else:
self.set_type(bounds_type)
# store all reference values as callables (to simplify later usage)
self.ref_low = []
self.ref_high = []
for in_values, out in ((ref_low, self.ref_low), (ref_high, self.ref_high)):
if not in_values is None:
for index in range(3):
if callable(in_values[index]):
out.append(in_values[index])
else:
# Create new variables within the scope of the lambda
# function.
# The lambda function just returns the float value.
out.append(lambda in_values=in_values, index=index:
in_values[index])
else:
out.extend([0, 0, 0])
# store the bounds values
if bounds_low is None:
bounds_low = [0, 0, 0]
if bounds_high is None:
bounds_high = [0, 0, 0]
self.set_bounds(bounds_low, bounds_high)
def set_name(self, name):
self.name = name
def get_name(self):
return self.name
def get_type(self):
return self.bounds_type
def set_type(self, bounds_type):
# complain if an unknown bounds_type value was given
if not bounds_type in (Bounds.TYPE_RELATIVE_MARGIN,
Bounds.TYPE_FIXED_MARGIN, Bounds.TYPE_CUSTOM):
raise ValueError, "failed to create an instance of " \
+ "pycam.Toolpath.Bounds due to an invalid value of " \
+ "'bounds_type': %s" % repr(bounds_type)
else:
self.bounds_type = bounds_type
def get_bounds(self):
return self.bounds_low[:], self.bounds_high[:]
def set_bounds(self, low=None, high=None):
if not low is None:
if len(low) != 3:
raise ValueError, "lower bounds should be supplied as a " \
+ "tuple/list of 3 items - but %d were given" % len(low)
else:
self.bounds_low = list(low[:])
if not high is None:
if len(high) != 3:
raise ValueError, "upper bounds should be supplied as a " \
+ "tuple/list of 3 items - but %d were given" % len(high)
else:
self.bounds_high = list(high[:])
def get_absolute_limits(self):
""" calculate the current absolute limits of the Bounds instance
@returns: a tuple of two lists containg the low and high limits
@rvalue: tuple(list)
"""
# copy the original dict
low = [None] * 3
high = [None] * 3
# calculate the absolute limits
if self.bounds_type == self.TYPE_RELATIVE_MARGIN:
for index in range(3):
dim_width = self.ref_high[index]() - self.ref_low[index]()
low[index] = self.ref_low[index]() \
- self.bounds_low[index] * dim_width
high[index] = self.ref_high[index]() \
+ self.bounds_high[index] * dim_width
elif self.bounds_type == self.TYPE_FIXED_MARGIN:
for index in range(3):
low[index] = self.ref_low[index]() - self.bounds_low[index]
high[index] = self.ref_high[index]() + self.bounds_high[index]
elif self.bounds_type == self.TYPE_CUSTOM:
for index in range(3):
low[index] = self.bounds_low[index]
high[index] = self.bounds_high[index]
else:
# this should not happen
raise NotImplementedError, "the function 'get_absolute_limits' is" \
+ " currently not implemented for the bounds_type " \
+ "'%s'" % str(self.bounds_type)
return low, high
def adjust_bounds_to_absolute_limits(self, limits_low, limits_high):
""" change the current bounds settings according to some absolute values
This does not change the type of this bounds instance (e.g. relative).
@value limits_low: a tuple describing the new lower absolute boundary
@type limits_low: (tuple|list) of float
@value limits_high: a tuple describing the new lower absolute boundary
@type limits_high: (tuple|list) of float
"""
# calculate the new settings
if self.bounds_type == self.TYPE_RELATIVE_MARGIN:
for index in range(3):
self.bounds_low[index] = \
(self.ref_low[index]() - limits_low[index]) \
/ (self.ref_high[index]() - self.ref_low[index]())
self.bounds_high[index] = \
(limits_high[index] - self.ref_high[index]()) \
/ (self.ref_high[index]() - self.ref_low[index]())
elif self.bounds_type == self.TYPE_FIXED_MARGIN:
for index in range(3):
self.bounds_low[index] = self.ref_low[index]() - limits_low[index]
self.bounds_high[index] = limits_high[index] - self.ref_high[index]()
elif self.bounds_type == self.TYPE_CUSTOM:
for index in range(3):
self.bounds_low[index] = limits_low[index]
self.bounds_high[index] = limits_high[index]
else:
# this should not happen
raise NotImplementedError, "the function " \
+ "'adjust_bounds_to_absolute_limits' is currently not " \
+ "implemented for the bounds_type '%s'" \
% str(self.bounds_type)
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