Commit 17acd806 authored by sumpfralle's avatar sumpfralle

initial work on a spherical extrusion feature (not finished -> hidden)


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@1040 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent e1819917
...@@ -990,7 +990,6 @@ ...@@ -990,7 +990,6 @@
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">2</property> <property name="padding">2</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
...@@ -1006,9 +1005,22 @@ This usually fixes inconsistent polygon winding states ...@@ -1006,9 +1005,22 @@ This usually fixes inconsistent polygon winding states
caused by careless DXF/SVG exporting programs.</property> 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>
<object class="GtkButton" id="ExtrudeSphereButton">
<property name="label" translatable="yes">Extrude (sphere)</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="no_show_all">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
<child> <child>
<object class="GtkButton" id="Projection2D"> <object class="GtkButton" id="Projection2D">
<property name="label" translatable="yes">2D Projection</property> <property name="label" translatable="yes">2D Projection</property>
...@@ -1018,7 +1030,7 @@ caused by careless DXF/SVG exporting programs.</property> ...@@ -1018,7 +1030,7 @@ caused by careless DXF/SVG exporting programs.</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="position">2</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -1030,7 +1042,7 @@ caused by careless DXF/SVG exporting programs.</property> ...@@ -1030,7 +1042,7 @@ caused by careless DXF/SVG exporting programs.</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="position">3</property> <property name="position">4</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -1042,7 +1054,7 @@ caused by careless DXF/SVG exporting programs.</property> ...@@ -1042,7 +1054,7 @@ caused by careless DXF/SVG exporting programs.</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="position">4</property> <property name="position">5</property>
</packing> </packing>
</child> </child>
</object> </object>
...@@ -1070,12 +1082,12 @@ caused by careless DXF/SVG exporting programs.</property> ...@@ -1070,12 +1082,12 @@ caused by careless DXF/SVG exporting programs.</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
......
...@@ -36,6 +36,7 @@ from pycam.Geometry import TransformableContainer ...@@ -36,6 +36,7 @@ from pycam.Geometry import TransformableContainer
from pycam.Utils import ProgressCounter from pycam.Utils import ProgressCounter
import pycam.Utils.log import pycam.Utils.log
import uuid import uuid
import math
log = pycam.Utils.log.get_logger() log = pycam.Utils.log.get_logger()
...@@ -692,3 +693,123 @@ class ContourModel(BaseModel): ...@@ -692,3 +693,123 @@ class ContourModel(BaseModel):
else: else:
return False return False
def extrude_to_sphere(self, height_factor=1.0, stepping=None, callback=None):
""" do a spherical extrusion of a 2D model.
This is mainly useful for extruding text in a visually pleasent way ...
BEWARE: currently not working correctly - see "calculate_point_height" below ...
"""
outer_polygons = [(poly, []) for poly in self._line_groups
if poly.is_outer()]
for poly in self._line_groups:
# ignore open polygons
if not poly.is_closed:
continue
if poly.is_outer():
continue
for outer_poly, children in outer_polygons:
if outer_poly == poly:
break
if outer_poly.is_polygon_inside(poly):
children.append(poly)
break
model = Model()
for poly, children in outer_polygons:
if callback and callback():
return None
group = PolygonGroup(poly, children, callback=callback)
new_model = group.extrude_to_sphere(height_factor=height_factor,
stepping=stepping)
if new_model:
model += new_model
return model
class PolygonGroup(object):
def __init__(self, outer, inner_list, callback=None):
self.outer = outer
self.inner = inner_list
self.callback = callback
self.lines = outer.get_lines()
for poly in inner_list:
self.lines.extend(poly.get_lines())
def extrude_to_sphere(self, height_factor=1.0, stepping=None):
if stepping is None:
stepping = min(self.outer.maxx - self.outer.minx,
self.outer.maxy - self.outer.miny) / 50
grid = []
for line in self._get_grid_matrix(stepping=stepping):
line_points = []
for x, y in line:
z = self.calculate_point_height(x, y, height_factor)
if z is None:
line_points.append(None)
else:
line_points.append(Point(x, y, z))
if self.callback:
self.callback()
grid.append(line_points)
# calculate the triangles within the grid
model = Model()
for line in range(len(grid) - 1):
for row in range(len(grid[0]) - 1):
p1 = grid[line][row]
p2 = grid[line][row + 1]
p3 = grid[line + 1][row]
p4 = grid[line + 1][row + 1]
if p1 and p2 and p3:
model.append(Triangle(p1, p2, p3))
if p2 and p4 and p3:
model.append(Triangle(p2, p4, p3))
# TODO: the connections between the outline and these triangles needs to be done
return model
def _get_grid_matrix(self, stepping):
x_dim = self.outer.maxx - self.outer.minx
y_dim = self.outer.maxy - self.outer.miny
x_points_num = int(max(4, math.ceil(x_dim / stepping)))
y_points_num = int(max(4, math.ceil(y_dim / stepping)))
x_step = x_dim / x_points_num
y_step = y_dim / y_points_num
grid = []
for x_index in range(x_points_num):
line = []
for y_index in range(y_points_num):
x_value = self.outer.minx + x_index * x_step + x_step / 2
y_value = self.outer.miny + y_index * y_step + y_step / 2
line.append((x_value, y_value))
grid.append(line)
return grid
def calculate_point_height(self, x, y, height_factor):
point = Point(x, y, self.outer.minz)
if not self.outer.is_point_inside(point):
return None
for poly in self.inner:
if poly.is_point_inside(point):
return None
point = Point(x, y, self.outer.minz)
line_distances = []
for line in self.lines:
if line.dir.cross(point.sub(line.p1)).z > 0:
# TODO: this is currently somehow broken
close_point = line.closest_point(point)
if not line.is_point_inside(close_point):
continue
dist = line.dist_to_point(point)
direction = close_point.sub(point)
line_distances.append((dist, direction))
line_distances.sort(key=lambda item: item[0])
dist1, vector1 = line_distances.pop(0)
for dist2, vector2 in line_distances:
if vector1.dot(vector2) <= 0:
break
else:
# no suitable line found
return None
radius = (dist1 + dist2) / 2
min_dist = min(dist1, dist2)
angle = math.acos((radius - min_dist) / radius)
return height_factor * math.sin(angle) * radius
...@@ -575,6 +575,8 @@ class ProjectGui: ...@@ -575,6 +575,8 @@ class ProjectGui:
100 / 25.4, False) 100 / 25.4, False)
self.gui.get_object("Projection2D").connect("clicked", self.gui.get_object("Projection2D").connect("clicked",
self.projection_2d) self.projection_2d)
self.gui.get_object("ExtrudeSphereButton").connect("clicked",
self.extrude_sphere)
# support grid # support grid
support_grid_type_control = self.gui.get_object( support_grid_type_control = self.gui.get_object(
"SupportGridTypesControl") "SupportGridTypesControl")
...@@ -1187,7 +1189,9 @@ class ProjectGui: ...@@ -1187,7 +1189,9 @@ 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")
controls_2d = ("ToggleModelDirectionButton", "DirectionsGuessButton") controls_2d = ("ToggleModelDirectionButton", "DirectionsGuessButton",
# TODO: currently disabled: "ExtrudeSphereButton",
)
for control in controls_2d: for control in controls_2d:
if is_reversible: if is_reversible:
self.gui.get_object(control).show() self.gui.get_object(control).show()
...@@ -2871,6 +2875,14 @@ class ProjectGui: ...@@ -2871,6 +2875,14 @@ class ProjectGui:
plane_z = 0 plane_z = 0
return Plane(Point(0, 0, plane_z), Vector(0, 0, 1)) return Plane(Point(0, 0, plane_z), Vector(0, 0, 1))
@progress_activity_guard
@gui_activity_guard
def extrude_sphere(self, widget=None):
self.update_progress_bar("Calculating spherical extrusion")
model = self.model.extrude_to_sphere(callback=self.update_progress_bar)
if model:
self.load_model(model)
@progress_activity_guard @progress_activity_guard
@gui_activity_guard @gui_activity_guard
def projection_2d(self, widget=None): def projection_2d(self, widget=None):
......
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