def vector_by_points(p0, p1, p2=None): """ Create a vector defined by two or three points. :param p0: Origin of plane. :type p0: :class:`.Point` or array_like :param p1: Point defining vector *p1* - *p0*. :type p1: :class:`.Point` or array_like :param p2: Point defining vector *p2* - *p0*. :type p2: :class:`.Point` or array_like :return: A vector from *p0* to *p1* if only two points are provided, or a vector defined by the cross product of *p10* x *p20* if all three points are provided. :rtype: :class:`.Vector` """ if not isinstance(p0, Point): p0 = Point(p0) if not isinstance(p1, Point): p1 = Point(p1) # Cross product if three points are provided. if p2 is not None: if not isinstance(p2, Point): p2 = Point(p2) v10 = p1.xyz - p0.xyz v20 = p2.xyz - p0.xyz vn = cross(v10, v20) return Vector(vn, p0) # Straight vector if two points are provided. return Vector(p1.xyz - p0.xyz, p0)
def plane_by_points(p0, p1, p2): """ Create a plane defined by three points. :param p0: Origin of plane. :type p0: :class:`.Point` or array_like :param p1: Point defining vector *p1* - *p0*. :type p1: :class:`.Point` or array_like :param p2: Point defining vector *p2* - *p0*. :type p2: :class:`.Point` or array_like :return: A plane with a normal vector defined by the cross product of *p10* x *p20* and an x-axis oriented towards *p1* :rtype: :class:`.Plane` """ if not isinstance(p0, Point): p0 = Point(p0) if not isinstance(p1, Point): p1 = Point(p1) if not isinstance(p2, Point): p2 = Point(p2) v10 = p1.xyz - p0.xyz v20 = p2.xyz - p0.xyz vn = cross(v10, v20) vv = cross(vn, v10) vu = cross(vv, vn) vu = Vector(vu, p0) vv = Vector(vv, p0) vn = Vector(vn, p0) return Plane(p0, vn, vu, vv)
def array_to_points1d(arr): n = arr.shape # Detect if weighted points are provided. if n[1] == 4: # p = (x/w, y/w, z/w). return [Point(arr[i, :-1] / arr[i, -1]) for i in range(n[0])] elif n[1] == 3: # p = (x,y,z). return [Point(arr[i]) for i in range(n[0])] return []
def array_to_points2d(arr): nm = arr.shape # Detect if weighted points are provided. pnts = [] for i in range(nm[0]): pi = [] for j in range(nm[1]): if nm[2] == 4: # p = (x/w, y/w, z/w). pi.append(Point(arr[i, j, :-1] / arr[i, j, -1])) elif nm[2] == 3: # p = (x,y,z). pi.append(Point(arr[i, j])) pnts.append(pi) return pnts
def eval(self, u, v, rtype='Point', domain='local'): """ Evaluate surface at parametric points. :param float u: Parametric point in u-direction. :param float v: Parametric point in v-direction. :param str rtype: Option to return a NumPy array or Point instance (rtype = 'ndarray' or 'Point'). :param str domain: Option to use local (0 <= u, v <= 1) or global (a <= u, v <= b) domain ('local', 'l', 'global', 'g'). :return: Point on surface. :rtype: :class:`.Point` or ndarray """ if is_array_like(u) and is_array_like(v): return self.eval_params(u, v, rtype, domain) if is_local_domain(domain): u = self.local_to_global_param('u', u) v = self.local_to_global_param('v', v) pnt = surface_point(self._n, self._p, self._uk, self._m, self._q, self._vk, self._cpw, u, v) if is_array_type(rtype): return pnt else: return Point(pnt)
def intersect_plane_plane(plane1, plane2): """ Find the intersection of two planes. :param plane1: Plane 1 to intersect. :type plane1: :class:`.Plane` :param plane2: Plane 2 to intersect. :type plane2: :class:`.Plane` :return: Intersection line of the two planes. Returns *None* if planes are parallel. :rtype: :class:`.Line` """ # Global parameters. n1 = plane1.vn.ijk n2 = plane2.vn.ijk p1 = plane1.p0.xyz p2 = plane2.p0.xyz # Cross product to find line vector. v = cross(n1, n2) vn = norm(v) # Return None if planes are parallel. if vn <= Settings.atol: return None v /= vn # Find point along intersection vector using intersection of 3 planes. d1 = dot(n1, p1) d2 = dot(n2, p2) n2v = cross(n2, v) p0 = Point((d1 * n2v + d2 * cross(v, n1)) / dot(n1, n2v)) line = Line(p0, Vector(v, p0)) return line
def deriv(self, u, v, k, l, rtype='Vector', domain='local'): """ Compute the surface derivative. :param float u: Parametric point. :param float v: Parametric point. :param int k: Derivative to return in u-direction (0 <= k <= d). :param int l: Derivative to return in v-direction (0 <= l <= d). :param str rtype: Option to return a NumPy array or a Vector instance (rtype = 'Vector' or 'ndarray'). :param str domain: Option to use local (0 <= u, v <= 1) or global (a <= u, v <= b) domain ('local', 'l', 'global', 'g'). :return: Surface derivative. :rtype: :class:`.Vector` or ndarray """ if self._cp is None: return None # Use NURBS rational curve derivative to compute Bezier derivative. # Convert to global since using a NURBS method. if is_local_domain(domain): u = self.local_to_global_param('u', u) v = self.local_to_global_param('v', v) d = k + l der = rat_surface_derivs(self._n, self._p, self._uk, self._m, self._q, self._vk, self._cpw, u, v, d) if is_array_type(rtype): return der[k, l] else: p0 = Point(der[0, 0]) return Vector(der[k, l], p0)
def eval_params(self, ulist, vlist, rtype='Point', domain='local'): """ Evaluate the surface at multiple parameters. :param array_like ulist: Parameters in u-direction. :param array_like vlist: Parameters in v-direction. :param str rtype: Option to return a NumPy array or Point instance (rtype = 'ndarray' or 'Point'). :param str domain: Option to use local (0 <= u, v <= 1) or global (a <= u, v <= b) domain ('local', 'l', 'global', 'g'). :return: Points on surface. :rtype: List :class:`.Point` instances or ndarray """ if not is_array_like(ulist) or not is_array_like(vlist): return self.eval(ulist, vlist, rtype, domain) if not is_local_domain(domain): ulist = [self.global_to_local_param('u', ui) for ui in ulist] vlist = [self.global_to_local_param('v', vi) for vi in vlist] pnts = bezier_surface_points(self._n, self._m, self._cpw, ulist, vlist) if is_array_type(rtype): return pnts else: return [Point(pi) for pi in pnts]
def deriv(self, u, k, d=None, rtype='Vector', domain='local'): """ Compute the *k* -th derivative at point *u*. :param float u: Parametric point. :param int k: Derivative to return (0 <= k <= d). :param int d: Highest derivative to compute. If *d* = *None*, then only the *k* th derivative will be computed. :param str rtype: Option to return a NumPy array or a Vector instance (rtype = 'Vector' or 'ndarray'). :param str domain: Option to use local (0 <= u <= 1) or global (a <= u <= b) domain ('local', 'l', 'global', 'g'). :return: Curve *k* -th derivative. :rtype: :class:`.Vector` or ndarray """ if self._cp is None: return None if d is None: d = k if is_local_domain(domain): u = self.local_to_global_param(u) der = rat_curve_derivs(self._n, self._p, self._uk, self._cpw, u, d) if is_array_type(rtype): return der[k] else: p0 = Point(der[0]) return Vector(der[k], p0)
def fit_plane(pnts, tol=None): """ Fit a plane to a scattered set of points. :param pnts: Points to fit (at least 3 points are required). :type pnts: list of :class:`.Point` instances or array_like :param float tol: Tolerance for checking the fit. If *None* is provided then the plane will be fit to the points. If a float is provided then the plane will not be created if the distance from any points is greater than *tol*. :return: Plane that best fits data. :rtype: :class:`.Plane` """ # Convert points to array. pnts = array(pnts, dtype=float64) if pnts.shape[0] < 3: return None # Calculate average to use as the plane origin. p0 = mean(pnts, axis=0) # Move points to centroid. pc = pnts - p0 # Use SVD. u, s, v = svd(pc, False) # Check that points are not on a line if abs(s[2] - s[1]) <= 1.0e-12: return None # Find min and max values that define normal vector and major and minor # axes of the plane. indx = argsort(s) vn = v[indx[0]] vv = v[indx[1]] vu = v[indx[2]] # Create plane. p0 = Point(p0) vu = Vector(vu, p0) vv = Vector(vv, p0) vn = Vector(vn, p0) plane = Plane(p0, vn, vu, vv) if tol is None: return plane # Check distance to each point. for pi in pnts: if abs(plane.dist2pnt(pi)) > tol: return None return plane
def _set_results(self, nsub, npts, results): """ Set projection results. """ if npts > 0: self._subdivisions = nsub self._npts = npts # Replace the ndarray with a point instance. for i in range(npts): results[i][0] = Point(results[i][0]) self._results = results # Sort results by distance. self._results.sort(key=lambda lst: lst[2])
def point(xyz=(0., 0., 0.)): """ Create a Point. :param array_like xyz: Location of point. :return: Point at *xyz*. :rtype: :class:`.Point` """ if CheckGeom.is_point(xyz): return xyz.copy() if len(xyz) == 3: return Point(xyz) return None
def line_by_points(p0, p1): """ Create a line defined by two points. :param p0: Origin of plane. :type p0: :class:`.Point` or array_like :param p1: Point defining vector *p1* - *p0*. :type p1: :class:`.Point` or array_like :return: A line defined by an oriign at *p0* and a vector *p10*. :rtype: :class:`.Line` """ if not isinstance(p0, Point): p0 = Point(p0) v = vector_by_points(p0, p1) return Line(p0, v)
def eval(self, u, rtype='Point', *args, **kwargs): """ Evaluate point on line. :param float u: Parameter (-inf < u < inf). :param str rtype: Option to return a NumPy array or :class:`.Point` instance (rtype = 'ndarray' or 'Point'). :return: Point on line. :rtype: :class:`.Point` or ndarray """ pnt = self._p0.xyz + u * self._v.ijk if is_array_type(rtype): return pnt else: return Point(pnt)
def eval(self, u=0., v=0., w=0., rtype='Point', *args, **kwargs): """ Evaluate point on the plane at (u, v). :param u: :param v: :param w: :param rtype: :return: """ pnt = (self._p0.xyz + u * self._vu.ijk + v * self._vv.ijk + w * self._vn.ijk) if is_array_type(rtype): return pnt else: return Point(pnt)
def to_point(geom): """ Check to see if the entity is a :class:`.Point` instance and return the instance if it is. If the entity is point_like, create a new :class:`.Point` instance and return it. :param geom: Geometric entity or possible array. :return: The Point instance if already a point, or a new Point in case it is array_like. :rtype: :class:`.Point` """ if isinstance(geom, Point): return geom elif isinstance(geom, (tuple, list, ndarray)): return Point(geom) return None
def create_icurve_by_points(surface, p0, p1, isurf=None, trim=True): """ Create an intersection curve between the two points on the surface. """ # Get surface parameters. uv0 = ProjectGeom.invert(p0, surface) uv1 = ProjectGeom.invert(p1, surface) if None in uv0 or None in uv1: return None # Re-evaluate in case points were not on surface. p0 = surface.eval(uv0[0], uv0[1], domain='global') p1 = surface.eval(uv1[0], uv1[1], domain='global') # Generate intersection plane if no surface is given. if not CheckGeom.is_surface_like(isurf): vn = surface.norm(uv0[0], uv0[1], domain='global') p2 = Point(p0.xyz + vn.ijk) isurf = plane_by_points(p0, p1, p2) # If points are colinear return None for now. if isurf is None: return None # Intersect the surface. si = IntersectGeom.perform(surface, isurf) if not si.success: return None indx = si.curve_nearest_point(p0) icrv = si.get_icurve(indx) # Project points to curve. u0 = ProjectGeom.invert(p0, icrv) u1 = ProjectGeom.invert(p1, icrv) if None in [u0, u1]: return None # Reverse if necessary. if u0 > u1: icrv.reverse(True) u0, u1 = -u0 + icrv.a + icrv.b, -u1 + icrv.a + icrv.b # Trim if desired and return. if not trim: return icrv return icrv.extract(u0, u1, domain='global')
def eval(self, dx, dy, dz, rtype='Point', *args, **kwargs): """ Evaluate a point relative to the system. :param dx: :param dy: :param dz: :param rtype: :param args: :param kwargs: :return: """ pnt = (self._origin.xyz + dx * self._vx.ijk + dy * self._vy.ijk + dz * self._vz.ijk) if is_array_type(rtype): return pnt else: return Point(pnt)
def plane_by_axes(p0, axes, sys=None): """ Create a plane defined by an origin and standard axes. :param p0: Origin of plane. :type p0: :class:`.Point` or array_like :param axes: Standard axes, one of 'xy', 'xz', or 'yz'. :param sys: Reference system for axes. :type sys: :class:`.System` :return: Plane oriented by axes. :rtype: :class:`.Plane` """ if not isinstance(axes, str): return None if axes.lower() not in ['xy', 'yx', 'xz', 'zx', 'yz', 'zy']: return None if not isinstance(p0, Point): p0 = Point(p0) vx = array([1., 0., 0.], dtype=float64) vy = array([0., 1., 0.], dtype=float64) vz = array([0., 0., 1.], dtype=float64) if CheckGeom.is_system(sys): vx = sys.vx.ijk vy = sys.vy.ijk vz = sys.vz.ijk if axes.lower() in ['xy', 'yx']: p1 = p0.xyz + vx p2 = p0.xyz + vy return plane_by_points(p0, p1, p2) if axes.lower() in ['xz', 'zx']: p1 = p0.xyz + vz p2 = p0.xyz + vx return plane_by_points(p0, p1, p2) if axes.lower() in ['yz', 'zy']: p1 = p0.xyz + vy p2 = p0.xyz + vz return plane_by_points(p0, p1, p2)
def eval(self, u, rtype='Point', domain='local', tol=None): """ Evaluate curve at parametric point. :param float u: Parametric point. :param str rtype: Option to return a NumPy array or Point instance (rtype = 'Point' or 'ndarray'). :param str domain: Option to use local (0 <= u <= 1) or global (a <= u <= b) domain ('local', 'l', 'global', 'g'). :param float tol: Tolerance for point refinement. :return: Point on curve. :rtype: :class:`.Point` or ndarray """ if is_local_domain(domain): u = self.local_to_global_param(u) uv1, uv2 = self.eval2d(u, rtype='ndarray', domain='global', tol=tol) p3d = self._s1.eval(uv1[0], uv1[1], rtype='ndarray', domain='global') if is_array_type(rtype): return p3d else: return Point(p3d)
def plane_by_normal(p0, vn): """ Create a plane by an origin and normal vector. :param p0: Origin of plane. :type p0: :class:`.Point` or array_like :param vn: Normal vector of plane. :type vn: :class:`.Vector` or array_like :return: Plane with given origin and normal vector. :rtype: :class:`.Plane` """ if not isinstance(p0, Point): p0 = Point(p0) if not isinstance(vn, Vector): vn = Vector(vn, p0) # Try x-axis. vu = cross([1., 0., 0.], vn.vxyz) if dot(vu, vu) > 0.: vv = Vector(cross(vu, vn.vxyz), p0) vu = Vector(vu, p0) return Plane(p0, vn, vu, vv) # Try y-axis. vu = cross([0., 1., 0.], vn.vxyz) if dot(vu, vu) > 0.: vv = Vector(cross(vu, vn.vxyz), p0) vu = Vector(vu, p0) return Plane(p0, vn, vu, vv) # Try z-axis. vu = cross([0., 0., 1.], vn.vxyz) if dot(vu, vu) > 0.: vv = Vector(cross(vu, vn.vxyz), p0) vu = Vector(vu, p0) return Plane(p0, vn, vu, vv) return None
def eval(self, u, v, rtype='Point', domain='local'): """ Evaluate surface at parametric points. :param float u: Parametric point in u-direction. :param float v: Parametric point in v-direction. :param str rtype: Option to return a NumPy array or Point instance (rtype = 'Point' or 'ndarray'). :param str domain: Option to use local (0 <= u, v <= 1) or global (a <= u, v <= b) domain ('local', 'l', 'global', 'g'). :return: Point on surface. :rtype: :class:`.Point` or ndarray """ if not is_local_domain(domain): u = self.global_to_local_param('u', u) v = self.global_to_local_param('v', v) pw = deCasteljau2(self._cpw, self._n, self._m, u, v) pnt = pw[:-1] / pw[-1] if is_array_type(rtype): return pnt else: return Point(pnt)
def eval_params(self, ulist, rtype='Point', domain='local'): """ Evaluate the curve at multiple parameters. :param array_like ulist: Curve parameters. :param str rtype: Option to return a NumPy array or list of Point instances (rtype = 'Point' or 'ndarray'). :param str domain: Option to use local (0 <= u <= 1) or global (a <= u <= b) domain ('local', 'l', 'global', 'g'). :return: Points at parameters. :rtype: List of :class:`.Point` instances or NumPy array """ if not is_array_like(ulist): return self.eval(ulist, rtype, domain) if is_local_domain(domain): ulist = map(self.global_to_local_param, ulist) pnts = curve_points(self._n, self._p, self._uk, self._cpw, ulist) if is_array_type(rtype): return pnts else: return [Point(pi) for pi in pnts]
def eval(self, u, rtype='Point', domain='local'): """ Evaluate curve at parametric point. :param float u: Parametric point. :param str rtype: Option to return a NumPy array or Point instance (rtype = 'Point' or 'ndarray'). :param str domain: Option to use local (0 <= u <= 1) or global (a <= u <= b) domain ('local', 'l', 'global', 'g'). :return: Point on curve. :rtype: :class:`.Point` or ndarray """ if is_array_like(u): return self.eval_params(u, rtype, domain) if is_local_domain(domain): u = self.local_to_global_param(u) pnt = curve_point(self._n, self._p, self._uk, self._cpw, u) if is_array_type(rtype): return pnt else: return Point(pnt)
def vector_by_axis(axis, origin): """ Create a vector along the specified axis. :param str axis: Axis ('x', 'y', or 'z'). :param array_like origin: Origin of vector. :return: Vector along specified axis. :rtype: :class:`.Vector` """ if not isinstance(origin, Point): origin = Point(origin) if not isinstance(axis, str): return None if axis.lower() not in ['x', 'y', 'z']: return None if axis.lower() in ['x']: return Vector([1., 0., 0.], origin) if axis.lower() in ['y']: return Vector([0., 1., 0.], origin) if axis.lower() in ['z']: return Vector([0., 0., 1.], origin)
from pynurbs.geometry.creator import CreateGeom from pynurbs.geometry.intersector import IntersectSurfaceSurface from pynurbs.geometry.point import Point from pynurbs.graphics.viewer import Viewer gui = Viewer() p0 = Point((0, 0, 0)) p1 = Point((2, 2, 0)) p2 = Point((4, 3, 0)) p3 = Point((8, -2, 0)) c1 = CreateGeom.interpolate_points([p0, p1, p2, p3]) p0 = Point((0, 0, 10)) p1 = Point((2, 2, 10)) p2 = Point((4, 3, 10)) p3 = Point((8, -2, 10)) c2 = CreateGeom.interpolate_points([p0, p1, p2, p3]) s1 = CreateGeom.interpolate_curves([c1, c2]) p0 = Point((0, 5, 5)) p1 = Point((2, 3, 5)) p2 = Point((4, 2, 5)) p3 = Point((8, 5, 5)) c3 = CreateGeom.interpolate_points([p0, p1, p2, p3]) p0 = Point((0, 5, 8)) p1 = Point((2, 3, 8)) p2 = Point((4, 2, 8)) p3 = Point((8, 5, 8))
from pynurbs.geometry.creator import CreateGeom from pynurbs.geometry.intersector import IntersectCurveCurve from pynurbs.geometry.point import Point from pynurbs.graphics.viewer import Viewer gui = Viewer() p0 = Point((0, 0, 0)) p1 = Point((2, 2, 0)) p2 = Point((4, 3, 0)) p3 = Point((8, -2, 0)) c1 = CreateGeom.interpolate_points([p0, p1, p2, p3]) gui.add_items(p0, p1, p2, p3, c1) gui.show() p0 = Point((5, -5, 0)) p1 = Point((10, 8, 0)) c2 = CreateGeom.interpolate_points([p0, p1]) gui.add_items(p0, p1, c2, c1) gui.show() cci = IntersectCurveCurve(c1, c2) gui.add_items(c1, c2, *cci.points) gui.show()