# -*- coding: utf-8 -*- """ $Id$ Copyright 2010 Lars Kruse Copyright 2008-2009 Lode Leroy This file is part of PyCAM. PyCAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. PyCAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with PyCAM. If not, see . """ from pycam.Geometry.utils import epsilon, sqrt, number from pycam.Geometry import IDGenerator def _is_near(x, y): return abs(x - y) < epsilon class Point(IDGenerator): __slots__ = ["id", "x", "y", "z", "_norm", "_normsq"] def __init__(self, x, y, z): super(Point, self).__init__() self.x = number(x) self.y = number(y) self.z = number(z) self.reset_cache() @property def norm(self): if self._norm is None: self._norm = sqrt(self.normsq) return self._norm @property def normsq(self): if self._normsq is None: self._normsq = self.dot(self) return self._normsq def copy(self): return self.__class__(float(self.x), float(self.y), float(self.z)) def __repr__(self): return "Point%d<%g,%g,%g>" % (self.id, self.x, self.y, self.z) def __cmp__(self, other): """ Two points are equal if all dimensions are identical. Otherwise the result is based on the individual x/y/z comparisons. """ if self.__class__ == other.__class__: if (self.id == other.id) or \ ((_is_near(self.x, other.x)) and \ (_is_near(self.y, other.y)) and \ (_is_near(self.z, other.z))): return 0 elif not _is_near(self.x, other.x): return cmp(self.x, other.x) elif not _is_near(self.y, other.y): return cmp(self.y, other.y) else: return cmp(self.z, other.z) else: return cmp(str(self), str(other)) def transform_by_matrix(self, matrix, transformed_list=None, callback=None): # accept 3x4 matrices as well as 3x3 matrices offsets = [] for column in matrix: if len(column) < 4: offsets.append(0) else: offsets.append(column[3]) x = self.x * matrix[0][0] + self.y * matrix[0][1] \ + self.z * matrix[0][2] + offsets[0] y = self.x * matrix[1][0] + self.y * matrix[1][1] \ + self.z * matrix[1][2] + offsets[1] z = self.x * matrix[2][0] + self.y * matrix[2][1] \ + self.z * matrix[2][2] + offsets[2] self.x = x self.y = y self.z = z if callback: callback() self.reset_cache() def reset_cache(self): self._norm = None self._normsq = None def mul(self, c): c = number(c) return Point(self.x * c, self.y * c, self.z * c) def div(self, c): c = number(c) return Point(self.x / c, self.y / c, self.z / c) def add(self, p): return Point(self.x + p.x, self.y + p.y, self.z + p.z) def sub(self, p): return Point(self.x - p.x, self.y - p.y, self.z - p.z) def dot(self, p): return self.x * p.x + self.y * p.y + self.z * p.z def cross(self, p): 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 normalized(self): n = self.norm if n == 0: return None else: return self.__class__(self.x / n, self.y / n, self.z / n) def is_inside(self, minx=None, maxx=None, miny=None, maxy=None, minz=None, maxz=None): return ((minx is None) or (minx - epsilon <= self.x)) \ and ((maxx is None) or (self.x <= maxx + epsilon)) \ and ((miny is None) or (miny - epsilon <= self.y)) \ and ((maxy is None) or (self.y <= maxy + epsilon)) \ and ((minz is None) or (minz - epsilon <= self.z)) \ and ((maxz is None) or (self.z <= maxz + epsilon)) def get_vector(self): return Vector(self.x, self.y, self.z) class Vector(Point): """ The Vector class is similar to the Point class. The only difference is that vectors are not shifted during transformations. This feature is necessary for normals (e.g. of Triangles or Planes). """ __slots__ = [] def transform_by_matrix(self, matrix, transformed_list=None, callback=None): x = self.x * matrix[0][0] + self.y * matrix[0][1] \ + self.z * matrix[0][2] y = self.x * matrix[1][0] + self.y * matrix[1][1] \ + self.z * matrix[1][2] z = self.x * matrix[2][0] + self.y * matrix[2][1] \ + self.z * matrix[2][2] self.x = x self.y = y self.z = z if callback: callback() self.reset_cache() def __repr__(self): return "Vector%d<%g,%g,%g>" % (self.id, self.x, self.y, self.z)