Commit e7f1b929 authored by sumpfralle's avatar sumpfralle

improved simulation:

* added a progress slider for backward/forward control
* removed "modal" property to allow model view changes during simulation


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@902 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent 53030e64
...@@ -4271,8 +4271,7 @@ Usually you will want to use the cutter radius here to cut around the outline.</ ...@@ -4271,8 +4271,7 @@ Usually you will want to use the cutter radius here to cut around the outline.</
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Show a simple simulation of the generated toolpath. <property name="tooltip_text" translatable="yes">Show a simple simulation of the generated toolpath.</property>
This feature requires the Open Dynamics Engine (ODE).</property>
<property name="image">ShowToolPathSimulation</property> <property name="image">ShowToolPathSimulation</property>
</object> </object>
<packing> <packing>
...@@ -4377,6 +4376,7 @@ This feature requires the Open Dynamics Engine (ODE).</property> ...@@ -4377,6 +4376,7 @@ This feature requires the Open Dynamics Engine (ODE).</property>
</child> </child>
<child> <child>
<object class="GtkHBox" id="ProgressWidget"> <object class="GtkHBox" id="ProgressWidget">
<property name="spacing">3</property>
<child> <child>
<object class="GtkProgressBar" id="ProgressBar"> <object class="GtkProgressBar" id="ProgressBar">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -4387,11 +4387,33 @@ This feature requires the Open Dynamics Engine (ODE).</property> ...@@ -4387,11 +4387,33 @@ This feature requires the Open Dynamics Engine (ODE).</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="ProgressCancelButton"> <object class="GtkVBox" id="vbox13">
<property name="label" translatable="yes">Cancel</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="orientation">vertical</property>
<property name="receives_default">True</property> <child>
<object class="GtkToggleButton" id="ShowToolpathProgressButton">
<property name="label" translatable="yes">Show Progress</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ProgressCancelButton">
<property name="label" translatable="yes">Cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
...@@ -5414,7 +5436,7 @@ Leave this field empty to use the default ("ngc").</property> ...@@ -5414,7 +5436,7 @@ Leave this field empty to use the default ("ngc").</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="ShowDrillProgressCheckBox"> <object class="GtkCheckButton" id="ShowDrillCheckBox">
<property name="label" translatable="yes">Show Drill</property> <property name="label" translatable="yes">Show Drill</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
...@@ -7804,7 +7826,6 @@ Please read the description of the Server Mode (linked below) to understand the ...@@ -7804,7 +7826,6 @@ Please read the description of the Server Mode (linked below) to understand the
<object class="GtkDialog" id="SimulationDialog"> <object class="GtkDialog" id="SimulationDialog">
<property name="border_width">5</property> <property name="border_width">5</property>
<property name="title" translatable="yes">PyCAM simulation</property> <property name="title" translatable="yes">PyCAM simulation</property>
<property name="modal">True</property>
<property name="destroy_with_parent">True</property> <property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property> <property name="type_hint">dialog</property>
<property name="has_separator">False</property> <property name="has_separator">False</property>
...@@ -7867,6 +7888,29 @@ upon interesting bugs and weird results.</property> ...@@ -7867,6 +7888,29 @@ upon interesting bugs and weird results.</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkLabel" id="SimulationProgressTimelineLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Progress:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHScale" id="SimulationProgressTimeline">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">SimulationProgressTimelineValue</property>
<property name="draw_value">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">3</property>
</packing>
</child>
<child> <child>
<object class="GtkHBox" id="hbox36"> <object class="GtkHBox" id="hbox36">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -7895,7 +7939,7 @@ upon interesting bugs and weird results.</property> ...@@ -7895,7 +7939,7 @@ upon interesting bugs and weird results.</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="position">2</property> <property name="position">4</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -7908,7 +7952,7 @@ upon interesting bugs and weird results.</property> ...@@ -7908,7 +7952,7 @@ upon interesting bugs and weird results.</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="position">3</property> <property name="position">5</property>
</packing> </packing>
</child> </child>
</object> </object>
...@@ -7920,34 +7964,6 @@ upon interesting bugs and weird results.</property> ...@@ -7920,34 +7964,6 @@ upon interesting bugs and weird results.</property>
<object class="GtkHButtonBox" id="dialog-action_area8"> <object class="GtkHButtonBox" id="dialog-action_area8">
<property name="visible">True</property> <property name="visible">True</property>
<property name="layout_style">end</property> <property name="layout_style">end</property>
<child>
<object class="GtkButton" id="SimulationCancelButton">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="UpdateSimulationButton">
<property name="label">gtk-refresh</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child> <child>
<object class="GtkButton" id="ExitSimulationButton"> <object class="GtkButton" id="ExitSimulationButton">
<property name="label">gtk-close</property> <property name="label">gtk-close</property>
...@@ -7959,7 +7975,7 @@ upon interesting bugs and weird results.</property> ...@@ -7959,7 +7975,7 @@ upon interesting bugs and weird results.</property>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">False</property> <property name="fill">False</property>
<property name="position">2</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
</object> </object>
...@@ -7972,9 +7988,11 @@ upon interesting bugs and weird results.</property> ...@@ -7972,9 +7988,11 @@ upon interesting bugs and weird results.</property>
</object> </object>
</child> </child>
<action-widgets> <action-widgets>
<action-widget response="0">SimulationCancelButton</action-widget>
<action-widget response="0">UpdateSimulationButton</action-widget>
<action-widget response="0">ExitSimulationButton</action-widget> <action-widget response="0">ExitSimulationButton</action-widget>
</action-widgets> </action-widgets>
</object> </object>
<object class="GtkAdjustment" id="SimulationProgressTimelineValue">
<property name="upper">100</property>
<property name="step_increment">1</property>
</object>
</interface> </interface>
...@@ -896,8 +896,7 @@ def draw_complete_model_view(settings): ...@@ -896,8 +896,7 @@ def draw_complete_model_view(settings):
# don't do it, if a new toolpath is just being calculated # don't do it, if a new toolpath is just being calculated
safety_height = settings.get("gcode_safety_height") safety_height = settings.get("gcode_safety_height")
if settings.get("show_toolpath") \ if settings.get("show_toolpath") \
and not (settings.get("show_drill_progress") \ and not settings.get("toolpath_in_progress") \
and (not settings.get("toolpath_in_progress") is None)) \
and not (settings.get("show_simulation") \ and not (settings.get("show_simulation") \
and settings.get("simulation_toolpath_moves")): and settings.get("simulation_toolpath_moves")):
for toolpath_obj in settings.get("toolpath"): for toolpath_obj in settings.get("toolpath"):
...@@ -907,13 +906,14 @@ def draw_complete_model_view(settings): ...@@ -907,13 +906,14 @@ def draw_complete_model_view(settings):
settings.get("color_toolpath_return"), settings.get("color_toolpath_return"),
show_directions=settings.get("show_directions")) show_directions=settings.get("show_directions"))
# draw the drill # draw the drill
if settings.get("show_drill_progress") \ if settings.get("show_drill"):
and settings.get("toolpath_in_progress"):
cutter = settings.get("cutter") cutter = settings.get("cutter")
if not cutter is None: if not cutter is None:
GL.glColor4f(*settings.get("color_cutter")) GL.glColor4f(*settings.get("color_cutter"))
cutter.to_OpenGL() cutter.to_OpenGL()
# also show the toolpath that is currently being calculated if settings.get("show_drill_progress") \
and settings.get("toolpath_in_progress"):
# show the toolpath that is currently being calculated
toolpath_in_progress = settings.get("toolpath_in_progress") toolpath_in_progress = settings.get("toolpath_in_progress")
# do a quick conversion from a list of Paths to a list of points # do a quick conversion from a list of Paths to a list of points
moves = [] moves = []
......
...@@ -104,7 +104,7 @@ PREFERENCES_DEFAULTS = { ...@@ -104,7 +104,7 @@ PREFERENCES_DEFAULTS = {
"show_dimensions": True, "show_dimensions": True,
"show_bounding_box": True, "show_bounding_box": True,
"show_toolpath": True, "show_toolpath": True,
"show_drill_progress": False, "show_drill": False,
"show_directions": False, "show_directions": False,
"color_background": (0.0, 0.0, 0.0, 1.0), "color_background": (0.0, 0.0, 0.0, 1.0),
"color_model": (0.5, 0.5, 1.0, 1.0), "color_model": (0.5, 0.5, 1.0, 1.0),
...@@ -653,12 +653,16 @@ class ProjectGui: ...@@ -653,12 +653,16 @@ class ProjectGui:
("show_dimensions", "ShowDimensionsCheckBox"), ("show_dimensions", "ShowDimensionsCheckBox"),
("show_bounding_box", "ShowBoundingCheckBox"), ("show_bounding_box", "ShowBoundingCheckBox"),
("show_toolpath", "ShowToolPathCheckBox"), ("show_toolpath", "ShowToolPathCheckBox"),
("show_drill_progress", "ShowDrillProgressCheckBox"), ("show_drill", "ShowDrillCheckBox"),
("show_directions", "ShowDirectionsCheckBox")): ("show_directions", "ShowDirectionsCheckBox")):
obj = self.gui.get_object(objname) obj = self.gui.get_object(objname)
self.settings.add_item(name, obj.get_active, obj.set_active) self.settings.add_item(name, obj.get_active, obj.set_active)
# all of the objects above should trigger redraw # all of the objects above should trigger redraw
obj.connect("toggled", self.update_view) obj.connect("toggled", self.update_view)
self.show_progress_button = self.gui.get_object("ShowToolpathProgressButton")
self.settings.add_item("show_drill_progress",
self.show_progress_button.get_active,
self.show_progress_button.set_active)
for name, objname in ( for name, objname in (
("view_light", "OpenGLLight"), ("view_light", "OpenGLLight"),
("view_shadow", "OpenGLShadow"), ("view_shadow", "OpenGLShadow"),
...@@ -813,18 +817,21 @@ class ProjectGui: ...@@ -813,18 +817,21 @@ class ProjectGui:
self.gui.get_object("toolpath_delete").connect("clicked", self.toolpath_table_event, "delete") self.gui.get_object("toolpath_delete").connect("clicked", self.toolpath_table_event, "delete")
self.gui.get_object("toolpath_simulate").connect("clicked", self.toolpath_table_event, "simulate") self.gui.get_object("toolpath_simulate").connect("clicked", self.toolpath_table_event, "simulate")
self.gui.get_object("ExitSimulationButton").connect("clicked", self.finish_toolpath_simulation) self.gui.get_object("ExitSimulationButton").connect("clicked", self.finish_toolpath_simulation)
self.gui.get_object("UpdateSimulationButton").connect("clicked", self.update_toolpath_simulation)
speed_factor_widget = self.gui.get_object("SimulationSpeedFactor") speed_factor_widget = self.gui.get_object("SimulationSpeedFactor")
self.settings.add_item("simulation_speed_factor", self.settings.add_item("simulation_speed_factor",
lambda: pow(10, speed_factor_widget.get_value()), lambda: pow(10, speed_factor_widget.get_value()),
lambda value: speed_factor_widget.set_value(math.log10(max(0.001, value)))) lambda value: speed_factor_widget.set_value(math.log10(max(0.001, value))))
simulation_progress = self.gui.get_object("SimulationProgressTimelineValue")
def update_simulation_progress(widget):
complete = self.settings.get("simulation_complete_distance")
self.settings.set("simulation_current_distance", widget.get_value() / 100.0 * complete)
simulation_progress.connect("value-changed", update_simulation_progress)
# update the speed factor label # update the speed factor label
speed_factor_widget.connect("value-changed", speed_factor_widget.connect("value-changed",
lambda widget: self.gui.get_object("SimulationSpeedFactorValueLabel").set_label( lambda widget: self.gui.get_object("SimulationSpeedFactorValueLabel").set_label(
"%g" % self.settings.get("simulation_speed_factor"))) "%g" % self.settings.get("simulation_speed_factor")))
self.simulation_window = self.gui.get_object("SimulationDialog") self.simulation_window = self.gui.get_object("SimulationDialog")
self.simulation_window.connect("delete-event", self.toggle_about_window, False) self.simulation_window.connect("delete-event", self.finish_toolpath_simulation)
self.gui.get_object("SimulationCancelButton").connect("clicked", self.cancel_progress)
# store the original content (for adding the number of current toolpaths in "update_toolpath_table") # store the original content (for adding the number of current toolpaths in "update_toolpath_table")
self._original_toolpath_tab_label = self.gui.get_object("ToolPathTabLabel").get_text() self._original_toolpath_tab_label = self.gui.get_object("ToolPathTabLabel").get_text()
# tool editor # tool editor
...@@ -3109,6 +3116,7 @@ class ProjectGui: ...@@ -3109,6 +3116,7 @@ class ProjectGui:
self.progress_widget.hide() self.progress_widget.hide()
self.task_pane.set_sensitive(True) self.task_pane.set_sensitive(True)
self.menubar.set_sensitive(True) self.menubar.set_sensitive(True)
self.show_progress_button.hide()
def disable_progress_cancel_button(self): def disable_progress_cancel_button(self):
""" mainly useful for non-interruptable operations (e.g. model """ mainly useful for non-interruptable operations (e.g. model
...@@ -3156,6 +3164,9 @@ class ProjectGui: ...@@ -3156,6 +3164,9 @@ class ProjectGui:
self.progress_bar.set_text(os.linesep.join(lines)) self.progress_bar.set_text(os.linesep.join(lines))
# update the GUI # update the GUI
current_time = time.time() current_time = time.time()
# show the "show_tool_button" ("hide" is called in the progress decorator)
if self.settings.get("toolpath_in_progress"):
self.show_progress_button.show()
# Don't update the GUI more often than once per second. # Don't update the GUI more often than once per second.
# Exception: text-only updates # Exception: text-only updates
# This restriction improves performance and reduces the # This restriction improves performance and reduces the
...@@ -3177,7 +3188,7 @@ class ProjectGui: ...@@ -3177,7 +3188,7 @@ class ProjectGui:
def cancel_progress(self, widget=None): def cancel_progress(self, widget=None):
self._progress_cancel_requested = True self._progress_cancel_requested = True
def finish_toolpath_simulation(self, widget=None): def finish_toolpath_simulation(self, widget=None, data=None):
# hide the simulation tab # hide the simulation tab
self.simulation_window.hide() self.simulation_window.hide()
# enable all other tabs again # enable all other tabs again
...@@ -3185,38 +3196,57 @@ class ProjectGui: ...@@ -3185,38 +3196,57 @@ class ProjectGui:
self.settings.set("simulation_object", None) self.settings.set("simulation_object", None)
self.settings.set("simulation_toolpath_moves", None) self.settings.set("simulation_toolpath_moves", None)
self.settings.set("show_simulation", False) self.settings.set("show_simulation", False)
self.settings.set("simulation_toolpath", None)
self.update_view() self.update_view()
# don't destroy the simulation window (for "destroy" event)
return True
@progress_activity_guard
def update_toolpath_simulation(self, widget=None, toolpath=None): def update_toolpath_simulation(self, widget=None, toolpath=None):
# get the currently selected toolpath, if none is give # update the GUI
if toolpath is None: while gtk.events_pending():
toolpath_index = self._treeview_get_active_index(self.toolpath_table, self.toolpath) gtk.main_iteration()
if toolpath_index is None: if not self.settings.get("show_simulation"):
return # cancel
else: return False
toolpath = self.toolpath[toolpath_index]
# set the current cutter
self.cutter = toolpath.toolpath_settings.get_tool()
# calculate steps
safety_height = self.settings.get("gcode_safety_height") safety_height = self.settings.get("gcode_safety_height")
machine_time = toolpath.get_machine_time(safety_height=safety_height) if not self.settings.get("simulation_toolpath"):
complete_distance = toolpath.get_machine_movement_distance( # get the currently selected toolpath, if none is give
safety_height=safety_height) if toolpath is None:
current_distance = 0 toolpath_index = self._treeview_get_active_index(self.toolpath_table, self.toolpath)
time_step = 1.0 / self.settings.get("drill_progress_max_fps") if toolpath_index is None:
feedrate = toolpath.toolpath_settings.get_tool_settings()["feedrate"] return
self.update_progress_bar("Simulating movements") else:
while current_distance <= complete_distance: toolpath = self.toolpath[toolpath_index]
current_distance += self.settings.get("simulation_speed_factor") * time_step * feedrate / 60 self.settings.set("simulation_toolpath", toolpath)
progress_value_percent = 100.0 * current_distance / complete_distance # set the current cutter
self.cutter = toolpath.toolpath_settings.get_tool()
# calculate steps
self.settings.set("simulation_machine_time",
toolpath.get_machine_time(safety_height=safety_height))
self.settings.set("simulation_complete_distance",
toolpath.get_machine_movement_distance(
safety_height=safety_height))
self.settings.set("simulation_current_distance", 0)
else:
toolpath = self.settings.get("simulation_toolpath")
if (self.settings.get("simulation_current_distance") \
< self.settings.get("simulation_complete_distance")):
time_step = 1.0 / self.settings.get("drill_progress_max_fps")
feedrate = toolpath.toolpath_settings.get_tool_settings()["feedrate"]
distance_step = self.settings.get("simulation_speed_factor") * time_step * feedrate / 60
self.settings.set("simulation_current_distance",
self.settings.get("simulation_current_distance") \
+ distance_step)
moves = toolpath.get_moves(safety_height=safety_height, moves = toolpath.get_moves(safety_height=safety_height,
max_movement=current_distance) max_movement=self.settings.get("simulation_current_distance"))
self.settings.set("simulation_toolpath_moves", moves) self.settings.set("simulation_toolpath_moves", moves)
if moves:
self.cutter.moveto(moves[-1][0])
self.update_view() self.update_view()
if self.update_progress_bar(percent=progress_value_percent): progress_value_percent = 100.0 * self.settings.get("simulation_current_distance") \
break / self.settings.get("simulation_complete_distance")
time.sleep(time_step) self.gui.get_object("SimulationProgressTimelineValue").set_value(progress_value_percent)
return True
@progress_activity_guard @progress_activity_guard
def update_toolpath_simulation_ode(self, widget=None, toolpath=None): def update_toolpath_simulation_ode(self, widget=None, toolpath=None):
...@@ -3278,17 +3308,16 @@ class ProjectGui: ...@@ -3278,17 +3308,16 @@ class ProjectGui:
"BoundsTabLabel"): "BoundsTabLabel"):
self.gui.get_object(objname).set_sensitive(new_state) self.gui.get_object(objname).set_sensitive(new_state)
def show_toolpath_simulation(self, toolpath): def show_toolpath_simulation(self, toolpath=None):
# disable the main controls # disable the main controls
self.toggle_tabs_for_simulation(False) self.toggle_tabs_for_simulation(False)
# show the simulation controls # show the simulation controls
self.simulation_window.show() self.simulation_window.show()
# start the simulation # start the simulation
self.settings.set("show_simulation", True) self.settings.set("show_simulation", True)
self.update_toolpath_simulation(toolpath=toolpath) time_step = int(1000 / self.settings.get("drill_progress_max_fps"))
# hide the controls immediately, if the simulation was cancelled # update the toolpath simulation repeatedly
if self.update_progress_bar(): gobject.timeout_add(time_step, self.update_toolpath_simulation)
self.finish_toolpath_simulation()
@progress_activity_guard @progress_activity_guard
def generate_toolpath(self, tool_settings, process_settings, bounds): def generate_toolpath(self, tool_settings, process_settings, bounds):
...@@ -3312,11 +3341,11 @@ class ProjectGui: ...@@ -3312,11 +3341,11 @@ class ProjectGui:
self.func() self.func()
# break the loop if someone clicked the "cancel" button # break the loop if someone clicked the "cancel" button
return parent.update_progress_bar(text, percent) return parent.update_progress_bar(text, percent)
if self.settings.get("show_drill_progress"): # allow the "show_drill_progress" setting to be changed instantly
callback = self.update_view def conditional_progress_update():
else: if self.settings.get("show_drill_progress"):
callback = None self.update_view()
draw_callback = UpdateView(callback, draw_callback = UpdateView(conditional_progress_update,
max_fps=self.settings.get("drill_progress_max_fps")).update max_fps=self.settings.get("drill_progress_max_fps")).update
self.update_progress_bar("Generating collision model") self.update_progress_bar("Generating collision model")
......
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