Commit 263a23f0 authored by sumpfralle's avatar sumpfralle

unify min/max properties for geometry objects

calculate all properties during instanciation (not on demand)


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@561 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent d1e456b7
...@@ -39,10 +39,10 @@ class BaseCutter(object): ...@@ -39,10 +39,10 @@ class BaseCutter(object):
self.id = BaseCutter.id self.id = BaseCutter.id
BaseCutter.id += 1 BaseCutter.id += 1
self.radius = radius self.radius = radius
self.radiussq = radius*radius self.radiussq = radius ** 2
self.required_distance = 0 self.required_distance = 0
self.distance_radius = self.radius self.distance_radius = self.radius
self.distance_radiussq = self.distance_radius * self.distance_radius self.distance_radiussq = self.distance_radius ** 2
# self.minx, self.maxx, self.miny and self.maxy are defined as # self.minx, self.maxx, self.miny and self.maxy are defined as
# properties below # properties below
self.shape = {} self.shape = {}
...@@ -98,20 +98,20 @@ class BaseCutter(object): ...@@ -98,20 +98,20 @@ class BaseCutter(object):
def drop(self, triangle): def drop(self, triangle):
# check bounding box collision # check bounding box collision
if self.minx > triangle.maxx(): if self.minx > triangle.maxx:
return None return None
if self.maxx < triangle.minx(): if self.maxx < triangle.minx:
return None return None
if self.miny > triangle.maxy(): if self.miny > triangle.maxy:
return None return None
if self.maxy < triangle.miny(): if self.maxy < triangle.miny:
return None return None
# check bounding circle collision # check bounding circle collision
c = triangle.center() c = triangle.center
if (c.x - self.location.x) ** 2 + (c.y - self.location.y) ** 2 \ if (c.x - self.location.x) ** 2 + (c.y - self.location.y) ** 2 \
> (self.distance_radiussq + 2 * self.distance_radius \ > (self.distance_radiussq + 2 * self.distance_radius \
* triangle.radius() + triangle.radiussq()): * triangle.radius + triangle.radiussq):
return None return None
(cl, d)= self.intersect(BaseCutter.vertical, triangle) (cl, d)= self.intersect(BaseCutter.vertical, triangle)
...@@ -121,22 +121,22 @@ class BaseCutter(object): ...@@ -121,22 +121,22 @@ class BaseCutter(object):
""" TODO: this function is never used - remove it? """ """ TODO: this function is never used - remove it? """
# check bounding box collision # check bounding box collision
if dx == 0: if dx == 0:
if self.miny > triangle.maxy(): if self.miny > triangle.maxy:
return None return None
if self.maxy < triangle.miny(): if self.maxy < triangle.miny:
return None return None
if dy == 0: if dy == 0:
if self.minx > triangle.maxx(): if self.minx > triangle.maxx:
return None return None
if self.maxx < triangle.minx(): if self.maxx < triangle.minx:
return None return None
if triangle.maxz() < self.location.z: if triangle.maxz < self.location.z:
return None return None
# check bounding sphere collision # check bounding sphere collision
c = triangle.center() c = triangle.center
d = (c.x - self.location.x) * dy -(c.y - self.location.y) * dx d = (c.x - self.location.x) * dy -(c.y - self.location.y) * dx
t = self.radius + triangle.radius() t = self.radius + triangle.radius
if abs(d) > t: if abs(d) > t:
return None return None
......
...@@ -175,8 +175,8 @@ class CylindricalCutter(BaseCutter): ...@@ -175,8 +175,8 @@ class CylindricalCutter(BaseCutter):
(cl, ccp, cp, l) = self.intersect_circle_line(direction, edge) (cl, ccp, cp, l) = self.intersect_circle_line(direction, edge)
if cp: if cp:
# check if the contact point is between the endpoints # check if the contact point is between the endpoints
m = cp.sub(edge.p1).dot(edge.dir()) m = cp.sub(edge.p1).dot(edge.dir)
if (m < 0) or (m > edge.len()): if (m < 0) or (m > edge.len):
return (None, INFINITE) return (None, INFINITE)
return (cl, l) return (cl, l)
...@@ -208,8 +208,8 @@ class CylindricalCutter(BaseCutter): ...@@ -208,8 +208,8 @@ class CylindricalCutter(BaseCutter):
(cl, ccp, cp, l) = self.intersect_cylinder_line(direction, edge) (cl, ccp, cp, l) = self.intersect_cylinder_line(direction, edge)
if not ccp: if not ccp:
return (None, INFINITE) return (None, INFINITE)
m = cp.sub(edge.p1).dot(edge.dir()) m = cp.sub(edge.p1).dot(edge.dir)
if (m < 0) or (m > edge.len()): if (m < 0) or (m > edge.len):
return (None, INFINITE) return (None, INFINITE)
if ccp.z < self.center.z: if ccp.z < self.center.z:
return (None, INFINITE) return (None, INFINITE)
......
...@@ -188,7 +188,7 @@ class SphericalCutter(BaseCutter): ...@@ -188,7 +188,7 @@ class SphericalCutter(BaseCutter):
# check if the contact point is between the endpoints # check if the contact point is between the endpoints
d = edge.p2.sub(edge.p1) d = edge.p2.sub(edge.p1)
m = cp.sub(edge.p1).dot(d) m = cp.sub(edge.p1).dot(d)
if (m < 0) or (m > d.normsq()): if (m < 0) or (m > d.normsq):
return (None, INFINITE) return (None, INFINITE)
return (cl, l) return (cl, l)
...@@ -220,8 +220,8 @@ class SphericalCutter(BaseCutter): ...@@ -220,8 +220,8 @@ class SphericalCutter(BaseCutter):
(cl, ccp, cp, l) = self.intersect_cylinder_line(direction, edge) (cl, ccp, cp, l) = self.intersect_cylinder_line(direction, edge)
if not ccp: if not ccp:
return (None, INFINITE) return (None, INFINITE)
m = cp.sub(edge.p1).dot(edge.dir()) m = cp.sub(edge.p1).dot(edge.dir)
if (m < 0) or (m > edge.len()): if (m < 0) or (m > edge.len):
return (None, INFINITE) return (None, INFINITE)
if ccp.z < self.center.z: if ccp.z < self.center.z:
return (None, INFINITE) return (None, INFINITE)
...@@ -299,7 +299,7 @@ class SphericalCutter(BaseCutter): ...@@ -299,7 +299,7 @@ class SphericalCutter(BaseCutter):
def drop_bis(self, triangle): def drop_bis(self, triangle):
""" TODO: this function is never called - remove it? """ """ TODO: this function is never called - remove it? """
n = triangle.normal() n = triangle.normal
if abs(n.dot(self.axis)) < epsilon: if abs(n.dot(self.axis)) < epsilon:
d = triangle.p1.sub(self.center).dot(n) d = triangle.p1.sub(self.center).dot(n)
if abs(d) >= self.radius - epsilon: if abs(d) >= self.radius - epsilon:
......
...@@ -151,7 +151,7 @@ class ToroidalCutter(BaseCutter): ...@@ -151,7 +151,7 @@ class ToroidalCutter(BaseCutter):
min_m = 0 min_m = 0
min_l = INFINITE min_l = INFINITE
min_cl = None min_cl = None
scale = int(edge.len() / self.distance_minorradius * 2) scale = int(edge.len / self.distance_minorradius * 2)
scale = max(3, scale) scale = max(3, scale)
for i in range(scale + 1): for i in range(scale + 1):
m = float(i) / scale m = float(i) / scale
...@@ -208,8 +208,8 @@ class ToroidalCutter(BaseCutter): ...@@ -208,8 +208,8 @@ class ToroidalCutter(BaseCutter):
if ccp and ccp.z < self.center.z: if ccp and ccp.z < self.center.z:
return (None, INFINITE) return (None, INFINITE)
if ccp: if ccp:
m = cp.sub(edge.p1).dot(edge.dir()) m = cp.sub(edge.p1).dot(edge.dir)
if (m < 0) or (m > edge.len()): if (m < 0) or (m > edge.len):
return (None, INFINITE) return (None, INFINITE)
return (cl, l) return (cl, l)
...@@ -254,8 +254,8 @@ class ToroidalCutter(BaseCutter): ...@@ -254,8 +254,8 @@ class ToroidalCutter(BaseCutter):
(cl, ccp, cp, l) = self.intersect_circle_line(direction, edge) (cl, ccp, cp, l) = self.intersect_circle_line(direction, edge)
if cp: if cp:
# check if the contact point is between the endpoints # check if the contact point is between the endpoints
m = cp.sub(edge.p1).dot(edge.dir()) m = cp.sub(edge.p1).dot(edge.dir)
if (m < 0) or (m > edge.len()): if (m < 0) or (m > edge.len):
return (None, INFINITE) return (None, INFINITE)
return (cl, l) return (cl, l)
......
...@@ -51,7 +51,7 @@ class STLExporter: ...@@ -51,7 +51,7 @@ class STLExporter:
for line in self.comment.split(self.linesep): for line in self.comment.split(self.linesep):
yield(";%s" % line) yield(";%s" % line)
for tr in self.model.triangles(): for tr in self.model.triangles():
norm = tr.normal().normalize() norm = tr.normal.normalized()
yield "facet normal %f %f %f" % (norm.x, norm.y, norm.z) yield "facet normal %f %f %f" % (norm.x, norm.y, norm.z)
yield " outer loop" yield " outer loop"
for p in (tr.p1, tr.p2, tr.p3): for p in (tr.p1, tr.p2, tr.p3):
......
...@@ -43,8 +43,7 @@ class Line(TransformableContainer): ...@@ -43,8 +43,7 @@ class Line(TransformableContainer):
Line.id += 1 Line.id += 1
self.p1 = p1 self.p1 = p1
self.p2 = p2 self.p2 = p2
self._dir = None self.reset_cache()
self._len = None
def __repr__(self): def __repr__(self):
return "Line<%g,%g,%g>-<%g,%g,%g>" % (self.p1.x, self.p1.y, self.p1.z, return "Line<%g,%g,%g>-<%g,%g,%g>" % (self.p1.x, self.p1.y, self.p1.z,
...@@ -75,54 +74,31 @@ class Line(TransformableContainer): ...@@ -75,54 +74,31 @@ class Line(TransformableContainer):
return 2 return 2
def reset_cache(self): def reset_cache(self):
self._dir = None self.dir = self.p2.sub(self.p1).normalized()
self._len = None self.len = self.p2.sub(self.p1).norm
self.minx = min(self.p1.x, self.p2.x)
def dir(self): self.miny = min(self.p1.y, self.p2.y)
if self._dir is None: self.minz = min(self.p1.z, self.p2.z)
self._dir = self.p2.sub(self.p1) self.maxx = max(self.p1.x, self.p2.x)
self._dir.normalize() self.maxy = max(self.p1.y, self.p2.y)
return self._dir self.maxz = max(self.p1.z, self.p2.z)
def len(self):
if self._len is None:
self._len = self.p2.sub(self.p1).norm()
return self._len
def point(self, l): def point(self, l):
return self.p1.add(self.dir().mul(l*self.len())) return self.p1.add(self.dir.mul(l*self.len))
def closest_point(self, p): def closest_point(self, p):
v = self.dir() v = self.dir
l = self.p1.dot(v) - p.dot(v) l = self.p1.dot(v) - p.dot(v)
return self.p1.sub(v.mul(l)) return self.p1.sub(v.mul(l))
def dist_to_point_sq(self, p): def dist_to_point_sq(self, p):
return p.sub(self.closest_point(p)).normsq() return p.sub(self.closest_point(p)).normsq
def dist_to_point(self, p): def dist_to_point(self, p):
return math.sqrt(self.dist_to_point_sq(p)) return math.sqrt(self.dist_to_point_sq(p))
def is_point_in_line(self, p): def is_point_in_line(self, p):
return abs(p.sub(self.p1).norm() + p.sub(self.p2).norm() - self.len()) < epsilon return abs(p.sub(self.p1).norm + p.sub(self.p2).norm - self.len) < epsilon
def minx(self):
return min(self.p1.x, self.p2.x)
def miny(self):
return min(self.p1.y, self.p2.y)
def minz(self):
return min(self.p1.z, self.p2.z)
def maxx(self):
return max(self.p1.x, self.p2.x)
def maxy(self):
return max(self.p1.y, self.p2.y)
def maxz(self):
return max(self.p1.z, self.p2.z)
def to_OpenGL(self): def to_OpenGL(self):
if GL_enabled: if GL_enabled:
...@@ -130,7 +106,7 @@ class Line(TransformableContainer): ...@@ -130,7 +106,7 @@ class Line(TransformableContainer):
GL.glVertex3f(self.p1.x, self.p1.y, self.p1.z) GL.glVertex3f(self.p1.x, self.p1.y, self.p1.z)
GL.glVertex3f(self.p2.x, self.p2.y, self.p2.z) GL.glVertex3f(self.p2.x, self.p2.y, self.p2.z)
# (optional) draw arrows for visualizing the direction of each line # (optional) draw arrows for visualizing the direction of each line
if True: if False:
line = (self.p2.x - self.p1.x, self.p2.y - self.p1.y) line = (self.p2.x - self.p1.x, self.p2.y - self.p1.y)
if line[0] == 0: if line[0] == 0:
ortho = (1.0, 0.0) ortho = (1.0, 0.0)
...@@ -155,9 +131,6 @@ class Line(TransformableContainer): ...@@ -155,9 +131,6 @@ class Line(TransformableContainer):
GL.glVertex3f(self.p2.x, self.p2.y, self.p2.z) GL.glVertex3f(self.p2.x, self.p2.y, self.p2.z)
GL.glEnd() GL.glEnd()
def get_points(self):
return (self.p1, self.p2)
def get_intersection(self, line, infinite_lines=False): def get_intersection(self, line, infinite_lines=False):
""" Get the point of intersection between two lines. Intersections """ Get the point of intersection between two lines. Intersections
outside the length of these lines are ignored. outside the length of these lines are ignored.
...@@ -171,18 +144,18 @@ class Line(TransformableContainer): ...@@ -171,18 +144,18 @@ class Line(TransformableContainer):
c = x3.sub(x1) c = x3.sub(x1)
# see http://mathworld.wolfram.com/Line-LineIntersection.html (24) # see http://mathworld.wolfram.com/Line-LineIntersection.html (24)
try: try:
factor = c.cross(b).dot(a.cross(b)) / a.cross(b).normsq() factor = c.cross(b).dot(a.cross(b)) / a.cross(b).normsq
except ZeroDivisionError: except ZeroDivisionError:
# lines are parallel # lines are parallel
# check if they are _one_ line # check if they are _one_ line
if a.cross(c).normsq() != 0: if a.cross(c).norm != 0:
# the lines are parallel with a distance # the lines are parallel with a distance
return None, None return None, None
# the lines are on one straight # the lines are on one straight
if self.is_point_in_line(x3): if self.is_point_in_line(x3):
return x3, a.len() / c.len() return x3, a.len / c.len
elif self.is_point_in_line(x4): elif self.is_point_in_line(x4):
return x4, a.len() / line.p2.sub(self.p1).len() return x4, a.len / line.p2.sub(self.p1).len
elif line.is_point_in_line(x1): elif line.is_point_in_line(x1):
return x1, 0 return x1, 0
elif line.is_point_in_line(x2): elif line.is_point_in_line(x2):
...@@ -206,13 +179,13 @@ class Line(TransformableContainer): ...@@ -206,13 +179,13 @@ class Line(TransformableContainer):
return None, None return None, None
def get_cropped_line(self, minx, maxx, miny, maxy, minz, maxz): def get_cropped_line(self, minx, maxx, miny, maxy, minz, maxz):
if (minx <= self.minx() <= self.maxx() <= maxx) \ if (minx <= self.minx <= self.maxx <= maxx) \
and (miny <= self.miny() <= self.maxy() <= maxy) \ and (miny <= self.miny <= self.maxy <= maxy) \
and (minz <= self.minz() <= self.maxz() <= maxz): and (minz <= self.minz <= self.maxz <= maxz):
return Line(line.p1, line.p2) return Line(line.p1, line.p2)
elif (maxx < self.minx()) or (self.maxx() < minx) \ elif (maxx < self.minx) or (self.maxx < minx) \
or (maxy < self.miny()) or (self.maxy() < miny) \ or (maxy < self.miny) or (self.maxy < miny) \
or (maxz < self.minz()) or (self.maxz() < minz): or (maxz < self.minz) or (self.maxz < minz):
return None return None
else: else:
# the line needs to be cropped # the line needs to be cropped
...@@ -228,11 +201,11 @@ class Line(TransformableContainer): ...@@ -228,11 +201,11 @@ class Line(TransformableContainer):
Plane(maxp, Point(0, 0, 1)), Plane(maxp, Point(0, 0, 1)),
] ]
# calculate all intersections # calculate all intersections
intersections = [plane.intersect_point(self.dir(), self.p1) intersections = [plane.intersect_point(self.dir, self.p1)
for plane in planes] for plane in planes]
# remove all intersections outside the box and outside the line # remove all intersections outside the box and outside the line
valid_intersections = [(cp, dist) for cp, dist in intersections valid_intersections = [(cp, dist) for cp, dist in intersections
if cp and (0 <= dist <= self.len()) \ if cp and (0 <= dist <= self.len) \
and (minx <= cp.x <= maxx) \ and (minx <= cp.x <= maxx) \
and (miny <= cp.y <= maxy) \ and (miny <= cp.y <= maxy) \
and (minz <= cp.z <= maxz)] and (minz <= cp.z <= maxz)]
...@@ -317,7 +290,7 @@ class LineGroup(TransformableContainer): ...@@ -317,7 +290,7 @@ class LineGroup(TransformableContainer):
offset_matrix = self.get_offset_matrix() offset_matrix = self.get_offset_matrix()
# initialize all offset vectors (if necessary) # initialize all offset vectors (if necessary)
for line in self._lines: for line in self._lines:
line_dir = line.dir() line_dir = line.dir
vector = (line_dir.x, line_dir.y, line_dir.z) vector = (line_dir.x, line_dir.y, line_dir.z)
offset_vector = Matrix.multiply_vector_matrix(vector, offset_vector = Matrix.multiply_vector_matrix(vector,
offset_matrix) offset_matrix)
...@@ -409,21 +382,21 @@ class LineGroup(TransformableContainer): ...@@ -409,21 +382,21 @@ class LineGroup(TransformableContainer):
offset_matrix = None offset_matrix = None
# check if all lines are in one specific layer (z/y/x) # check if all lines are in one specific layer (z/y/x)
# return the respective axis rotation matrix # return the respective axis rotation matrix
z_start_value = self._lines[0].minz() z_start_value = self._lines[0].minz
on_z_level = [True for line in self._lines on_z_level = [True for line in self._lines
if line.minz() == line.maxz() == z_start_value] if line.minz == line.maxz == z_start_value]
if len(on_z_level) == len(self._lines): if len(on_z_level) == len(self._lines):
offset_matrix = Matrix.TRANSFORMATIONS["z"] offset_matrix = Matrix.TRANSFORMATIONS["z"]
else: else:
y_start_value = self._lines[0].y y_start_value = self._lines[0].y
on_y_level = [True for line in self._lines on_y_level = [True for line in self._lines
if line.miny() == line.maxy() == y_start_value] if line.miny == line.maxy == y_start_value]
if len(on_y_level) == len(self._lines): if len(on_y_level) == len(self._lines):
offset_matrix = Matrix.TRANSFORMATIONS["y"] offset_matrix = Matrix.TRANSFORMATIONS["y"]
else: else:
x_start_value = self._lines[0].x x_start_value = self._lines[0].x
on_x_level = [True for line in self._lines on_x_level = [True for line in self._lines
if line.minx() == line.maxx() == x_start_value] if line.minx == line.maxx == x_start_value]
if len(on_x_level) == len(self._lines): if len(on_x_level) == len(self._lines):
offset_matrix = Matrix.TRANSFORMATIONS["x"] offset_matrix = Matrix.TRANSFORMATIONS["x"]
# store the result to avoid re-calculation # store the result to avoid re-calculation
...@@ -432,19 +405,19 @@ class LineGroup(TransformableContainer): ...@@ -432,19 +405,19 @@ class LineGroup(TransformableContainer):
def _update_limits(self, line): def _update_limits(self, line):
if self.minx is None: if self.minx is None:
self.minx = line.minx() self.minx = line.minx
self.maxx = line.maxx() self.maxx = line.maxx
self.miny = line.miny() self.miny = line.miny
self.maxy = line.maxy() self.maxy = line.maxy
self.minz = line.minz() self.minz = line.minz
self.maxz = line.maxz() self.maxz = line.maxz
else: else:
self.minx = min(self.minx, line.minx()) self.minx = min(self.minx, line.minx)
self.maxx = max(self.maxx, line.maxx()) self.maxx = max(self.maxx, line.maxx)
self.miny = min(self.miny, line.miny()) self.miny = min(self.miny, line.miny)
self.maxy = max(self.maxy, line.maxy()) self.maxy = max(self.maxy, line.maxy)
self.minz = min(self.minz, line.minz()) self.minz = min(self.minz, line.minz)
self.maxz = max(self.maxz, line.maxz()) self.maxz = max(self.maxz, line.maxz)
def reset_cache(self): def reset_cache(self):
if not self._lines: if not self._lines:
...@@ -453,12 +426,12 @@ class LineGroup(TransformableContainer): ...@@ -453,12 +426,12 @@ class LineGroup(TransformableContainer):
else: else:
first = self._lines[0] first = self._lines[0]
# initialize the start limit with valid values # initialize the start limit with valid values
self.minx = first.minx() self.minx = first.minx
self.maxx = first.maxx() self.maxx = first.maxx
self.miny = first.miny() self.miny = first.miny
self.maxy = first.maxy() self.maxy = first.maxy
self.minz = first.minz() self.minz = first.minz
self.maxz = first.maxz() self.maxz = first.maxz
# update the limit for each line # update the limit for each line
for line in self._lines: for line in self._lines:
self._update_limits(line) self._update_limits(line)
...@@ -471,17 +444,15 @@ class LineGroup(TransformableContainer): ...@@ -471,17 +444,15 @@ class LineGroup(TransformableContainer):
l2 = self._lines[index] l2 = self._lines[index]
skel_p = Point((l1.p1.x + l2.p2.x) / 2.0, (l1.p1.y + l2.p2.y) / 2.0, skel_p = Point((l1.p1.x + l2.p2.x) / 2.0, (l1.p1.y + l2.p2.y) / 2.0,
(l1.p1.z + l2.p2.z) / 2.0) (l1.p1.z + l2.p2.z) / 2.0)
skel_dir = skel_p.sub(l1.p2) skel_dir = skel_p.sub(l1.p2).normalized()
skel_up_vector = skel_dir.cross(l1.dir()) skel_up_vector = skel_dir.cross(l1.dir)
offset_line = self._line_offsets[index - 1] offset_line = self._line_offsets[index - 1]
offset_up_vector = offset_line.cross(l1.dir()) offset_up_vector = offset_line.cross(l1.dir)
# TODO: check for other axis as well # TODO: check for other axis as well
if offset_up_vector.z * skel_up_vector.z < 0: if offset_up_vector.z * skel_up_vector.z < 0:
# reverse the skeleton vector to point outwards # reverse the skeleton vector to point outwards
skel_dir = skel_dir.mul(-1) skel_dir = skel_dir.mul(-1)
skel_dir.normalize()
skeletion.append(skel_dir) skeletion.append(skel_dir)
return skeleton return skeleton
def get_offset_line_groups(self, offset): def get_offset_line_groups(self, offset):
...@@ -489,13 +460,13 @@ class LineGroup(TransformableContainer): ...@@ -489,13 +460,13 @@ class LineGroup(TransformableContainer):
if offset == 0: if offset == 0:
return Line(line.p1, line.p2) return Line(line.p1, line.p2)
else: else:
cross_offset = line_offset.dir().mul(offset) cross_offset = line_offset.dir.mul(offset)
# Prolong the line at the beginning and at the end - to allow # Prolong the line at the beginning and at the end - to allow
# overlaps. Use factor "2" to take care for star-like structure # overlaps. Use factor "2" to take care for star-like structure
# where a complete convex triangle would get cropped (two lines # where a complete convex triangle would get cropped (two lines
# get lost instead of just one). Use the "abs" value to # get lost instead of just one). Use the "abs" value to
# compensate negative offsets. # compensate negative offsets.
in_line = line.dir().mul(2 * abs(offset)) in_line = line.dir.mul(2 * abs(offset))
return Line(line.p1.add(cross_offset).sub(in_line), return Line(line.p1.add(cross_offset).sub(in_line),
line.p2.add(cross_offset).add(in_line)) line.p2.add(cross_offset).add(in_line))
def do_lines_intersection(l1, l2): def do_lines_intersection(l1, l2):
...@@ -514,7 +485,7 @@ class LineGroup(TransformableContainer): ...@@ -514,7 +485,7 @@ class LineGroup(TransformableContainer):
c = x3.sub(x1) c = x3.sub(x1)
# see http://mathworld.wolfram.com/Line-LineIntersection.html (24) # see http://mathworld.wolfram.com/Line-LineIntersection.html (24)
try: try:
factor = c.cross(b).dot(a.cross(b)) / a.cross(b).normsq() factor = c.cross(b).dot(a.cross(b)) / a.cross(b).normsq
except ZeroDivisionError: except ZeroDivisionError:
l2.p1 = None l2.p1 = None
return return
...@@ -523,13 +494,13 @@ class LineGroup(TransformableContainer): ...@@ -523,13 +494,13 @@ class LineGroup(TransformableContainer):
l2.p1 = None l2.p1 = None
else: else:
intersection = x1.add(a.mul(factor)) intersection = x1.add(a.mul(factor))
if Line(l1.p1, intersection).dir() != l1.dir(): if Line(l1.p1, intersection).dir != l1.dir:
# Remove lines that would change their direction due to the # Remove lines that would change their direction due to the
# new intersection. These are usually lines that become # new intersection. These are usually lines that become
# obsolete due to a more favourable intersection of the two # obsolete due to a more favourable intersection of the two
# neighbouring lines. This appears at small corners. # neighbouring lines. This appears at small corners.
l1.p1 = None l1.p1 = None
elif Line(intersection, l2.p2).dir() != l2.dir(): elif Line(intersection, l2.p2).dir != l2.dir:
# see comment above # see comment above
l2.p1 = None l2.p1 = None
elif l1.p1 == intersection: elif l1.p1 == intersection:
...@@ -687,9 +658,9 @@ class LineGroup(TransformableContainer): ...@@ -687,9 +658,9 @@ class LineGroup(TransformableContainer):
new_groups = [] new_groups = []
for line in self._lines: for line in self._lines:
new_line = None new_line = None
if (minx <= line.minx() <= line.maxx() <= maxx) \ if (minx <= line.minx <= line.maxx <= maxx) \
and (miny <= line.miny() <= line.maxy() <= maxy) \ and (miny <= line.miny <= line.maxy <= maxy) \
and (minz <= line.minz() <= line.maxz() <= maxz): and (minz <= line.minz <= line.maxz <= maxz):
new_line = line new_line = line
else: else:
cropped_line = line.get_cropped_line(minx, maxx, miny, maxy, cropped_line = line.get_cropped_line(minx, maxx, miny, maxy,
......
...@@ -121,8 +121,7 @@ def get_rotation_matrix_from_to(v_orig, v_dest): ...@@ -121,8 +121,7 @@ def get_rotation_matrix_from_to(v_orig, v_dest):
# destination vectors. # destination vectors.
rot_axis = Point(v_orig[1] * v_dest[2] - v_orig[2] * v_dest[1], rot_axis = Point(v_orig[1] * v_dest[2] - v_orig[2] * v_dest[1],
v_orig[2] * v_dest[0] - v_orig[0] * v_dest[2], v_orig[2] * v_dest[0] - v_orig[0] * v_dest[2],
v_orig[0] * v_dest[1] - v_orig[1] * v_dest[0]) v_orig[0] * v_dest[1] - v_orig[1] * v_dest[0]).normalized()
rot_axis.normalize()
# get the rotation matrix # get the rotation matrix
# see http://www.fastgraph.com/makegames/3drotation/ # see http://www.fastgraph.com/makegames/3drotation/
c = math.cos(rot_angle) c = math.cos(rot_angle)
......
...@@ -92,26 +92,20 @@ class BaseModel(TransformableContainer): ...@@ -92,26 +92,20 @@ class BaseModel(TransformableContainer):
+ "support the 'export' function.") % str(type(self))) + "support the 'export' function.") % str(type(self)))
def _update_limits(self, item): def _update_limits(self, item):
if callable(item.minx):
minx, miny, minz = item.minx(), item.miny(), item.minz()
maxx, maxy, maxz = item.maxx(), item.maxy(), item.maxz()
else:
minx, miny, minz = item.minx, item.miny, item.minz
maxx, maxy, maxz = item.maxx, item.maxy, item.maxz
if self.minx is None: if self.minx is None:
self.minx = minx self.minx = item.minx
self.miny = miny self.miny = item.miny
self.minz = minz self.minz = item.minz
self.maxx = maxx self.maxx = item.maxx
self.maxy = maxy self.maxy = item.maxy
self.maxz = maxz self.maxz = item.maxz
else: else:
self.minx = min(self.minx, minx) self.minx = min(self.minx, item.minx)
self.miny = min(self.miny, miny) self.miny = min(self.miny, item.miny)
self.minz = min(self.minz, minz) self.minz = min(self.minz, item.minz)
self.maxx = max(self.maxx, maxx) self.maxx = max(self.maxx, item.maxx)
self.maxy = max(self.maxy, maxy) self.maxy = max(self.maxy, item.maxy)
self.maxz = max(self.maxz, maxz) self.maxz = max(self.maxz, item.maxz)
def append(self, item): def append(self, item):
self._update_limits(item) self._update_limits(item)
......
...@@ -35,9 +35,9 @@ class Plane: ...@@ -35,9 +35,9 @@ class Plane:
return "Plane<%s,%s>" % (self.p, self.n) return "Plane<%s,%s>" % (self.p, self.n)
def intersect_point(self, direction, point): def intersect_point(self, direction, point):
if direction.norm() != 1: if direction.norm != 1:
# calculations will go wrong, if the direction is not a unit vector # calculations will go wrong, if the direction is not a unit vector
direction = Point(direction.x, direction.y, direction.z).normalize() direction = Point(direction.x, direction.y, direction.z).normalized()
denom = self.n.dot(direction) denom = self.n.dot(direction)
if denom == 0: if denom == 0:
return (None, INFINITE) return (None, INFINITE)
......
...@@ -36,8 +36,7 @@ class Point: ...@@ -36,8 +36,7 @@ class Point:
self.x = float(x) self.x = float(x)
self.y = float(y) self.y = float(y)
self.z = float(z) self.z = float(z)
self._norm = None self.reset_cache()
self._normsq = None
def __repr__(self): def __repr__(self):
return "Point%d<%g,%g,%g>" % (self.id, self.x, self.y, self.z) return "Point%d<%g,%g,%g>" % (self.id, self.x, self.y, self.z)
...@@ -74,13 +73,14 @@ class Point: ...@@ -74,13 +73,14 @@ class Point:
self.reset_cache() self.reset_cache()
def reset_cache(self): def reset_cache(self):
self._norm = None self.normsq = self.dot(self)
self._normsq = None self.norm = math.sqrt(self.normsq)
def mul(self, c): def mul(self, c):
return Point(self.x * c, self.y * c, self.z * c) return Point(self.x * c, self.y * c, self.z * c)
def div(self, c): def div(self, c):
c = float(c)
return Point(self.x / c, self.y / c, self.z / c) return Point(self.x / c, self.y / c, self.z / c)
def add(self, p): def add(self, p):
...@@ -96,23 +96,10 @@ class Point: ...@@ -96,23 +96,10 @@ class Point:
return Point(self.y * p.z - p.y * self.z, p.x * self.z - self.x * p.z, return Point(self.y * p.z - p.y * self.z, p.x * self.z - self.x * p.z,
self.x * p.y - p.x * self.y) self.x * p.y - p.x * self.y)
def normsq(self): def normalized(self):
if self._normsq is None: n = self.norm
self._normsq = self.dot(self) if n == 0:
return self._normsq return None
else:
def norm(self): return Point(self.x / n, self.y / n, self.z / n)
if self._norm is None:
self._norm = math.sqrt(self.normsq())
return self._norm
def normalize(self):
n = self.norm()
if n != 0:
self.x /= n
self.y /= n
self.z /= n
self._norm = 1.0
self._normsq = 1.0
return self
...@@ -59,18 +59,48 @@ class Triangle(TransformableContainer): ...@@ -59,18 +59,48 @@ class Triangle(TransformableContainer):
self.e3 = Line(p3, p1) self.e3 = Line(p3, p1)
else: else:
self.e3 = e3 self.e3 = e3
self._normal = n self.normal = n
self._minx = None self.reset_cache()
self._miny = None
self._minz = None def reset_cache(self):
self._maxx = None self.minx = min(self.p1.x, self.p2.x, self.p3.x)
self._maxy = None self.miny = min(self.p1.y, self.p2.y, self.p3.y)
self._maxz = None self.minz = min(self.p1.z, self.p2.z, self.p3.z)
self._center = None self.maxx = max(self.p1.x, self.p2.x, self.p3.x)
self._middle = None self.maxy = max(self.p1.y, self.p2.y, self.p3.y)
self._radius = None self.maxz = max(self.p1.z, self.p2.z, self.p3.z)
self._radiussq = None # calculate normal, if p1-p2-pe are in clockwise order
self._plane = None if self.normal is None:
self.normal = self.p3.sub(self.p1).cross(self.p2.sub( \
self.p1)).normalized()
self.center = self.p1.add(self.p2).add(self.p3).div(3)
self.plane = Plane(self.center, self.normal)
# calculate circumcircle (resulting in radius and middle)
denom = self.p2.sub(self.p1).cross(self.p3.sub(self.p2)).norm
self.radius = (self.p2.sub(self.p1).norm \
* self.p3.sub(self.p2).norm * self.p3.sub(self.p1).norm) \
/ (2 * denom)
self.radiussq = self.radius ** 2
def get_middle(self):
""" this function is only used for a special debug case of "to_OpenGL".
Thus we don't need to calculate this value for every triangle.
"""
if hasattr(self, "_middle"):
return self._middle
denom = self.p2.sub(self.p1).cross(self.p3.sub(self.p2)).norm
denom2 = 2 * denom * denom
alpha = self.p3.sub(self.p2).normsq \
* self.p1.sub(self.p2).dot(self.p1.sub(self.p3)) / denom2
beta = self.p1.sub(self.p3).normsq \
* self.p2.sub(self.p1).dot(self.p2.sub(self.p3)) / denom2
gamma = self.p1.sub(self.p2).normsq \
* self.p3.sub(self.p1).dot(self.p3.sub(self.p2)) / denom2
self._middle = Point(
self.p1.x * alpha + self.p2.x * beta + self.p3.x * gamma,
self.p1.y * alpha + self.p2.y * beta + self.p3.y * gamma,
self.p1.z * alpha + self.p2.z * beta + self.p3.z * gamma)
return self._middle
def __repr__(self): def __repr__(self):
return "Triangle%d<%s,%s,%s>" % (self.id, self.p1, self.p2, self.p3) return "Triangle%d<%s,%s,%s>" % (self.id, self.p1, self.p2, self.p3)
...@@ -85,55 +115,53 @@ class Triangle(TransformableContainer): ...@@ -85,55 +115,53 @@ class Triangle(TransformableContainer):
return 3 return 3
def transform_by_matrix(self, matrix, transformed_list=None, **kwargs): def transform_by_matrix(self, matrix, transformed_list=None, **kwargs):
previous_normal = self._normal previous_normal = self.normal
super(Triangle, self).transform_by_matrix(matrix, transformed_list, super(Triangle, self).transform_by_matrix(matrix, transformed_list,
**kwargs) **kwargs)
# try to keep the original normal vector (transform it manually) # try to keep the original normal vector (transform it manually)
if not previous_normal is None: if not previous_normal is None:
previous_normal.transform_by_matrix(matrix, **kwargs) previous_normal.transform_by_matrix(matrix, **kwargs)
self._normal = previous_normal self.normal = previous_normal
def name(self):
return "triangle%d" % self.id
def to_OpenGL(self): def to_OpenGL(self):
if not GL_enabled: if not GL_enabled:
return return
GL.glBegin(GL.GL_TRIANGLES) GL.glBegin(GL.GL_TRIANGLES)
# use normals to improve lighting (contributed by imyrek) # use normals to improve lighting (contributed by imyrek)
normal_t = self.normal() normal_t = self.normal
GL.glNormal3f(normal_t.x, normal_t.y, normal_t.z) GL.glNormal3f(normal_t.x, normal_t.y, normal_t.z)
GL.glVertex3f(self.p1.x, self.p1.y, self.p1.z) GL.glVertex3f(self.p1.x, self.p1.y, self.p1.z)
GL.glVertex3f(self.p2.x, self.p2.y, self.p2.z) GL.glVertex3f(self.p2.x, self.p2.y, self.p2.z)
GL.glVertex3f(self.p3.x, self.p3.y, self.p3.z) GL.glVertex3f(self.p3.x, self.p3.y, self.p3.z)
GL.glEnd() GL.glEnd()
if False: # display surface normals if False: # display surface normals
n = self.normal() n = self.normal
c = self.center() c = self.center
d = 0.5 d = 0.5
GL.glBegin(GL.GL_LINES) GL.glBegin(GL.GL_LINES)
GL.glVertex3f(c.x, c.y, c.z) GL.glVertex3f(c.x, c.y, c.z)
GL.glVertex3f(c.x+n.x*d, c.y+n.y*d, c.z+n.z*d) GL.glVertex3f(c.x+n.x*d, c.y+n.y*d, c.z+n.z*d)
GL.glEnd() GL.glEnd()
if False and hasattr(self, "_middle"): # display bounding sphere if False: # display bounding sphere
GL.glPushMatrix() GL.glPushMatrix()
GL.glTranslate(self._middle.x, self._middle.y, self._middle.z) middle = self.get_middle()
GL.glTranslate(middle.x, middle.y, middle.z)
if not hasattr(self, "_sphere"): if not hasattr(self, "_sphere"):
self._sphere = GLU.gluNewQuadric() self._sphere = GLU.gluNewQuadric()
GLU.gluSphere(self._sphere, self._radius, 10, 10) GLU.gluSphere(self._sphere, self.radius, 10, 10)
GL.glPopMatrix() GL.glPopMatrix()
if False: # draw triangle id on triangle face if False: # draw triangle id on triangle face
GL.glPushMatrix() GL.glPushMatrix()
cc = GL.glGetFloatv(GL.GL_CURRENT_COLOR) cc = GL.glGetFloatv(GL.GL_CURRENT_COLOR)
c = self.center() c = self.center
GL.glTranslate(c.x, c.y, c.z) GL.glTranslate(c.x, c.y, c.z)
p12 = self.p1.add(self.p2).mul(0.5) p12 = self.p1.add(self.p2).mul(0.5)
p3_12 = self.p3.sub(p12).normalize() p3_12 = self.p3.sub(p12).normalized()
p2_1 = self.p1.sub(self.p2).normalize() p2_1 = self.p1.sub(self.p2).normalized()
pn = p2_1.cross(p3_12) pn = p2_1.cross(p3_12)
GL.glMultMatrixf((p2_1.x, p2_1.y, p2_1.z, 0, p3_12.x, p3_12.y, GL.glMultMatrixf((p2_1.x, p2_1.y, p2_1.z, 0, p3_12.x, p3_12.y,
p3_12.z, 0, pn.x, pn.y, pn.z, 0, 0, 0, 0, 1)) p3_12.z, 0, pn.x, pn.y, pn.z, 0, 0, 0, 0, 1))
n = self.normal().mul(0.01) n = self.normal.mul(0.01)
GL.glTranslatef(n.x, n.y, n.z) GL.glTranslatef(n.x, n.y, n.z)
GL.glScalef(0.003, 0.003, 0.003) GL.glScalef(0.003, 0.003, 0.003)
w = 0 w = 0
...@@ -145,15 +173,14 @@ class Triangle(TransformableContainer): ...@@ -145,15 +173,14 @@ class Triangle(TransformableContainer):
GLUT.glutStrokeCharacter(GLUT.GLUT_STROKE_ROMAN, ord(ch)) GLUT.glutStrokeCharacter(GLUT.GLUT_STROKE_ROMAN, ord(ch))
GL.glPopMatrix() GL.glPopMatrix()
GL.glColor4f(cc[0], cc[1], cc[2], cc[3]) GL.glColor4f(cc[0], cc[1], cc[2], cc[3])
if False: # draw point id on triangle face if False: # draw point id on triangle face
cc = GL.glGetFloatv(GL.GL_CURRENT_COLOR) cc = GL.glGetFloatv(GL.GL_CURRENT_COLOR)
c = self.center() c = self.center
p12 = self.p1.add(self.p2).mul(0.5) p12 = self.p1.add(self.p2).mul(0.5)
p3_12 = self.p3.sub(p12).normalize() p3_12 = self.p3.sub(p12).normalized()
p2_1 = self.p1.sub(self.p2).normalize() p2_1 = self.p1.sub(self.p2).normalized()
pn = p2_1.cross(p3_12) pn = p2_1.cross(p3_12)
n = self.normal().mul(0.01) n = self.normal.mul(0.01)
for p in (self.p1, self.p2, self.p3): for p in (self.p1, self.p2, self.p3):
GL.glPushMatrix() GL.glPushMatrix()
pp = p.sub(p.sub(c).mul(0.3)) pp = p.sub(p.sub(c).mul(0.3))
...@@ -172,34 +199,18 @@ class Triangle(TransformableContainer): ...@@ -172,34 +199,18 @@ class Triangle(TransformableContainer):
GL.glPopMatrix() GL.glPopMatrix()
GL.glColor4f(cc[0], cc[1], cc[2], cc[3]) GL.glColor4f(cc[0], cc[1], cc[2], cc[3])
def normal(self):
if self._normal is None:
# calculate normal, if p1-p2-pe are in clockwise order
vector = self.p3.sub(self.p1).cross(self.p2.sub(self.p1))
denom = vector.norm()
self._normal = vector.div(denom)
return self._normal
def plane(self):
if self._plane is None:
self._plane = Plane(self.center(), self.normal())
return self._plane
def point_inside(self, p): def point_inside(self, p):
# http://www.blackpawn.com/texts/pointinpoly/default.html # http://www.blackpawn.com/texts/pointinpoly/default.html
# Compute vectors # Compute vectors
v0 = self.p3.sub(self.p1) v0 = self.p3.sub(self.p1)
v1 = self.p2.sub(self.p1) v1 = self.p2.sub(self.p1)
v2 = p.sub(self.p1) v2 = p.sub(self.p1)
# Compute dot products # Compute dot products
dot00 = v0.dot(v0) dot00 = v0.dot(v0)
dot01 = v0.dot(v1) dot01 = v0.dot(v1)
dot02 = v0.dot(v2) dot02 = v0.dot(v2)
dot11 = v1.dot(v1) dot11 = v1.dot(v1)
dot12 = v1.dot(v2) dot12 = v1.dot(v2)
# Compute barycentric coordinates # Compute barycentric coordinates
denom = dot00 * dot11 - dot01 * dot01 denom = dot00 * dot11 - dot01 * dot01
# Originally, "u" and "v" are multiplied with "1/denom". # Originally, "u" and "v" are multiplied with "1/denom".
...@@ -207,81 +218,8 @@ class Triangle(TransformableContainer): ...@@ -207,81 +218,8 @@ class Triangle(TransformableContainer):
# "almost" invalid). # "almost" invalid).
u = dot11 * dot02 - dot01 * dot12 u = dot11 * dot02 - dot01 * dot12
v = dot00 * dot12 - dot01 * dot02 v = dot00 * dot12 - dot01 * dot02
# Check if point is in triangle # Check if point is in triangle
return ((u * denom) >= 0) and ((v * denom) >= 0) and (u + v <= denom) return ((u * denom) >= 0) and ((v * denom) >= 0) and (u + v <= denom)
def minx(self):
if self._minx is None:
self._minx = min(self.p1.x, self.p2.x, self.p3.x)
return self._minx
def miny(self):
if self._miny is None:
self._miny = min(self.p1.y, self.p2.y, self.p3.y)
return self._miny
def minz(self):
if self._minz is None:
self._minz = min(self.p1.z, self.p2.z, self.p3.z)
return self._minz
def maxx(self):
if self._maxx is None:
self._maxx = max(self.p1.x, self.p2.x, self.p3.x)
return self._maxx
def maxy(self):
if self._maxy is None:
self._maxy = max(self.p1.y, self.p2.y, self.p3.y)
return self._maxy
def maxz(self):
if self._maxz is None:
self._maxz = max(self.p1.z, self.p2.z, self.p3.z)
return self._maxz
def center(self):
if self._center is None:
self._center = self.p1.add(self.p2).add(self.p3).mul(1.0/3)
return self._center
def middle(self):
if self._middle is None:
self.calc_circumcircle()
return self._middle
def radius(self):
if self._radius is None:
self.calc_circumcircle()
return self._radius
def radiussq(self):
if self._radiussq is None:
self.calc_circumcircle()
return self._radiussq
def calc_circumcircle(self):
# We can't use the cached value of "normal", since we don't want the
# normalized value.
normal = self.p2.sub(self.p1).cross(self.p3.sub(self.p2))
denom = normal.norm()
self._radius = (self.p2.sub(self.p1).norm() \
* self.p3.sub(self.p2).norm() * self.p3.sub(self.p1).norm()) \
/ (2 * denom)
self._radiussq = self._radius*self._radius
denom2 = 2*denom*denom
alpha = self.p3.sub(self.p2).normsq() \
* self.p1.sub(self.p2).dot(self.p1.sub(self.p3)) / denom2
beta = self.p1.sub(self.p3).normsq() \
* self.p2.sub(self.p1).dot(self.p2.sub(self.p3)) / denom2
gamma = self.p1.sub(self.p2).normsq() \
* self.p3.sub(self.p1).dot(self.p3.sub(self.p2)) / denom2
self._middle = Point(
self.p1.x * alpha + self.p2.x * beta + self.p3.x * gamma,
self.p1.y * alpha + self.p2.y * beta + self.p3.y * gamma,
self.p1.z * alpha + self.p2.z * beta + self.p3.z * gamma)
def subdivide(self, depth): def subdivide(self, depth):
sub = [] sub = []
if depth == 0: if depth == 0:
...@@ -296,20 +234,3 @@ class Triangle(TransformableContainer): ...@@ -296,20 +234,3 @@ class Triangle(TransformableContainer):
sub += Triangle(p4, self.p2, p5).subdivide(depth - 1) sub += Triangle(p4, self.p2, p5).subdivide(depth - 1)
return sub return sub
def reset_cache(self):
self._minx = None
self._miny = None
self._minz = None
self._maxx = None
self._maxy = None
self._maxz = None
self._center = None
self._middle = None
self._radius = None
self._radiussq = None
self._normal = None
self._plane = None
def get_points(self):
return (self.p1, self.p2, self.p3)
...@@ -61,8 +61,7 @@ def intersect_lines(xl, zl, nxl, nzl, xm, zm, nxm, nzm): ...@@ -61,8 +61,7 @@ def intersect_lines(xl, zl, nxl, nzl, xm, zm, nxm, nzm):
def intersect_cylinder_point(center, axis, radius, radiussq, direction, point): def intersect_cylinder_point(center, axis, radius, radiussq, direction, point):
# take a plane along direction and axis # take a plane along direction and axis
n = direction.cross(axis) n = direction.cross(axis).normalized()
n.normalize()
# distance of the point to this plane # distance of the point to this plane
d = n.dot(point) - n.dot(center) d = n.dot(point) - n.dot(center)
if abs(d) > radius: if abs(d) > radius:
...@@ -77,14 +76,14 @@ def intersect_cylinder_point(center, axis, radius, radiussq, direction, point): ...@@ -77,14 +76,14 @@ def intersect_cylinder_point(center, axis, radius, radiussq, direction, point):
return (ccp, point, -l) return (ccp, point, -l)
def intersect_cylinder_line(center, axis, radius, radiussq, direction, edge): def intersect_cylinder_line(center, axis, radius, radiussq, direction, edge):
d = edge.dir() d = edge.dir
# take a plane throught the line and along the cylinder axis (1) # take a plane throught the line and along the cylinder axis (1)
n = d.cross(axis) n = d.cross(axis)
if n.normsq() == 0: if n.norm == 0:
# no contact point, but should check here if cylinder *always* # no contact point, but should check here if cylinder *always*
# intersects line... # intersects line...
return (None, None, INFINITE) return (None, None, INFINITE)
n.normalize() n = n.normalized()
# the contact line between the cylinder and this plane (1) # the contact line between the cylinder and this plane (1)
# is where the surface normal is perpendicular to the plane # is where the surface normal is perpendicular to the plane
# so line := ccl + \lambda * axis # so line := ccl + \lambda * axis
...@@ -94,11 +93,11 @@ def intersect_cylinder_line(center, axis, radius, radiussq, direction, edge): ...@@ -94,11 +93,11 @@ def intersect_cylinder_line(center, axis, radius, radiussq, direction, edge):
ccl = center.add(n.mul(radius)) ccl = center.add(n.mul(radius))
# now extrude the contact line along the direction, this is a plane (2) # now extrude the contact line along the direction, this is a plane (2)
n2 = direction.cross(axis) n2 = direction.cross(axis)
if n2.normsq() == 0: if n2.norm == 0:
# no contact point, but should check here if cylinder *always* # no contact point, but should check here if cylinder *always*
# intersects line... # intersects line...
return (None, None, INFINITE) return (None, None, INFINITE)
n2.normalize() n2 = n2.normalized()
plane1 = Plane(ccl, n2) plane1 = Plane(ccl, n2)
# intersect this plane with the line, this gives us the contact point # intersect this plane with the line, this gives us the contact point
(cp, l) = plane1.intersect_point(d, edge.p1) (cp, l) = plane1.intersect_point(d, edge.p1)
...@@ -115,20 +114,20 @@ def intersect_cylinder_line(center, axis, radius, radiussq, direction, edge): ...@@ -115,20 +114,20 @@ def intersect_cylinder_line(center, axis, radius, radiussq, direction, edge):
def intersect_circle_plane(center, radius, direction, triangle): def intersect_circle_plane(center, radius, direction, triangle):
# let n be the normal to the plane # let n be the normal to the plane
n = triangle.normal() n = triangle.normal
if n.dot(direction) == 0: if n.dot(direction) == 0:
return (None, None, INFINITE) return (None, None, INFINITE)
# project onto z=0 # project onto z=0
n2 = Point(n.x, n.y, 0) n2 = Point(n.x, n.y, 0)
if n2.normsq() == 0: if n2.norm == 0:
(cp, d) = triangle.plane().intersect_point(direction, center) (cp, d) = triangle.plane.intersect_point(direction, center)
ccp = cp.sub(direction.mul(d)) ccp = cp.sub(direction.mul(d))
return (ccp, cp, d) return (ccp, cp, d)
n2.normalize() n2 = n2.normalized()
# the cutter contact point is on the circle, where the surface normal is n # the cutter contact point is on the circle, where the surface normal is n
ccp = center.add(n2.mul(-radius)) ccp = center.add(n2.mul(-radius))
# intersect the plane with a line through the contact point # intersect the plane with a line through the contact point
(cp, d) = triangle.plane().intersect_point(direction, ccp) (cp, d) = triangle.plane.intersect_point(direction, ccp)
return (ccp, cp, d) return (ccp, cp, d)
def intersect_circle_point(center, axis, radius, radiussq, direction, point): def intersect_circle_point(center, axis, radius, radiussq, direction, point):
...@@ -137,13 +136,13 @@ def intersect_circle_point(center, axis, radius, radiussq, direction, point): ...@@ -137,13 +136,13 @@ def intersect_circle_point(center, axis, radius, radiussq, direction, point):
# intersect with line gives ccp # intersect with line gives ccp
(ccp, l) = plane.intersect_point(direction, point) (ccp, l) = plane.intersect_point(direction, point)
# check if inside circle # check if inside circle
if ccp and (center.sub(ccp).normsq() <= radiussq): if ccp and (center.sub(ccp).normsq <= radiussq):
return (ccp, point, -l) return (ccp, point, -l)
return (None, None, INFINITE) return (None, None, INFINITE)
def intersect_circle_line(center, axis, radius, radiussq, direction, edge): def intersect_circle_line(center, axis, radius, radiussq, direction, edge):
# make a plane by sliding the line along the direction (1) # make a plane by sliding the line along the direction (1)
d = edge.dir() d = edge.dir
if d.dot(axis) == 0: if d.dot(axis) == 0:
if direction.dot(axis) == 0: if direction.dot(axis) == 0:
return (None, None, INFINITE) return (None, None, INFINITE)
...@@ -151,7 +150,7 @@ def intersect_circle_line(center, axis, radius, radiussq, direction, edge): ...@@ -151,7 +150,7 @@ def intersect_circle_line(center, axis, radius, radiussq, direction, edge):
(p1, l) = plane.intersect_point(direction, edge.p1) (p1, l) = plane.intersect_point(direction, edge.p1)
(p2, l) = plane.intersect_point(direction, edge.p2) (p2, l) = plane.intersect_point(direction, edge.p2)
pc = Line(p1, p2).closest_point(center) pc = Line(p1, p2).closest_point(center)
d_sq = pc.sub(center).normsq() d_sq = pc.sub(center).normsq
if d_sq > radiussq: if d_sq > radiussq:
return (None, None, INFINITE) return (None, None, INFINITE)
a = sqrt(radiussq - d_sq) a = sqrt(radiussq - d_sq)
...@@ -170,11 +169,11 @@ def intersect_circle_line(center, axis, radius, radiussq, direction, edge): ...@@ -170,11 +169,11 @@ def intersect_circle_line(center, axis, radius, radiussq, direction, edge):
cp = pc.sub(direction.mul(l)) cp = pc.sub(direction.mul(l))
return (ccp, cp, -l) return (ccp, cp, -l)
n = d.cross(direction) n = d.cross(direction)
if n.normsq() == 0: if n.norm == 0:
# no contact point, but should check here if circle *always* intersects # no contact point, but should check here if circle *always* intersects
# line... # line...
return (None, None, INFINITE) return (None, None, INFINITE)
n.normalize() n = n.normalized()
# take a plane through the base # take a plane through the base
plane = Plane(center, axis) plane = Plane(center, axis)
# intersect base with line # intersect base with line
...@@ -183,14 +182,14 @@ def intersect_circle_line(center, axis, radius, radiussq, direction, edge): ...@@ -183,14 +182,14 @@ def intersect_circle_line(center, axis, radius, radiussq, direction, edge):
return (None, None, INFINITE) return (None, None, INFINITE)
# intersection of 2 planes: lp + \lambda v # intersection of 2 planes: lp + \lambda v
v = axis.cross(n) v = axis.cross(n)
if v.normsq() == 0: if v.norm == 0:
return (None, None, INFINITE) return (None, None, INFINITE)
v.normalize() v = v.normalized()
# take plane through intersection line and parallel to axis # take plane through intersection line and parallel to axis
n2 = v.cross(axis) n2 = v.cross(axis)
if n2.normsq() == 0: if n2.norm == 0:
return (None, None, INFINITE) return (None, None, INFINITE)
n2.normalize() n2 = n2.normalized()
# distance from center to this plane # distance from center to this plane
dist = n2.dot(center) - n2.dot(lp) dist = n2.dot(center) - n2.dot(lp)
distsq = dist * dist distsq = dist * dist
...@@ -207,7 +206,7 @@ def intersect_circle_line(center, axis, radius, radiussq, direction, edge): ...@@ -207,7 +206,7 @@ def intersect_circle_line(center, axis, radius, radiussq, direction, edge):
def intersect_sphere_plane(center, radius, direction, triangle): def intersect_sphere_plane(center, radius, direction, triangle):
# let n be the normal to the plane # let n be the normal to the plane
n = triangle.normal() n = triangle.normal
if n.dot(direction) == 0: if n.dot(direction) == 0:
return (None, None, INFINITE) return (None, None, INFINITE)
# the cutter contact point is on the sphere, where the surface normal is n # the cutter contact point is on the sphere, where the surface normal is n
...@@ -216,7 +215,7 @@ def intersect_sphere_plane(center, radius, direction, triangle): ...@@ -216,7 +215,7 @@ def intersect_sphere_plane(center, radius, direction, triangle):
else: else:
ccp = center.add(n.mul(radius)) ccp = center.add(n.mul(radius))
# intersect the plane with a line through the contact point # intersect the plane with a line through the contact point
(cp, d) = triangle.plane().intersect_point(direction, ccp) (cp, d) = triangle.plane.intersect_point(direction, ccp)
return (ccp, cp, d) return (ccp, cp, d)
def intersect_sphere_point(center, radius, radiussq, direction, point): def intersect_sphere_point(center, radius, radiussq, direction, point):
...@@ -226,9 +225,9 @@ def intersect_sphere_point(center, radius, radiussq, direction, point): ...@@ -226,9 +225,9 @@ def intersect_sphere_point(center, radius, radiussq, direction, point):
# (2) (x-x_0)^2 = R^2 # (2) (x-x_0)^2 = R^2
# (1) in (2) gives a quadratic in \lambda # (1) in (2) gives a quadratic in \lambda
p0_x0 = center.sub(point) p0_x0 = center.sub(point)
a = direction.normsq() a = direction.normsq
b = 2 * p0_x0.dot(direction) b = 2 * p0_x0.dot(direction)
c = p0_x0.normsq() - radiussq c = p0_x0.normsq - radiussq
d = b * b - 4 * a * c d = b * b - 4 * a * c
if d < 0: if d < 0:
return (None, None, INFINITE) return (None, None, INFINITE)
...@@ -242,13 +241,13 @@ def intersect_sphere_point(center, radius, radiussq, direction, point): ...@@ -242,13 +241,13 @@ def intersect_sphere_point(center, radius, radiussq, direction, point):
def intersect_sphere_line(center, radius, radiussq, direction, edge): def intersect_sphere_line(center, radius, radiussq, direction, edge):
# make a plane by sliding the line along the direction (1) # make a plane by sliding the line along the direction (1)
d = edge.dir() d = edge.dir
n = d.cross(direction) n = d.cross(direction)
if n.normsq() == 0: if n.norm == 0:
# no contact point, but should check here if sphere *always* intersects # no contact point, but should check here if sphere *always* intersects
# line... # line...
return (None, None, INFINITE) return (None, None, INFINITE)
n.normalize() n = n.normalized()
# calculate the distance from the sphere center to the plane # calculate the distance from the sphere center to the plane
dist = - center.dot(n) - edge.p1.dot(n) dist = - center.dot(n) - edge.p1.dot(n)
...@@ -260,8 +259,7 @@ def intersect_sphere_line(center, radius, radiussq, direction, edge): ...@@ -260,8 +259,7 @@ def intersect_sphere_line(center, radius, radiussq, direction, edge):
# find the center on the circle closest to this plane # find the center on the circle closest to this plane
# which means the other component is perpendicular to this plane (2) # which means the other component is perpendicular to this plane (2)
n2 = n.cross(d) n2 = n.cross(d).normalized()
n2.normalize()
# the contact point is on a big circle through the sphere... # the contact point is on a big circle through the sphere...
dist2 = sqrt(radiussq - dist * dist) dist2 = sqrt(radiussq - dist * dist)
...@@ -277,7 +275,7 @@ def intersect_sphere_line(center, radius, radiussq, direction, edge): ...@@ -277,7 +275,7 @@ def intersect_sphere_line(center, radius, radiussq, direction, edge):
def intersect_torus_plane(center, axis, majorradius, minorradius, direction, def intersect_torus_plane(center, axis, majorradius, minorradius, direction,
triangle): triangle):
# take normal to the plane # take normal to the plane
n = triangle.normal() n = triangle.normal
if n.dot(direction) == 0: if n.dot(direction) == 0:
return (None, None, INFINITE) return (None, None, INFINITE)
if n.dot(axis) == 1: if n.dot(axis) == 1:
...@@ -286,14 +284,13 @@ def intersect_torus_plane(center, axis, majorradius, minorradius, direction, ...@@ -286,14 +284,13 @@ def intersect_torus_plane(center, axis, majorradius, minorradius, direction,
b = n.mul(-1) b = n.mul(-1)
z = axis z = axis
a = b.sub(z.mul(z.dot(b))) a = b.sub(z.mul(z.dot(b)))
a_sq = a.normsq() a_sq = a.normsq
if a_sq <= 0: if a_sq <= 0:
return (None, None, INFINITE) return (None, None, INFINITE)
a = a.div(sqrt(a_sq)) a = a.div(sqrt(a_sq))
ccp = center.add(a.mul(majorradius)).add(b.mul(minorradius)) ccp = center.add(a.mul(majorradius)).add(b.mul(minorradius))
# find intersection with plane # find intersection with plane
plane = triangle.plane() (cp, l) = triangle.plane.intersect_point(direction, ccp)
(cp, l) = plane.intersect_point(direction, ccp)
return (ccp, cp, l) return (ccp, cp, l)
def intersect_torus_point(center, axis, majorradius, minorradius, majorradiussq, def intersect_torus_point(center, axis, majorradius, minorradius, majorradiussq,
......
...@@ -91,7 +91,7 @@ class Camera: ...@@ -91,7 +91,7 @@ class Camera:
dimz = s.get("maxz") - s.get("minz") dimz = s.get("maxz") - s.get("minz")
max_dim = max(max(dimx, dimy), dimz) max_dim = max(max(dimx, dimy), dimz)
distv = Point(v["distance"][0], v["distance"][1], distv = Point(v["distance"][0], v["distance"][1],
v["distance"][2]).normalize() v["distance"][2]).normalized()
# The multiplier "2.0" is based on: sqrt(2) + margin -- the squre root # The multiplier "2.0" is based on: sqrt(2) + margin -- the squre root
# makes sure, that the the diagonal fits. # makes sure, that the the diagonal fits.
distv = distv.mul((max_dim * 2.0) / math.sin(v["fovy"]/2)) distv = distv.mul((max_dim * 2.0) / math.sin(v["fovy"]/2))
...@@ -216,8 +216,8 @@ class Camera: ...@@ -216,8 +216,8 @@ class Camera:
# Calculate the proportion of each model axis according to the x axis of # Calculate the proportion of each model axis according to the x axis of
# the screen. # the screen.
distv = self.view["distance"] distv = self.view["distance"]
distv = Point(distv[0], distv[1], distv[2]).normalize() distv = Point(distv[0], distv[1], distv[2]).normalized()
factors_x = distv.cross(Point(v_up[0], v_up[1], v_up[2])).normalize() factors_x = distv.cross(Point(v_up[0], v_up[1], v_up[2])).normalized()
factors_x = (factors_x.x, factors_x.y, factors_x.z) factors_x = (factors_x.x, factors_x.y, factors_x.z)
return (factors_x, factors_y) return (factors_x, factors_y)
......
...@@ -79,8 +79,8 @@ class DXFParser: ...@@ -79,8 +79,8 @@ class DXFParser:
current_group = [] current_group = []
groups.append(current_group) groups.append(current_group)
def get_distance_between_groups(group1, group2): def get_distance_between_groups(group1, group2):
forward = group1[-1].p2.sub(group2[0].p1).norm() forward = group1[-1].p2.sub(group2[0].p1).norm
backward = group2[-1].p2.sub(group1[0].p1).norm() backward = group2[-1].p2.sub(group1[0].p1).norm
return min(forward, backward) return min(forward, backward)
remaining_groups = groups[:] remaining_groups = groups[:]
ordered_groups = [] ordered_groups = []
......
...@@ -232,8 +232,7 @@ def ImportModel(filename, use_kdtree=True, program_locations=None): ...@@ -232,8 +232,7 @@ def ImportModel(filename, use_kdtree=True, program_locations=None):
m = endfacet.match(line) m = endfacet.match(line)
if m: if m:
if not n: if not n:
n = p3.sub(p1).cross(p2.sub(p1)) n = p3.sub(p1).cross(p2.sub(p1)).normalized()
n.normalize()
# make sure the points are in ClockWise order # make sure the points are in ClockWise order
dotcross = n.dot(p3.sub(p1).cross(p2.sub(p1))) dotcross = n.dot(p3.sub(p1).cross(p2.sub(p1)))
......
...@@ -106,13 +106,13 @@ class ZBuffer: ...@@ -106,13 +106,13 @@ class ZBuffer:
self.add_triangle(t) self.add_triangle(t)
def add_triangle(self, t): def add_triangle(self, t):
minx = int((t.minx() - self.minx) / (self.maxx - self.minx) \ minx = int((t.minx - self.minx) / (self.maxx - self.minx) \
* self.xres) - 1 * self.xres) - 1
maxx = int((t.maxx() - self.minx) / (self.maxx - self.minx) \ maxx = int((t.maxx - self.minx) / (self.maxx - self.minx) \
* self.xres) + 1 * self.xres) + 1
miny = int((t.miny() - self.miny) / (self.maxy - self.miny) \ miny = int((t.miny - self.miny) / (self.maxy - self.miny) \
* self.yres) - 1 * self.yres) - 1
maxy = int((t.maxy() - self.miny) / (self.maxy - self.miny) \ maxy = int((t.maxy - self.miny) / (self.maxy - self.miny) \
* self.yres) + 2 * self.yres) + 2
if minx < 0: if minx < 0:
minx = 0 minx = 0
......
...@@ -31,10 +31,8 @@ log = pycam.Utils.log.get_logger() ...@@ -31,10 +31,8 @@ log = pycam.Utils.log.get_logger()
def _check_colinearity(p1, p2, p3): def _check_colinearity(p1, p2, p3):
v1 = p2.sub(p1) v1 = p2.sub(p1).normalized()
v2 = p3.sub(p2) v2 = p3.sub(p2).normalized()
v1.normalize()
v2.normalize()
# compare if the normalized distances between p1-p2 and p2-p3 are equal # compare if the normalized distances between p1-p2 and p2-p3 are equal
return v1 == v2 return v1 == v2
...@@ -120,7 +118,7 @@ class ToolPath: ...@@ -120,7 +118,7 @@ class ToolPath:
result["time"] = 0 result["time"] = 0
result["position"] = start_position result["position"] = start_position
def move(new_pos): def move(new_pos):
result["time"] += new_pos.sub(result["position"]).norm() / feedrate result["time"] += new_pos.sub(result["position"]).norm / feedrate
result["position"] = new_pos result["position"] = new_pos
# move to safey height at the starting position # move to safey height at the starting position
safety_height = settings.get_process_settings()["safety_height"] safety_height = settings.get_process_settings()["safety_height"]
......
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