def extrude(nrb, displ, axis=None): """ Construct a NURBS surface/volume by extruding a NURBS curve/surface. Parameters ---------- nrb : NURBS displ : array_like or float axis : array_like or int, optional Example ------- >>> crv = circle() >>> srf = extrude(crv, displ=1, axis=2) >>> srf = bilinear() >>> vol = extrude(srf, displ=1, axis=2) """ assert nrb.dim <= 2 T = transform().translate(displ, axis) Cw = np.empty(nrb.shape+(2,4)) Cw[...,0,:] = nrb.control Cw[...,1,:] = T(nrb.control) UVW = nrb.knots + ([0,0,1,1],) return NURBS(UVW, Cw)
def extrude(nrb, displ, axis=None): """ Construct a NURBS surface/volume by extruding a NURBS curve/surface. Parameters ---------- nrb : NURBS displ : array_like or float axis : array_like or int, optional Example ------- >>> crv = circle() >>> srf = extrude(crv, displ=1, axis=2) >>> srf = bilinear() >>> vol = extrude(srf, displ=1, axis=2) """ assert nrb.dim <= 2 T = transform().translate(displ, axis) Cw = np.empty(nrb.shape + (2, 4)) Cw[..., 0, :] = nrb.control Cw[..., 1, :] = T(nrb.control) UVW = nrb.knots + ([0, 0, 1, 1], ) return NURBS(UVW, Cw)
def transform(self, trans): """ Apply a scaling, rotation, or a translation to a NURBS object. A NURBS object can be scaled, rotated, or translated by applying the tranformation to the control points. To contruct composite transformations, consult the docstrings in :mod:`igakit.transform`. Parameters ---------- trans : array_like a matrix or transformation which scales, rotates, and/or translates a NURBS object. """ if not isinstance(trans, transform): trans = transform(trans) array = self.array.copy() array[...,:4] = trans(self.control) self._array = np.ascontiguousarray(array) return self
def transform(self, trans): """ Apply a scaling, rotation, or a translation to a NURBS object. A NURBS object can be scaled, rotated, or translated by applying the tranformation to the control points. To contruct composite transformations, consult the docstrings in :mod:`igakit.transform`. Parameters ---------- trans : array_like a matrix or transformation which scales, rotates, and/or translates a NURBS object. """ if not isinstance(trans, transform): trans = transform(trans) array = self.array.copy() array[..., :4] = trans(self.control) self._array = np.ascontiguousarray(array) return self
def revolve(nrb, filename, point, axis=2, angle=None): """ Construct a NURBS surface/volume by revolving a NURBS curve/surface. Parameters ---------- nrb : NURBS point : array_like axis : int or array_like, optional angle : float or 2-tuple of floats, optional Example ------- >>> crv = line(1,2) >>> srf = revolve(crv, point=0, axis=2, angle=[Pi/2,2*Pi]) >>> vol = revolve(srf, point=3, axis=1, angle=-Pi/2) """ assert nrb.dim <= 2 point = np.asarray(point, dtype='d') assert point.ndim in (0, 1) assert point.size <= 3 axis = np.asarray(axis) assert axis.ndim in (0, 1) assert 1 <= axis.size <= 3 if axis.ndim == 0: v = np.zeros(3, dtype='d') axis = (0, 1, 2)[int(axis)] v[axis] = 1 else: v = np.zeros(3, dtype='d') v[:axis.size] = axis norm_axis = np.linalg.norm(v) assert norm_axis > 0 v /= norm_axis # Transform the NURBS object to a new reference frame # (O,X,Y,Z) centered at point and z-oriented with axis n = [v[1], -v[0], 0] # n = cross(v, z) gamma = np.arccos(v[2]) # cos_gamma = dot(v, z) T = transform().translate(-point).rotate(gamma, n) nrb = nrb.clone().transform(T) # Map cartesian coordinates (x,y,z) to cylindrical coordinates # (rho,theta,z) with theta in [0,2*pi] and precompute sines and # cosines of theta angles. Cw = nrb.control X, Y, Z, W = (Cw[..., i] for i in range(4)) rho = np.hypot(X, Y) theta = np.arctan2(Y, X) theta[theta < 0] += 2 * np.pi sines, cosines = np.sin(theta), np.cos(theta) # Create a circular arc in the XY plane matX = scipy.io.loadmat(filename) knotsX = np.array(matX['knots']) knotsX = knotsX.tolist()[0] CX = np.transpose(np.array(matX['controlPoints'])) UX = knotsX arc = NURBS([UX], CX) #arc = circle(angle=angle) Aw = arc.control # Allocate control points and knots of the result Qw = np.empty(nrb.shape + arc.shape + (4, )) UVW = nrb.knots + arc.knots # Loop over all control points of the NURBS object # to revolve taking advantage of NumPy nd-indexing dot = np.dot # inline numpy.dot zeros = np.zeros # inline numpy.zeros for idx in np.ndindex(nrb.shape): z = Z[idx] w = W[idx] r = rho[idx] r_sin_a = r * sines[idx] r_cos_a = r * cosines[idx] # for the sake of speed, inline # the transformation matrix # M = Rz(theta)*Tz(z)*Sxy(rho) M = zeros((4, 4)) M[0, 0] = r_cos_a M[0, 1] = -r_sin_a M[1, 0] = r_sin_a M[1, 1] = r_cos_a M[2, 3] = z M[3, 3] = 1 # Compute new 4D control points by transforming the # arc control point and tensor-product the weights Qi = Qw[idx] Qi[...] = dot(Aw, M.T) Qi[..., 3] *= w # Create the new NURBS object and map # back to the original reference frame return NURBS(UVW, Qw).transform(T.invert())
def circle(radius=1, center=None, angle=None): """ Construct a NURBS circular arc or full circle Parameters ---------- radius : float, optional center : array_like, optional angle : float or 2-tuple of floats, optional Examples -------- >>> crv = circle() >>> crv.shape (9,) >>> P = crv([0, 0.25, 0.5, 0.75, 1]) >>> assert np.allclose(P[0], ( 1, 0, 0)) >>> assert np.allclose(P[1], ( 0, 1, 0)) >>> assert np.allclose(P[2], (-1, 0, 0)) >>> assert np.allclose(P[3], ( 0, -1, 0)) >>> assert np.allclose(P[4], ( 1, 0, 0)) >>> crv = circle(angle=3*Pi/2) >>> crv.shape (7,) >>> P = crv([0, 1/3., 2/3., 1]) >>> assert np.allclose(P[0], ( 1, 0, 0)) >>> assert np.allclose(P[1], ( 0, 1, 0)) >>> assert np.allclose(P[2], (-1, 0, 0)) >>> assert np.allclose(P[3], ( 0, -1, 0)) >>> crv = circle(radius=2, center=(1,1), angle=(Pi/2,-Pi/2)) >>> crv.shape (5,) >>> P = crv([0, 0.5, 1]) >>> assert np.allclose(P[0], (1, 3, 0)) >>> assert np.allclose(P[1], (3, 1, 0)) >>> assert np.allclose(P[2], (1, -1, 0)) >>> crv = circle(radius=3, center=2, angle=Pi/2) >>> crv.shape (3,) >>> P = crv([0, 1]) >>> assert np.allclose(P[0], ( 5, 0, 0)) >>> assert np.allclose(P[1], ( 2, 3, 0)) """ if angle is None: # Full circle, 4 knot spans, 9 control points #spans = 4 #Cw = np.zeros((9,4), dtype='d') #Cw[:,:2] = [[ 1, 0], [ 1, 1], [ 0, 1], # [-1, 1], [-1, 0], [-1,-1], # [ 0,-1], [ 1,-1], [ 1, 0]] #Cw[:,:2] *= radius #wm = np.sqrt(2)/2 #Cw[:,3] = 1; Cw[1::2,:] *= wm #Modified to permit C^1 circle # Full circle, 2 knot spans, 5 control points spans = 2 Cw = np.zeros((6, 4), dtype='d') #Cw[:,:] = [[ 1, 0, 0, 9], [ 1, 2, 0, 1], [ -1, 2, 0, 1.0/3], # [-1, -2, 0, 1.0/3], [1, -2, 0, 1], [1 ,0 ,0 ,9]] Cw[:, :] = [[1 * 9, 0, 0, 9], [1, 2, 0, 1], [-1 * 1.0 / 3, 2 * 1.0 / 3, 0, 1.0 / 3], [-1 * 1.0 / 3, -2 * 1.0 / 3, 0, 1.0 / 3], [1, -2, 0, 1], [1 * 9, 0, 0, 9]] Cw[:, :2] *= radius else: raise Exception('For angle<360 new C^1 knots not yet implemented', 'For angle<360 new C^1 knots not yet implemented') Pi = np.pi # inline numpy.pi # Determine start and end angles if isinstance(angle, (tuple, list)): start, end = angle if start is None: start = 0 if end is None: end = 2 * Pi else: start, end = 0, angle # Compute sweep and number knot spans sweep = end - start quadrants = (0.0, Pi / 2, Pi, 3 * Pi / 2) spans = np.searchsorted(quadrants, abs(sweep)) # Construct a single-segment NURBS circular arc # centered at the origin and bisected by +X axis alpha = sweep / (2 * spans) sin_a = np.sin(alpha) cos_a = np.cos(alpha) tan_a = np.tan(alpha) x = radius * cos_a y = radius * sin_a wm = cos_a xm = x + y * tan_a Ca = [[x, -y, 0, 1], [wm * xm, 0, 0, wm], [x, y, 0, 1]] # Compute control points by successive rotation # of the controls points in the first segment Cw = np.empty((2 * spans + 1, 4), dtype='d') R = transform().rotate(alpha + start, 2) Cw[0:3, :] = R(Ca) if spans > 1: R = transform().rotate(2 * alpha, 2) for i in range(1, spans): n = 2 * i + 1 Cw[n:n + 2, :] = R(Cw[n - 2:n, :]) # Translate control points to center if center is not None: T = transform().translate(center) Cw = T(Cw) # Compute knot vector in the range [0,1] #a, b = 0, 1 #U = np.empty(2*(spans+1)+2, dtype='d') #U[0], U[-1] = a, b #U[1:-1] = np.linspace(a,b,spans+1).repeat(2) U = np.array([0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0]) # Return the new NURBS object return NURBS([U], Cw)
def rotate(self, angle, axis=2): t = transform().rotate(angle, axis) return self.transform(t)
def scale(self, scale, axis=None): t = transform().scale(scale, axis) return self.transform(t)
def move(self, displ, axis=None): t = transform().move(displ, axis) return self.transform(t)
def translate(self, displ, axis=None): t = transform().translate(displ, axis) return self.transform(t)
def revolve(nrb, point, axis=2, angle=None): """ Construct a NURBS surface/volume by revolving a NURBS curve/surface. Parameters ---------- nrb : NURBS point : array_like axis : int or array_like, optional angle : float or 2-tuple of floats, optional Example ------- >>> crv = line(1,2) >>> srf = revolve(crv, point=0, axis=2, angle=[Pi/2,2*Pi]) >>> vol = revolve(srf, point=3, axis=1, angle=-Pi/2) """ assert nrb.dim <= 2 point = np.asarray(point, dtype='d') assert point.ndim in (0, 1) assert point.size <= 3 axis = np.asarray(axis) assert axis.ndim in (0, 1) assert 1 <= axis.size <= 3 if axis.ndim == 0: v = np.zeros(3, dtype='d') axis = (0,1,2)[int(axis)] v[axis] = 1 else: v = np.zeros(3, dtype='d') v[:axis.size] = axis norm_axis = np.linalg.norm(v) assert norm_axis > 0 v /= norm_axis # Transform the NURBS object to a new reference frame # (O,X,Y,Z) centered at point and z-oriented with axis n = [v[1], -v[0], 0] # n = cross(v, z) gamma = np.arccos(v[2]) # cos_gamma = dot(v, z) T = transform().translate(-point).rotate(gamma, n) nrb = nrb.clone().transform(T) # Map cartesian coordinates (x,y,z) to cylindrical coordinates # (rho,theta,z) with theta in [0,2*pi] and precompute sines and # cosines of theta angles. Cw = nrb.control X, Y, Z, W = (Cw[...,i] for i in range(4)) rho = np.hypot(X, Y) theta = np.arctan2(Y, X); theta[theta<0] += 2*np.pi sines, cosines = np.sin(theta), np.cos(theta) # Create a circular arc in the XY plane arc = circle(angle=angle) Aw = arc.control # Allocate control points and knots of the result Qw = np.empty(nrb.shape + arc.shape + (4,)) UVW = nrb.knots + arc.knots # Loop over all control points of the NURBS object # to revolve taking advantage of NumPy nd-indexing dot = np.dot # inline numpy.dot zeros = np.zeros # inline numpy.zeros for idx in np.ndindex(nrb.shape): z = Z[idx] w = W[idx] r = rho[idx] r_sin_a = r*sines[idx] r_cos_a = r*cosines[idx] # for the sake of speed, inline # the transformation matrix # M = Rz(theta)*Tz(z)*Sxy(rho) M = zeros((4,4)) M[0,0] = r_cos_a; M[0,1] = -r_sin_a M[1,0] = r_sin_a; M[1,1] = r_cos_a M[2,3] = z M[3,3] = 1 # Compute new 4D control points by transforming the # arc control point and tensor-product the weights Qi = Qw[idx] Qi[...] = dot(Aw, M.T) Qi[...,3] *= w # Create the new NURBS object and map # back to the original reference frame return NURBS(UVW, Qw).transform(T.invert())
def circle(radius=1, center=None, angle=None): """ Construct a NURBS circular arc or full circle Parameters ---------- radius : float, optional center : array_like, optional angle : float or 2-tuple of floats, optional Examples -------- >>> crv = circle() >>> crv.shape (9,) >>> P = crv([0, 0.25, 0.5, 0.75, 1]) >>> assert np.allclose(P[0], ( 1, 0, 0)) >>> assert np.allclose(P[1], ( 0, 1, 0)) >>> assert np.allclose(P[2], (-1, 0, 0)) >>> assert np.allclose(P[3], ( 0, -1, 0)) >>> assert np.allclose(P[4], ( 1, 0, 0)) >>> crv = circle(angle=3*Pi/2) >>> crv.shape (7,) >>> P = crv([0, 1/3., 2/3., 1]) >>> assert np.allclose(P[0], ( 1, 0, 0)) >>> assert np.allclose(P[1], ( 0, 1, 0)) >>> assert np.allclose(P[2], (-1, 0, 0)) >>> assert np.allclose(P[3], ( 0, -1, 0)) >>> crv = circle(radius=2, center=(1,1), angle=(Pi/2,-Pi/2)) >>> crv.shape (5,) >>> P = crv([0, 0.5, 1]) >>> assert np.allclose(P[0], (1, 3, 0)) >>> assert np.allclose(P[1], (3, 1, 0)) >>> assert np.allclose(P[2], (1, -1, 0)) >>> crv = circle(radius=3, center=2, angle=Pi/2) >>> crv.shape (3,) >>> P = crv([0, 1]) >>> assert np.allclose(P[0], ( 5, 0, 0)) >>> assert np.allclose(P[1], ( 2, 3, 0)) """ if angle is None: # Full circle, 4 knot spans, 9 control points spans = 4 Cw = np.zeros((9,4), dtype='d') Cw[:,:2] = [[ 1, 0], [ 1, 1], [ 0, 1], [-1, 1], [-1, 0], [-1,-1], [ 0,-1], [ 1,-1], [ 1, 0]] Cw[:,:2] *= radius wm = np.sqrt(2)/2 Cw[:,3] = 1; Cw[1::2,:] *= wm else: Pi = np.pi # inline numpy.pi # Determine start and end angles if isinstance(angle, (tuple, list)): start, end = angle if start is None: start = 0 if end is None: end = 2*Pi else: start, end = 0, angle # Compute sweep and number knot spans sweep = end - start quadrants = (0.0, Pi/2, Pi, 3*Pi/2) spans = np.searchsorted(quadrants, abs(sweep)) # Construct a single-segment NURBS circular arc # centered at the origin and bisected by +X axis alpha = sweep/(2*spans) sin_a = np.sin(alpha) cos_a = np.cos(alpha) tan_a = np.tan(alpha) x = radius*cos_a y = radius*sin_a wm = cos_a xm = x + y*tan_a Ca = [[ x, -y, 0, 1], [wm*xm, 0, 0, wm], [ x, y, 0, 1]] # Compute control points by successive rotation # of the controls points in the first segment Cw = np.empty((2*spans+1,4), dtype='d') R = transform().rotate(alpha+start, 2) Cw[0:3,:] = R(Ca) if spans > 1: R = transform().rotate(2*alpha, 2) for i in range(1, spans): n = 2*i+1 Cw[n:n+2,:] = R(Cw[n-2:n,:]) # Translate control points to center if center is not None: T = transform().translate(center) Cw = T(Cw) # Compute knot vector in the range [0,1] a, b = 0, 1 U = np.empty(2*(spans+1)+2, dtype='d') U[0], U[-1] = a, b U[1:-1] = np.linspace(a,b,spans+1).repeat(2) # Return the new NURBS object return NURBS([U], Cw)