def __init__(self, exp, coeff=None): """Create an n-d polynomial""" self.exp = at.checkArray(exp, kind='i', ndim=2) if coeff is None: self.coeff = np.ones(self.nterms) else: self.coeff = at.checkArray(coeff, (self.nterms, ), 'f', 'i')
def lookAt(self, focus=None, eye=None, up=None): """Set the Modelview matrix to look at the specified focus point. The Modelview matrix is set with the camera positioned at eye and looking at the focus points, while the camera up vector is in the plane of the camera axis (focus-eye) and the specified up vector. If any of the arguments is left unspecified, the current value will be used. """ if not self.locked: if focus is None: focus = self.focus else: focus = at.checkArray(focus, (3, ), 'f') if eye is None: eye = self.eye else: eye = at.checkArray(eye, (3, ), 'f') if up is None: up = self.upvector else: up = at.normalize(at.checkArray(up, (3, ), 'f')) vector = eye - focus self.focus = focus self.dist = at.length(vector) axis2 = at.normalize(vector) axis0 = at.normalize(np.cross(up, axis2)) axis1 = at.normalize(np.cross(axis2, axis0)) m = Matrix4() m.rotate(np.column_stack([axis0, axis1, axis2])) m.translate(-eye) self.setModelview(m)
def index(self, sel): """Convert a selector to an index sel is either a list of element numbers or a bool array with length self.size Returns an index array with the selected numbers. """ try: sel = at.checkArray(sel, shape=(self.size, ), kind='b') sel = np.where(sel)[0] except: sel = at.checkArray(sel, kind='i') return sel
def normalize(x, w): """Normalized coordinates inside a window. Parameters: - `x`: an (np,nc) array with coordinates. - `w`: a (2,nc) array with minimal and width of the window that will be mapped to the range -1..1. Returns an array with the x values linearly remapped thus that values w[0] become -1 and values w[0]+w[1] become +1. """ x = at.checkArray(x, (-1, -1), 'f') np, nc = x.shape w = at.checkArray(w, (2, nc), 'f', 'i') return (x - w[0]) * 2 / w[1] - 1
def fromWindow(self, x): """Convert window coordinates to normalized device coordinates""" # This is only correct when glDepthRange(0.0, 1.0) # We should not change the depth range vp = gl_viewport() x = at.checkArray(x, (-1, 3), 'f') return normalize(x[:, :3], [[vp[0], vp[1], 0], [vp[2], vp[3], 1]])
def __init__(self, geometry, fldtype, data, fldname=None): """Initialize a Field. """ if not hasattr(geometry, 'fieldtypes') or fldtype not in geometry.fieldtypes: raise ValueError("Can not add field of type '%s' to a %s" % (fldtype, geometry.__class__.__name__)) if fldtype == 'node': datashape = (geometry.nnodes(), -1) elif fldtype == 'elemc': datashape = (geometry.nelems(), -1) elif fldtype == 'elemn': datashape = (geometry.nelems(), geometry.nplex(), -1) elif fldtype == 'elemg': datashape = (geometry.nelems(), -1, -1) scalar = len(data.shape) < len(datashape) if scalar: datashape = datashape[:-1] data = at.checkArray(data, shape=datashape) if fldname is None: fldname = next(Field._autoname) # All data seem OK, store them self.geometry = geometry self.fldname = fldname self.fldtype = fldtype self.data = data self.scalar = scalar
def denormalize(x, w): """Map normalized coordinates to fit a window Parameters: - `x`: an (np,nc) array with normalized coordinates. - `w`: a (2,nc) array with minimal and width values of the window. Returns an array with the x values linearly remapped thus that values -1 coincide with the minimum window values and +1 with the minimum+width values. """ x = at.checkArray(x, (-1, -1), 'f') np, nc = x.shape w = at.checkArray(w, (2, nc), 'f', 'i') return w[0] + (1 + x) * w[1] / 2
def saneSettings(k): """Sanitize sloppy settings for JavaScript output""" ok = {} try: ok['color'] = checkArray(k['color'], (3,), 'f') except: try: c = checkInt(k['color'][0]) print("COLOR INDEX %s" % c) colormap = pf.canvas.settings.colormap ok['color'] = colormap[c % len(colormap)] except: print("Unexpected color: %s" % k['color']) try: ok['alpha'] = checkFloat(k['alpha'], 0., 1.) except: pass try: ok['caption'] = str(k['caption']) except: pass try: ok['control'] = intersection(k['control'], controller_format.keys()) except: pass return ok
def transform(self, x): """Transform a vertex using this matrix. - `x`: a (3,) or (4,) vector. If the vector has length 4, it holds homogeneous coordinates, and the result is the dot product of the vector with the Matrix: x * M. If the vector has length 3, the 4th homogeneous coordinate is assumed to be 1, and the product is computed in an optimized way. """ try: x = at.checkArray(x, (3, ), 'f') return np.dot(x, self[:3, :3]) + self[3, :3] except: x = at.checkArray(x, (4, ), 'f') return np.dot(x, self)
def __new__(clas,data,mass,ctr): """Create a new Tensor instance""" ar = Tensor.__new__(clas,data) # We need mass and ctr! ar.mass = float(mass) ar.ctr = Coords(at.checkArray(ctr,shape=(3,),kind='f')) return ar
def convexHull(points): """Return the convex hull of a set of points. Parameters: - `points`: float array (npoints, 2|3): a set of 2D or 3D point coordinates. Returns a :class:`Connectivity` containing the indices of the points that constitute the convex hull of the given point set. The convex hull is the minimal set of simplices enclosing all the points. For a 3D convex hull, the Connectivity will have plexitude 3 and an eltype 'tri3', while for 2D convex hulls, the Connectivity has plexitude 2 and eltype 'line2'. This requires SciPy version 0.12.0 or higher. If :func:`scipy.spatial.ConvexHull raises an error, an empty connectivity is returned. This happens if all the points of a 3D set are in a plane or all the points of a 2D set are on a line. """ software.requireModule('scipy', '0.12.0') from scipy.spatial import ConvexHull points = at.checkArray(points, ndim=2, kind='f') ndim = points.shape[1] if ndim not in [2, 3]: raise ValueError('Expected 2D or 3D coordinate array') try: hull = ConvexHull(points).simplices except: hull = [] return Connectivity(hull, nplex=ndim, eltype='tri3' if ndim == 3 else 'line2')
def __new__(clas,data=None,symmetric=True,cs=None): """Create a new Tensor instance""" try: data = at.checkArray(data,shape=(3,3),kind='f',allow='if') except: try: data = at.checkArray(data,shape=(6,),kind='f',allow='if') except: raise ValueError("Data should have shape (3,3) or (6,)") data = data[Tensor._contracted_index] ar = data.view(clas) if cs is None: cs = CoordSys() if not(isinstance(cs,CoordSys)): raise ValueError('Wrong Coordinate System') ar.cs = cs return ar
def __init__(self,oab=None,points=None,rot=None,trl=None): """Initialize the CoordSys""" if oab is not None: oab = at.checkArray(oab,(3,3),'f') rot = at.rotmat(oab) trl = oab[0] elif points is not None: points = at.checkArray(points,(4,3),'f') rot = at.normalize(points[:3] - points[3]) trl = points[3] else: if rot is None: rot = np.eye(3, 3) if trl is None: trl = np.zeros((3,)) self.rot = rot self.trl = trl
def __new__(clas, data=None): """Create a new Matrix instance""" if data is None: data = np.eye(4, 4) else: data = at.checkArray(data, (4, 4), 'f') ar = data.view(clas) ar._gl = None return ar
def write_stl_bin(fn, x, color=None): """Write a binary stl. Parameters: - `x`: (ntri,4,3) float array describin ntri triangles. The first item of each triangle is the normal, the other three are the vertices. - `color`: (4,) int array with values in the range 0..255. These are the red, green, blue and alpha components of the color. This is a single color for all the triangles, and will be stored in the header of the STL file. """ x = checkArray(x, shape=(-1, 4, 3), kind='f') if color is not None: #color = checkArray(color, shape=(4,), kind='i').astype(np.uint8) color = checkArray(color, shape=(4, ), kind='u', allow='i').astype(np.uint8) def addTriangle(i): x[i].tofile(fil) fil.write('\x00\x00') print("Writing binary STL %s" % fn) ver = pf.fullVersion() if len(ver) > 50: ver = ver[:50] if color is None: color = '' else: color = "COLOR=%4s" % color.tostring() print("Adding %s to the header" % color) with open(fn, 'wb') as fil: head = "%-50s%-30s" % (ver, color) fil.write(head) ntri = x.shape[0] print("Number of triangles: %s" % ntri) np.array(ntri).astype(np.int32).tofile(fil) x = x.astype(np.float32) [addTriangle(i) for i in range(ntri)] print("Finished writing binary STL, %s bytes" % utils.fileSize(fn))
def rotate(self, angle, axis=None): """Rotate a Matrix4. The rotation can be specified by - an angle and axis, - a 3x3 rotation matrix, - a 4x4 trtransformation matrix (Matrix4). Parameters: - `angle`: float: the rotation angle. A 3x3 or 4x4 matrix may be give instead, to directly specify the roation matrix. - `axis`: int or (3,) float: the axis to rotate around Changes the Matrix in place and also returns the result. Example: >>> Matrix4().rotate(90.,[0.,1.,0.]) matrix([[ 0., 0., -1., 0.], [ 0., 1., 0., 0.], [ 1., 0., 0., 0.], [ 0., 0., 0., 1.]]) """ ## !! TRANSPOSE!! ## x^2(1-c)+c xy(1-c)-zs xz(1-c)+ys 0 ## yx(1-c)+zs y^2(1-c)+c yz(1-c)-xs 0 ## xz(1-c)-ys yz(1-c)+xs z^2(1-c)+c 0 ## 0 0 0 1 try: rot = at.checkArray(angle, (4, 4), 'f')[:3, :3] except: try: rot = at.checkArray(angle, (3, 3), 'f') except: angle = at.checkFloat(angle) rot = np.matrix(at.rotationMatrix(angle, axis)) self.rot = rot * self.rot return self
def toEye(self, x): """Transform a vertex from world to eye coordinates. This transforms the vertex using the current Modelview matrix. It is equivalent with multiplying the homogeneous coordinates with the Modelview matrix, but is done here in an optimized way. """ x = at.checkArray(x, (-1, 3), 'f') return np.dot(x, self.modelview[:3, :3]) + self.modelview[3, :3]
def focus(self, vector): """Set the camera reference point (the focus point). The focus is the point the camer is looking at. It is a point on the camera's optical axis. - `vector`: (3,) float array: the global coordinates of the focus. """ if not self.locked: self._focus = at.checkArray(vector, (3, ), 'f') self.viewChanged = True
def toWorld(self, x): """Transform a vertex from eye to world coordinates. This transforms the vertex using the inverse of the current Modelview matrix. It is equivalent with multiplying the homogeneous coordinates with the inverse Modelview matrix, but is done here in an optimized way. """ x = at.checkArray(x, (3, ), 'f') + [0., 0., self.dist] return np.dot(x, self.rot.T) + self.focus
def evalAtoms(self, x): """Evaluate the monomials at the given points x is an (npoints,ndim) array of points where the polynomial is to be evaluated. The result is an (npoints,nterms) array of values. """ x = at.checkArray(x, (-1, self.ndim), 'f', 'i') symbol = 'xyz' g = dict([(symbol[i], x[:, i]) for i in range(self.ndim)]) atoms = self.atoms(symbol) aa = np.zeros((len(x), len(atoms)), at.Float) for k, a in enumerate(atoms): aa[:, k] = eval(a, g) return aa
def evalAtoms1(self, x): """Evaluate the monomials at the given points x is an (npoints,ndim) array of points where the polynomial is to be evaluated. The result is an (npoints,nterms) array of values. """ x = at.checkArray(x, (-1, self.ndim), 'f', 'i') maxd = self.degrees() mon = [at.powers(x[:, j], maxd[j]) for j in range(self.ndim)] terms = [[mon[j][e[j]] for j in range(self.ndim)] for e in self.exp] terms = np.dstack([ np.column_stack([mon[j][e[j]] for j in range(self.ndim)]) for e in self.exp ]) return terms.prod(axis=1)
def tetrahedral_volume(x): """Compute the volume of tetrahedrons. - `x`: an (ntet,4,3) shaped float array, representing ntet tetrahedrons. Returns an (ntet,) shaped array with the volume of the tetrahedrons. Depending on the ordering of the points, this volume may be positive or negative. It will be positive if point 4 is on the side of the positive normal formed by the first 3 points. """ x = at.checkArray(x,shape=(-1,4,3),kind='f') a, b, c = [ x[:,i,:] - x[:,3,:] for i in range(3) ] d = np.cross(b, c) e = (a*d).sum(axis=-1) return -e / 6
def translateTo(self,ref,toG=False): """Return the inertia tensor around axes translated to the reference point ref. Parameters: - `ref`: arraylike (3,). The new reference point coordinates. - `toG`: bool. If False (default) the inertia tensor is translated to the the new reference point, otherwise it will be translated to its center of mass """ trl = at.checkArray(ref,shape=(3,),kind='f') - self.ctr return self.translate(self,trl,toG=toG)
def reduceAdjacency(adj): """Reduce an adjacency table. An adjacency table is an integer array where each row lists the numbers of the items that are connected to the item with number equal to the row index. Rows are padded with -1 values to create rows of equal length. A reduced adjacency table is one where each row: - does not contain the row index itself, - does not contain duplicates, - is sorted in ascending order, and that has at least one row without -1 value. Paramaters: - `adj`: an 2-D integer array with value >=0 or -1 Returns: an integer array with shape (adj.shape[0],maxc), with maxc <= adj.shape[1], where row `i` retains the unique non-negative numbers of the original array except the value `i`, and is possibly padded with -1 values. Example: >>> a = np.array([[ 0, 0, 0, 1, 2, 5], ... [-1, 0, 1, -1, 1, 3], ... [-1, -1, 0, -1, -1, 2], ... [-1, -1, 1, -1, -1, 3], ... [-1, -1, -1, -1, -1, -1], ... [-1, -1, 0, -1, -1, 5]]) >>> reduceAdjacency(a) array([[ 1, 2, 5], [-1, 0, 3], [-1, -1, 0], [-1, -1, 1], [-1, -1, -1], [-1, -1, 0]]) """ adj = at.checkArray(adj, ndim=2) n = adj.shape[0] adj[adj == np.arange(n).reshape(n, -1)] = -1 # remove the item i adj = sortAdjacency(adj) adj[np.where(adj[:, :-1] == adj[:, 1:])] = -1 #remove duplicate items adj = sortAdjacency(adj) return adj
def surface_volume_inertia(x,center_only=False): """Return the inertia of the volume inside a 3-plex Formex. - `x`: an (ntri,3,3) shaped float array, representing ntri triangles. This uses the same algorithm as tetrahedral_inertia using [0.,0.,0.] as the 4-th point for each tetrahedron. Returns a tuple (V,C,I) where V is the total volume, C is the center of mass (3,) and I is the inertia tensor (6,) of the tetrahedral model. Example: >>> from .simple import sphere >>> S = sphere(4).toFormex() >>> V,C,I = surface_volume_inertia(S.coords) >>> print(V,C,I) 4.04701 [-0. -0. -0.] [ 1.58 1.58 1.58 -0. 0. 0. ] """ def K(x,y): x1,x2,x3 = x[:,0], x[:,1], x[:,2] y1,y2,y3 = y[:,0], y[:,1], y[:,2] return x1 * (y1+y2+y3) + \ x2 * ( y2+y3) + \ x3 * ( y3) x = at.checkArray(x,shape=(-1,3,3),kind='f') v = surface_volume(x) V = v.sum() c = x.sum(axis=1) / 4. # 4-th point is 0.,0.,0. C = (c*v[:,np.newaxis]).sum(axis=0) / V if center_only: return V,C x -= C aa = 2 * K(x,x) * v.reshape(-1,1) aa = aa.sum(axis=0) a0 = aa[1] + aa[2] a1 = aa[0] + aa[2] a2 = aa[0] + aa[1] x0,x1,x2 = x[...,0],x[...,1],x[...,2] a3 = (( K(x1,x2) + K(x2,x1) ) * v).sum(axis=0) a4 = (( K(x2,x0) + K(x0,x2) ) * v).sum(axis=0) a5 = (( K(x0,x1) + K(x1,x0) ) * v).sum(axis=0) I = np.array([a0,a1,a2,a3,a4,a5]) / 20. return V,C,I
def __init__(self, data, type): """Initialize the UniaxialStrain""" data = checkArray(data, shape=(-1, ), kind='f') if type == 'nominal': data = data + 1. elif type == 'log': data = np.exp(data) elif type == 'green': data = sqrt(2 * data + 1.) elif type == 'almansi': data = 1. / sqrt(1. - 2 * data) elif type != 'stretch': raise ValueError("Invalid strain type: %s" % type) self.data = data
def __init__(self, data, type, strain, straintype=None): """Initialize the UniaxialStress""" data = checkArray(data, shape=(-1, ), kind='f') if not isinstance(strain, UniaxialStrain): strain = UniaxialStrain(strain, straintype) stretch = strain.stretch() if type == 'nominal': data = data * stretch elif type == 'pk2': data = data * stretch**2 elif type != 'cauchy': raise ValueError("Invalid stress type: %s" % type) self.data = data self.strain = strain
def inverseIndex(ind, sort=False, expand=False): """Create the inverse of a 2D index array. Parameters: - `ind`: a Varray or a 2D index array. A 2D index array is a 2D integer array where only nonnegative values are significant and negative values are silently ignored. While in most cases all values in a row are unique, this is not a requirement. Degenerate elements may have the same node number appearing multiple times in the same row. - `sort`: bool. If True, rows are sorted. - `expand`: bool. If True, an :class:`numpy.ndarray` is returned. Returns the inverse index, as a Varray (default) or as an ndarray (if expand is True). If sort is True, rows are sorted. Example: >>> a = inverseIndex([[0,1],[0,2],[1,2],[0,3]]) >>> print(a) Varray (4,3) [0 1 3] [0 2] [1 2] [3] <BLANKLINE> """ if isinstance(ind, Varray): ind = ind.toArray() ind = at.checkArray(ind, ndim=2, kind='i') b = np.resize(np.arange(ind.shape[0]), ind.shape[::-1]) c = at.stack([ind, b.transpose()]).reshape(2, -1) s = c[0].argsort() t = c[0][s] u = c[1][s] v = t.searchsorted(np.arange(t.max() + 1)) if v[0] > 0: # There were negative numbers: remove them u = u[v[0]:] v -= v[0] va = Varray(u, v) if sort: va.sort() if expand: return va.toArray() return va
def saveGreyImage(a, f, flip=True): """Save a 2D int array as a grey image. Parameters: - `a`: int array (nx,ny) with values in the range 0..255. These are the grey values of the pixels. - `f`: filename - `flip`: by default, the vertical axis is flipped, so that images are stored starting at the top. If your data already have the vertical axis downwards, use flip=False. """ a = at.checkArray(a, ndim=2, kind='u', allow='i').astype(np.uint8) c = np.flipud(a) c = np.dstack([c, c, c]) im = numpy2qimage(c) im.save(f)
def translate(self, vector): """Translate a 4x4 matrix by a (3,) vector. - `vector`: (3,) float array: the translation vector Changes the Matrix in place and also returns the result Example: >>> Matrix4().translate([1.,2.,3.]) matrix([[ 1., 0., 0., 0.], [ 0., 1., 0., 0.], [ 0., 0., 1., 0.], [ 1., 2., 3., 1.]]) """ vector = at.checkArray(vector, (3, ), 'f') self.trl += vector * self.rot return self