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