Commit c658a40c authored by sumpfralle's avatar sumpfralle

polygons representing "holes" are now half-transparent

added button for repair of inconsistent polygon directions


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@1013 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent 1dde61ca
...@@ -8,6 +8,7 @@ Version 0.5 - UNRELEASED ...@@ -8,6 +8,7 @@ Version 0.5 - UNRELEASED
* changed "simulation mode" for visualizing the machine moves * changed "simulation mode" for visualizing the machine moves
* added a very simple "pocketing" mode for 2D models * added a very simple "pocketing" mode for 2D models
* added 2D projection of 3D models * added 2D projection of 3D models
* added automatic repair of inconsistent polygon winding (inside/outside detection)
* added toolpath cropping * added toolpath cropping
* added toolpath grid pattern: clone a single toolpath in rows and columns * added toolpath grid pattern: clone a single toolpath in rows and columns
* added support for DXF features "LWPOLYLINE" and "ARC" * added support for DXF features "LWPOLYLINE" and "ARC"
...@@ -28,6 +29,7 @@ Version 0.5 - UNRELEASED ...@@ -28,6 +29,7 @@ Version 0.5 - UNRELEASED
* via a button and via the context menu * via a button and via the context menu
* improved stability of remote processing: disconnected nodes should not cause problems anymore * improved stability of remote processing: disconnected nodes should not cause problems anymore
* automatically distributed support bridges can now be placed at corners or edges * automatically distributed support bridges can now be placed at corners or edges
* visualize "inside" polygons (holes) through partial transparency
Version 0.4.0.1 - 2010-10-24 Version 0.4.0.1 - 2010-10-24
* disabled parallel processing for Windows standalone executable * disabled parallel processing for Windows standalone executable
......
...@@ -995,20 +995,22 @@ ...@@ -995,20 +995,22 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="ScaleInchMM"> <object class="GtkButton" id="DirectionsGuessButton">
<property name="label" translatable="yes">Inch -&gt; mm</property> <property name="label" translatable="yes">Revise directions</property>
<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">Analyze the inside/outside relationships of all polygons.
This usually fixes inconsistent polygon winding states
caused by careless DXF/SVG exporting programs.</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="ScaleMMInch"> <object class="GtkButton" id="Projection2D">
<property name="label" translatable="yes">mm -&gt; Inch</property> <property name="label" translatable="yes">2D Projection</property>
<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>
...@@ -1019,8 +1021,8 @@ ...@@ -1019,8 +1021,8 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="Projection2D"> <object class="GtkButton" id="ScaleMMInch">
<property name="label" translatable="yes">2D Projection</property> <property name="label" translatable="yes">mm -&gt; Inch</property>
<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>
...@@ -1030,6 +1032,18 @@ ...@@ -1030,6 +1032,18 @@
<property name="position">3</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkButton" id="ScaleInchMM">
<property name="label" translatable="yes">Inch -&gt; mm</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">4</property>
</packing>
</child>
</object> </object>
</child> </child>
</object> </object>
......
...@@ -443,11 +443,51 @@ class ContourModel(BaseModel): ...@@ -443,11 +443,51 @@ class ContourModel(BaseModel):
else: else:
return [group for group in self._line_groups if group.minz <= z] return [group for group in self._line_groups if group.minz <= z]
def detect_directions(self, callback=None):
if callback:
progress_callback = pycam.Utils.ProgressCounter(
2 * len(self.get_polygons()), callback).increment
else:
progress_callback = None
finished = []
remaining_polys = list(self.get_polygons())
remaining_polys.sort(key=lambda poly: abs(poly.get_area()))
while remaining_polys:
# pick the largest polygon
current = remaining_polys.pop()
# start with the smallest finished polygon
for comp, is_outer in finished:
if comp.is_polygon_inside(current):
finished.insert(0, (current, not is_outer))
break
else:
finished.insert(0, (current, True))
if progress_callback and progress_callback():
return
# Adjust the directions of all polygons according to the result
# of the previous analysis.
change_counter = 0
for polygon, is_outer in finished:
if polygon.is_outer() != is_outer:
polygon.reverse_direction()
change_counter += 1
if progress_callback and progress_callback():
self.reset_cache()
return
log.info("The winding of %d polygons was fixed." % change_counter)
self.reset_cache()
def reverse_directions(self, callback=None): def reverse_directions(self, callback=None):
if callback:
progress_callback = pycam.Utils.ProgressCounter(
len(self.get_polygons()), callback).increment
else:
progress_callback = None
for polygon in self._line_groups: for polygon in self._line_groups:
polygon.reverse_direction() polygon.reverse_direction()
if callback and callback(): if progress_callback and progress_callback():
return None self.reset_cache()
return
self.reset_cache() self.reset_cache()
def get_reversed(self): def get_reversed(self):
......
...@@ -29,6 +29,12 @@ import pycam.Utils.log ...@@ -29,6 +29,12 @@ import pycam.Utils.log
# import later to avoid circular imports # import later to avoid circular imports
#from pycam.Geometry.Model import ContourModel #from pycam.Geometry.Model import ContourModel
try:
import OpenGL.GL as GL
GL_enabled = True
except ImportError:
GL_enabled = False
log = pycam.Utils.log.get_logger() log = pycam.Utils.log.get_logger()
...@@ -332,8 +338,23 @@ class Polygon(TransformableContainer): ...@@ -332,8 +338,23 @@ class Polygon(TransformableContainer):
return self._lines_cache[:] return self._lines_cache[:]
def to_OpenGL(self, **kwords): def to_OpenGL(self, **kwords):
for line in self.get_lines(): if not GL_enabled:
line.to_OpenGL(**kwords) return
if self.is_closed:
is_outer = self.is_outer()
if not is_outer:
color = GL.glGetFloatv(GL.GL_CURRENT_COLOR)
GL.glColor(color[0], color[1], color[2], color[3] / 2)
GL.glBegin(GL.GL_LINE_LOOP)
for point in self._points:
GL.glVertex3f(point.x, point.y, point.z)
GL.glEnd()
if not is_outer:
GL.glColor(*color)
else:
for line in self.get_lines():
line.to_OpenGL(**kwords)
def _update_limits(self, point): def _update_limits(self, point):
if self.minx is None: if self.minx is None:
......
...@@ -578,6 +578,8 @@ class ProjectGui: ...@@ -578,6 +578,8 @@ class ProjectGui:
lambda widget, data: self.window.set_default(None)) lambda widget, data: self.window.set_default(None))
self.gui.get_object("ToggleModelDirectionButton").connect("clicked", self.gui.get_object("ToggleModelDirectionButton").connect("clicked",
self.reverse_model_direction) self.reverse_model_direction)
self.gui.get_object("DirectionsGuessButton").connect("clicked",
self.guess_model_directions)
self.gui.get_object("ScaleInchMM").connect("clicked", self.scale_model, self.gui.get_object("ScaleInchMM").connect("clicked", self.scale_model,
100 * 25.4, False) 100 * 25.4, False)
self.gui.get_object("ScaleMMInch").connect("clicked", self.scale_model, self.gui.get_object("ScaleMMInch").connect("clicked", self.scale_model,
...@@ -1196,11 +1198,18 @@ class ProjectGui: ...@@ -1196,11 +1198,18 @@ class ProjectGui:
def update_model_type_related_controls(self): def update_model_type_related_controls(self):
is_reversible = (not self.model is None) \ is_reversible = (not self.model is None) \
and hasattr(self.model, "reverse_directions") and hasattr(self.model, "reverse_directions")
self.gui.get_object("ToggleModelDirectionButton").set_sensitive( controls_2d = ("ToggleModelDirectionButton", "DirectionsGuessButton")
is_reversible) for control in controls_2d:
if is_reversible:
self.gui.get_object(control).show()
else:
self.gui.get_object(control).hide()
is_projectable = (not self.model is None) \ is_projectable = (not self.model is None) \
and hasattr(self.model, "get_waterline_contour") and hasattr(self.model, "get_waterline_contour")
self.gui.get_object("Projection2D").set_sensitive(is_projectable) if is_projectable:
self.gui.get_object("Projection2D").show()
else:
self.gui.get_object("Projection2D").hide()
# disable the lower boundary for contour models # disable the lower boundary for contour models
is_contour = isinstance(self.model, pycam.Geometry.Model.ContourModel) is_contour = isinstance(self.model, pycam.Geometry.Model.ContourModel)
self.gui.get_object("boundary_z_low").set_sensitive(not is_contour) self.gui.get_object("boundary_z_low").set_sensitive(not is_contour)
...@@ -2844,6 +2853,17 @@ class ProjectGui: ...@@ -2844,6 +2853,17 @@ class ProjectGui:
value = "%.3f" % getattr(self.model, attr) value = "%.3f" % getattr(self.model, attr)
self.gui.get_object(label_name).set_label(value) self.gui.get_object(label_name).set_label(value)
@progress_activity_guard
@gui_activity_guard
def guess_model_directions(self, widget=None):
if (self.model is None) \
or not hasattr(self.model, "reverse_directions"):
return
self._store_undo_state()
self.update_progress_bar(text="Analyzing directions of contour model")
self.model.detect_directions(callback=self.update_progress_bar)
self.update_support_model()
@progress_activity_guard @progress_activity_guard
@gui_activity_guard @gui_activity_guard
def reverse_model_direction(self, widget=None): def reverse_model_direction(self, widget=None):
...@@ -2852,10 +2872,7 @@ class ProjectGui: ...@@ -2852,10 +2872,7 @@ class ProjectGui:
return return
self._store_undo_state() self._store_undo_state()
self.update_progress_bar(text="Reversing directions of contour model") self.update_progress_bar(text="Reversing directions of contour model")
progress_callback = pycam.Utils.ProgressCounter( self.model.reverse_directions(callback=self.update_progress_bar)
len(self.model.get_polygons()),
self.update_progress_bar).increment
self.model.reverse_directions(callback=progress_callback)
self.update_support_model() self.update_support_model()
@progress_activity_guard @progress_activity_guard
......
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