diff options
author | Anthony LaTorre <tlatorre9@gmail.com> | 2011-08-16 17:07:52 -0400 |
---|---|---|
committer | Anthony LaTorre <tlatorre9@gmail.com> | 2011-08-16 17:07:52 -0400 |
commit | 54d7d1efe215337d121813e27cd4909b9a76e912 (patch) | |
tree | 7865db28adf2f9328fb9dcbbca8f8f125ecad40c /make.py | |
parent | fd2e841c4c40f9e46258ac8d11c32c2204cddd5b (diff) | |
download | chroma-54d7d1efe215337d121813e27cd4909b9a76e912.tar.gz chroma-54d7d1efe215337d121813e27cd4909b9a76e912.tar.bz2 chroma-54d7d1efe215337d121813e27cd4909b9a76e912.zip |
add linear_extrude() function to make.py. rotate_extrude() now takes the number of rotational steps to extrude instead of the angle step size. updated documention in make.py.
Diffstat (limited to 'make.py')
-rw-r--r-- | make.py | 144 |
1 files changed, 93 insertions, 51 deletions
@@ -1,67 +1,109 @@ import numpy as np from geometry import Mesh from transform import rotate +from itertoolset import * -def rotate_extrude(x, y, theta=np.pi/32, remove_duplicate_vertices=True): - x, y, = np.asarray(x), np.asarray(y) +def mesh_grid(grid): + return np.vstack(zip(grid[:-1].flatten(),grid[1:].flatten(),np.roll(grid[1:],-1,1).flatten()) + zip(grid[:-1].flatten(),np.roll(grid[1:],-1,1).flatten(),np.roll(grid[:-1],-1,1).flatten())) - if len(x.shape) != 1 or len(y.shape) != 1 or x.size != y.size: - raise ValueError('shape mismatch') +def linear_extrude(x1, y1, height, x2=None, y2=None): + """ + Return the solid mesh formed by linearly extruding the polygon formed by + the x and y points `x1` and `y1` by a distance `height`. If `x2` and `y2` + are given extrude by connecting the points `x1` and `y1` to `x2` and `y2`; + this allows the creation of tapered solids. - points = np.zeros((len(x),3), dtype=np.float32) - points[:,0] = x - points[:,1] = y + .. note:: + The path traced by the points `x` and `y` should go counter-clockwise, + otherwise the mesh will be inside out. - angles = np.arange(0, 2*np.pi, theta) + Example: + >>> # create a hexagon prism + >>> angles = np.linspace(0, 2*np.pi, 6, endpoint=False) + >>> m = linear_extrude(np.cos(angles), np.sin(angles), 5.0) + """ + if len(x1) != len(y1): + raise Exception('`x` and `y` arrays must have the same length.') - vertices = np.empty((len(points)*len(angles), 3), dtype=np.float32) - triangles = np.zeros((len(angles)*(len(points)-1)*2, 3), dtype=np.int32) + if x2 is None: + x2 = x1 - step = len(points) - 1 + if y2 is None: + y2 = y1 - for i, angle in enumerate(angles): - this_slice = i*len(points) - vertices[this_slice:this_slice+len(points)] = rotate(points, angle, (0,-1,0)) + if len(x2) != len(y2) or len(x2) != len(x1): + raise Exception('`x` and `y` arrays must have the same length.') - start = 2*i*step - next_slice = ((i+1) % len(angles))*len(points) + n = len(x1) - triangles[start:start+step,0] = np.arange(this_slice, this_slice+len(points)-1) - triangles[start:start+step,1] = np.arange(next_slice, next_slice+len(points)-1) - triangles[start:start+step,2] = np.arange(this_slice+1, this_slice+len(points)) + vertex_iterators = [izip(repeat(0,n),repeat(0,n),repeat(-height/2.0,n)),izip(x1,y1,repeat(-height/2.0,n)),izip(x2,y2,repeat(height/2.0,n)),izip(repeat(0,n),repeat(0,n),repeat(height/2.0,n))] - triangles[start+step:start+2*step,0] = np.arange(next_slice, next_slice+len(points)-1) - triangles[start+step:start+2*step,2] = np.arange(this_slice+1, this_slice+len(points)) - triangles[start+step:start+2*step,1] = np.arange(next_slice+1, next_slice+len(points)) + vertices = np.fromiter(flatten(roundrobin(*vertex_iterators)), float) + vertices = vertices.reshape((len(vertices)//3,3)) - return Mesh(vertices, triangles, remove_duplicate_vertices=remove_duplicate_vertices) + triangles = mesh_grid(np.arange(len(vertices)).reshape((len(x1),len(vertices)//len(x1))).transpose()[::-1]) + + return Mesh(vertices, triangles, remove_duplicate_vertices=True) + +def rotate_extrude(x, y, nsteps=64): + """ + Return the solid mesh formed by extruding the profile defined by the x and + y points `x` and `y` around the y axis. + + .. note:: + The path traced by the points `x` and `y` should go counter-clockwise, + otherwise the mesh will be inside out. + """ + if len(x) != len(y): + raise Exception('`x` and `y` arrays must have the same length.') + + points = np.array([x,y,np.zeros(len(x))]).transpose() + + steps = np.linspace(0, 2*np.pi, nsteps, endpoint=False) + vertices = np.vstack([rotate(points,angle,(0,-1,0)) for angle in steps]) + triangles = mesh_grid(np.arange(len(vertices)).reshape((len(steps),len(points))).transpose()[::-1]) + + return Mesh(vertices, triangles, remove_duplicate_vertices=True) def cube(size=1): - a = np.sqrt(size**2/2.0) - x = [0,a,a,0] - y = [-size/2.0,-size/2.0,size/2.0,size/2.0] - return rotate_extrude(x, y, np.pi/2) - -def cylinder(radius1=1, radius2=1, height=2, theta=np.pi/32): - x = [0,radius1,radius2,0] - y = [-height/2.0, -height/2.0, height/2.0, height/2.0] - return rotate_extrude(x, y, theta) - -def segmented_cylinder(radius, height=2, theta=np.pi/32, n=50): - x = np.concatenate((np.linspace(0, radius, n, endpoint=False), - [radius] * n, - np.linspace(radius, 0, n, endpoint=False), - [0.0])) - y = np.concatenate(([-height/2.0] * n, - np.linspace(-height/2.0, height/2.0, n, endpoint=False), - [height/2.0] * (n+1))) - return rotate_extrude(x, y, theta) - -def sphere(radius=1, theta=np.pi/32): - profile_angles = np.arange(-np.pi/2, np.pi/2+theta, theta) - return rotate_extrude(radius*np.cos(profile_angles), radius*np.sin(profile_angles), theta) - -def torus(radius=1, offset=3, phi=np.pi/32, theta=np.pi/32): - profile_angles = np.arange(0, 2*np.pi+phi, phi) - x, y = np.cos(profile_angles), np.sin(profile_angles) - return rotate_extrude(x + offset, y) + "Return a cube mesh whose sides have length `size`." + return linear_extrude([-size/2.0,size/2.0,size/2.0,-size/2.0],[-size/2.0,-size/2.0,size/2.0,size/2.0], height=size) + +def cylinder(radius=1, height=2, radius2=None, nsteps=64): + """ + Return a cylinder mesh with a radius of length `radius`, and a height of + length `height`. If `radius2` is specified, return a cone shaped cylinder + with bottom radius `radius`, and top radius `radius2`. + """ + if radius2 is None: + radius2 = radius + + return rotate_extrude([0,radius,radius2,0], [-height/2.0, -height/2.0, height/2.0, height/2.0], nsteps) + +def segmented_cylinder(radius, height=2, nsteps=64, nsegments=100): + """ + Return a cylinder mesh segmented into `nsegments` points along its profile. + """ + nsegments_radius = int((nsegments*radius/(2*radius+height))/2) + nsegments_height = int((nsegments*height/(2*radius+height))/2) + x = np.concatenate([np.linspace(0,radius,nsegments_radius,endpoint=False),[radius]*nsegments_height,np.linspace(radius,0,nsegments_radius,endpoint=False),[0]]) + y = np.concatenate([[-height/2.0]*nsegments_radius,np.linspace(-height/2.0,height/2.0,nsegments_height,endpoint=False),[height/2.0]*(nsegments_radius+1)]) + return rotate_extrude(x, y, nsteps) + +def sphere(radius=1, nsteps=64): + "Return a sphere mesh." + profile_angles = np.linspace(-np.pi/2, np.pi/2, nsteps) + return rotate_extrude(radius*np.cos(profile_angles), radius*np.sin(profile_angles), nsteps) + +def torus(radius=1, offset=3, nsteps=64, circle_steps=None): + """ + Return a torus mesh. `offset` is the distance from the center of the torus + to the center of the torus barrel. `radius` is the radius of the torus + barrel. `nsteps` is the number of steps in the rotational extrusion of the + circle. `circle_steps` if specified is the number of steps around the + circumference of the torus barrel, else it defaults to `nsteps`. + """ + if circle_steps is None: + circle_steps = nsteps + profile_angles = np.linspace(0, 2*np.pi, circle_steps) + return rotate_extrude(np.cos(profile_angles) + offset, np.sin(profile_angles), nsteps) |