Commit 0e85bcfe authored by sumpfralle's avatar sumpfralle

implemented progress bar for model transformations (useful for huge models)


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@538 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent ad50d9d1
......@@ -70,6 +70,10 @@ class Line(TransformableContainer):
yield self.p1
yield self.p2
def get_children_count(self):
# a line always contains two points
return 2
def reset_cache(self):
self._dir = None
self._len = None
......@@ -295,6 +299,10 @@ class LineGroup(TransformableContainer):
for line in self._lines:
yield line
def get_children_count(self):
# two children per line -> 3 times the number of lines
return 3 * len(self._lines)
def _init_line_offsets(self):
if self._lines and self._line_offsets is None:
self._line_offsets = []
......@@ -303,23 +311,29 @@ class LineGroup(TransformableContainer):
for line in self._lines:
line_dir = line.dir()
vector = (line_dir.x, line_dir.y, line_dir.z)
offset_vector = Matrix.multiply_vector_matrix(vector, offset_matrix)
offset_point = Point(offset_vector[0], offset_vector[1], offset_vector[2])
self._line_offsets.append(Line(line.p1, line.p1.add(offset_point)))
def transform_by_matrix(self, matrix, transformed_list):
offset_vector = Matrix.multiply_vector_matrix(vector,
offset_matrix)
offset_point = Point(offset_vector[0], offset_vector[1],
offset_vector[2])
self._line_offsets.append(Line(line.p1,
line.p1.add(offset_point)))
def transform_by_matrix(self, matrix, transformed_list, **kwargs):
if self._lines:
offset_matrix = self.get_offset_matrix()
# initialize all offset vectors (if necessary)
self._init_line_offsets()
super(LineGroup, self).transform_by_matrix(matrix, transformed_list)
super(LineGroup, self).transform_by_matrix(matrix, transformed_list,
**kwargs)
# transform all offset vectors
if self._lines:
for offset in self._line_offsets:
if not id(offset) in transformed_list:
offset.transform_by_matrix(matrix, transformed_list)
offset.transform_by_matrix(matrix, transformed_list,
**kwargs)
# transform the offset vector of this line group
self._offset_matrix = Matrix.multiply_matrix_matrix(matrix, offset_matrix)
self._offset_matrix = Matrix.multiply_matrix_matrix(matrix,
offset_matrix)
def get_lines(self):
return self._lines[:]
......
......@@ -30,7 +30,7 @@ from pycam.Geometry.Matrix import TRANSFORMATIONS
from pycam.Toolpath import Bounds
from pycam.Geometry.utils import INFINITE
from pycam.Geometry import TransformableContainer
from pycam.Utils import ProgressCounter
class BaseModel(TransformableContainer):
......@@ -68,6 +68,15 @@ class BaseModel(TransformableContainer):
else:
yield item
def get_children_count(self):
result = 0
for item_group in self._item_groups:
for item in item_group:
result += 1
if hasattr(item, "get_children_count"):
result += item.get_children_count()
return result
def to_OpenGL(self):
for item in self.next():
item.to_OpenGL()
......@@ -128,21 +137,31 @@ class BaseModel(TransformableContainer):
for item in self.next():
self._update_limits(item)
def transform_by_template(self, direction="normal"):
def _get_progress_callback(self, update_callback):
if update_callback:
return ProgressCounter(self.get_children_count(),
update_callback=update_callback).increment
else:
return None
def transform_by_template(self, direction="normal", callback=None):
if direction in TRANSFORMATIONS.keys():
self.transform_by_matrix(TRANSFORMATIONS[direction])
self.transform_by_matrix(TRANSFORMATIONS[direction],
callback=self._get_progress_callback(callback))
def shift(self, shift_x, shift_y, shift_z):
def shift(self, shift_x, shift_y, shift_z, callback=None):
matrix = ((1, 0, 0, shift_x), (0, 1, 0, shift_y), (0, 0, 1, shift_z))
self.transform_by_matrix(matrix)
self.transform_by_matrix(matrix,
callback=self._get_progress_callback(callback))
def scale(self, scale_x, scale_y=None, scale_z=None):
def scale(self, scale_x, scale_y=None, scale_z=None, callback=None):
if scale_y is None:
scale_y = scale_x
if scale_z is None:
scale_z = scale_x
matrix = ((scale_x, 0, 0, 0), (0, scale_y, 0, 0), (0, 0, scale_z, 0))
self.transform_by_matrix(matrix)
self.transform_by_matrix(matrix,
callback=self._get_progress_callback(callback))
def get_bounds(self):
return Bounds(Bounds.TYPE_CUSTOM, (self.minx, self.miny, self.minz),
......
......@@ -59,7 +59,7 @@ class Point:
else:
return cmp(str(self), str(other))
def transform_by_matrix(self, matrix, transformed_list=None):
def transform_by_matrix(self, matrix, transformed_list=None, callback=None):
x = self.x * matrix[0][0] + self.y * matrix[0][1] \
+ self.z * matrix[0][2] + matrix[0][3]
y = self.x * matrix[1][0] + self.y * matrix[1][1] \
......@@ -69,6 +69,8 @@ class Point:
self.x = x
self.y = y
self.z = z
if callback:
callback()
self.reset_cache()
def reset_cache(self):
......
......@@ -80,12 +80,17 @@ class Triangle(TransformableContainer):
yield self.p2
yield self.p3
def transform_by_matrix(self, matrix, transformed_list=None):
def get_children_count(self):
# tree points per triangle
return 3
def transform_by_matrix(self, matrix, transformed_list=None, **kwargs):
previous_normal = self._normal
super(Triangle, self).transform_by_matrix(matrix, transformed_list)
super(Triangle, self).transform_by_matrix(matrix, transformed_list,
**kwargs)
# try to keep the original normal vector (transform it manually)
if not previous_normal is None:
previous_normal.transform_by_matrix(matrix)
previous_normal.transform_by_matrix(matrix, **kwargs)
self._normal = previous_normal
def name(self):
......
......@@ -38,6 +38,10 @@ class TransformableContainer(object):
be provided. This method is called when all children of the object were
successfully transformed.
A method 'get_children_count' for calculating the number of children
(recursively) is necessary for the "callback" parameter of
"transform_by_matrix".
Optionally the method 'transform_by_matrix' may be used to perform
object-specific calculations (e.g. retaining the 'normal' vector of a
triangle).
......@@ -47,7 +51,7 @@ class TransformableContainer(object):
not required to be a subclass of TransformableContainer.
"""
def transform_by_matrix(self, matrix, transformed_list=None):
def transform_by_matrix(self, matrix, transformed_list=None, callback=None):
if transformed_list is None:
transformed_list = []
# Prevent any kind of loops or double transformations (e.g. Points in
......@@ -57,7 +61,8 @@ class TransformableContainer(object):
for item in self.next():
if not id(item) in transformed_list:
if isinstance(item, TransformableContainer):
item.transform_by_matrix(matrix, transformed_list)
item.transform_by_matrix(matrix, transformed_list,
callback=callback)
else:
# non-TransformableContainer do not care to update the
# 'transformed_list'. Thus we need to do it.
......@@ -65,7 +70,11 @@ class TransformableContainer(object):
# Don't transmit the 'transformed_list' if the object is
# not a TransformableContainer. It is not necessary and it
# is hard to understand on the lowest level (e.g. Point).
item.transform_by_matrix(matrix)
item.transform_by_matrix(matrix, callback=callback)
# run the callback - e.g. for a progress counter
if callback and callback():
# user requesteded abort
break
self.reset_cache()
def __iter__(self):
......@@ -76,6 +85,11 @@ class TransformableContainer(object):
+ "'TransformableContainer' but it fails to implement the " \
+ "'next' generator") % str(type(self)))
def get_children_count(self):
raise NotImplementedError(("'%s' is a subclass of " \
+ "'TransformableContainer' but it fails to implement the " \
+ "'get_children_count' method") % str(type(self)))
def reset_cache(self):
raise NotImplementedError(("'%s' is a subclass of " \
+ "'TransformableContainer' but it fails to implement the " \
......
......@@ -512,7 +512,8 @@ class ProjectGui:
self.progress_bar = self.gui.get_object("ProgressBar")
self.progress_widget = self.gui.get_object("ProgressWidget")
self.task_pane = self.gui.get_object("MainTabs")
self.gui.get_object("ProgressCancelButton").connect("clicked", self.cancel_progress)
self.progress_cancel_button = self.gui.get_object("ProgressCancelButton")
self.progress_cancel_button.connect("clicked", self.cancel_progress)
# make sure that the toolpath settings are consistent
self.toolpath_table = self.gui.get_object("ToolPathTable")
self.toolpath_table.get_selection().connect("changed", self.toolpath_table_event, "update_buttons")
......@@ -1195,6 +1196,7 @@ class ProjectGui:
# (e.g. disabled if no OpenGL support is available)
toggle_3d_checkbox.set_active(self.view3d.enabled and new_state)
@progress_activity_guard
@gui_activity_guard
def transform_model(self, widget):
if widget is self.gui.get_object("Rotate"):
......@@ -1209,7 +1211,10 @@ class ProjectGui:
return
for obj, value in controls:
if self.gui.get_object(obj).get_active():
self.model.transform_by_template(value)
self.disable_progress_cancel_button()
self.update_progress_bar("Transforming model")
self.model.transform_by_template(value,
callback=self.update_progress_bar)
self.update_view()
def _treeview_get_active_index(self, table, datalist):
......@@ -1604,6 +1609,7 @@ class ProjectGui:
except IOError, err_msg:
log.warn("Failed to write preferences file (%s): %s" % (config_filename, err_msg))
@progress_activity_guard
@gui_activity_guard
def shift_model(self, widget, use_form_values=True):
if use_form_values:
......@@ -1614,7 +1620,10 @@ class ProjectGui:
shift_x = -self.model.minx
shift_y = -self.model.miny
shift_z = -self.model.minz
self.model.shift(shift_x, shift_y, shift_z)
self.update_progress_bar("Shifting model")
self.disable_progress_cancel_button()
self.model.shift(shift_x, shift_y, shift_z,
callback=self.update_progress_bar)
self.update_support_grid_model()
self.update_view()
......@@ -1629,8 +1638,11 @@ class ProjectGui:
def _set_model_center(self, center):
new_x, new_y, new_z = center
old_x, old_y, old_z = self._get_model_center()
self.model.shift(new_x - old_x, new_y - old_y, new_z - old_z)
self.update_progress_bar("Centering model")
self.model.shift(new_x - old_x, new_y - old_y, new_z - old_z,
callback=self.update_progress_bar)
@progress_activity_guard
@gui_activity_guard
def scale_model(self, widget=None, percent=None):
if percent is None:
......@@ -1639,7 +1651,9 @@ class ProjectGui:
if (factor <= 0) or (factor == 1):
return
old_center = self._get_model_center()
self.model.scale(factor)
self.update_progress_bar("Scaling model")
self.disable_progress_cancel_button()
self.model.scale(factor, callback=self.update_progress_bar)
self._set_model_center(old_center)
self.update_support_grid_model()
self.update_view()
......@@ -1662,6 +1676,7 @@ class ProjectGui:
scale_value.set_sensitive(enable_controls)
scale_value.set_value(value)
@progress_activity_guard
@gui_activity_guard
def scale_model_axis_fit(self, widget):
proportionally = self.gui.get_object("ScaleDimensionsProportionally").get_active()
......@@ -1672,8 +1687,10 @@ class ProjectGui:
factor = value / (getattr(self.model, "max" + axis_suffix) - getattr(self.model, "min" + axis_suffix))
# store the original center of the model
old_center = self._get_model_center()
self.update_progress_bar("Scaling model")
self.disable_progress_cancel_button()
if proportionally:
self.model.scale(factor)
self.model.scale(factor, callback=self.update_progress_bar)
else:
factor_x, factor_y, factor_z = (1, 1, 1)
if index == 0:
......@@ -1684,7 +1701,8 @@ class ProjectGui:
factor_z = factor
else:
return
self.model.scale(factor_x, factor_y, factor_z)
self.model.scale(factor_x, factor_y, factor_z,
callback=self.update_progress_bar)
# move the model to its previous center
self._set_model_center(old_center)
self.update_support_grid_model()
......@@ -2162,18 +2180,30 @@ class ProjectGui:
self.menubar.set_sensitive(False)
self.task_pane.set_sensitive(False)
self.update_progress_bar("", 0)
self.progress_cancel_button.set_sensitive(True)
self.progress_widget.show()
else:
self.progress_widget.hide()
self.task_pane.set_sensitive(True)
self.menubar.set_sensitive(True)
def disable_progress_cancel_button(self):
""" mainly useful for non-interruptable operations (e.g. model
transformations)
"""
self.progress_cancel_button.set_sensitive(False)
def update_progress_bar(self, text=None, percent=None):
if not percent is None:
percent = min(max(percent, 0.0), 100.0)
self.progress_bar.set_fraction(percent/100.0)
if not text is None:
self.progress_bar.set_text(text)
# update the GUI
while gtk.events_pending():
gtk.main_iteration()
# return if the user requested a break
return self._progress_cancel_requested
def cancel_progress(self, widget=None):
self._progress_cancel_requested = True
......@@ -2223,7 +2253,9 @@ class ProjectGui:
for path_index, path in enumerate(paths):
progress_text = "Simulating path %d/%d" % (path_index, len(paths))
progress_value_percent = 100.0 * path_index / len(paths)
self.update_progress_bar(progress_text, progress_value_percent)
if self.update_progress_bar(progress_text, progress_value_percent):
# break if the user pressed the "cancel" button
break
for index in range(len(path.points)):
self.cutter.moveto(path.points[index])
if index != 0:
......@@ -2232,14 +2264,9 @@ class ProjectGui:
if start != end:
simulation_backend.process_cutter_movement(start, end)
self.update_view()
# update the GUI
while gtk.events_pending():
gtk.main_iteration()
# break the loop if someone clicked the "cancel" button
if self._progress_cancel_requested:
if self.update_progress_bar():
break
if self._progress_cancel_requested:
break
# enable the simulation widget again (if we were started from the GUI)
if not widget is None:
self.gui.get_object("SimulationTab").set_sensitive(True)
......@@ -2256,7 +2283,7 @@ class ProjectGui:
self.settings.set("show_simulation", True)
self.update_toolpath_simulation(toolpath=toolpath)
# hide the controls immediately, if the simulation was cancelled
if self._progress_cancel_requested:
if self.update_progress_bar():
self.finish_toolpath_simulation()
@progress_activity_guard
......@@ -2272,16 +2299,12 @@ class ProjectGui:
def update(self, text=None, percent=None, tool_position=None):
if not tool_position is None:
parent.cutter.moveto(tool_position)
parent.update_progress_bar(text, percent)
if (time.time() - self.last_update) > 1.0/self.max_fps:
self.last_update = time.time()
if self.func:
self.func()
# update the GUI
while gtk.events_pending():
gtk.main_iteration()
# break the loop if someone clicked the "cancel" button
return parent._progress_cancel_requested
return parent.update_progress_bar(text, percent)
if self.settings.get("show_drill_progress"):
callback = self.update_view
else:
......@@ -2310,7 +2333,7 @@ class ProjectGui:
if toolpath is None:
# user interruption
# return "False" if the action was cancelled
return not self._progress_cancel_requested
return not self.update_progress_bar()
elif isinstance(toolpath, basestring):
# an error occoured - "toolpath" contains the error message
log.error("Failed to generate toolpath: %s" % toolpath)
......@@ -2329,7 +2352,7 @@ class ProjectGui:
self.update_toolpath_table()
self.update_view()
# return "False" if the action was cancelled
return not self._progress_cancel_requested
return not self.update_progress_bar()
def get_toolpath_settings(self, tool_settings, process_settings, bounds):
toolpath_settings = pycam.Gui.Settings.ToolpathSettings()
......
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