def test_sphere_union(self): a = CSG.sphere(center=(0., 0., 0.), radius=1.0, slices=64, stacks=32) b = CSG.sphere(center=(1.99, 0., 0.), radius=1.0, slices=64, stacks=32) c = a + b a.saveVTK('test_sphere_union_a.vtk') b.saveVTK('test_sphere_union_b.vtk') c.saveVTK('test_sphere_union.vtk')
def test_cube_union(self): a = CSG.cube() b = CSG.cube([0.5, 0.5, 0.0]) c = a + b c.saveVTK('test_cube_union.vtk') d = c.refine().refine() d.saveVTK('test_cube_union_refined_2x.vtk')
def test_sphere_cylinder_subtract(self): a = CSG.sphere(center=[0.5, 0.5, 0.5], radius=0.5, slices=8, stacks=4) b = CSG.cylinder(start=[0., 0., 0.], end=[1., 0., 0.], radius=0.3, slices=16) a.subtract(b).saveVTK('test_sphere_cylinder_subtract.vtk')
def test_bolt(self): shaft = CSG.cylinder(start=[0., 0., 0.], end=[1., 0., 0.], radius=0.1, slices=32) head = CSG.cone(start=[-0.12, 0., 0.], end=[0.10, 0., 0.], radius=0.25) notch1 = CSG.cube(center=[-0.10, 0., 0.], radius=[0.02, 0.20, 0.02]) notch2 = CSG.cube(center=[-0.10, 0., 0.], radius=[0.02, 0.02, 0.20]) bolt = shaft + head - notch1 - notch2 bolt.saveVTK('test_bolt.vtk')
def test_toPolygons(self): a = CSG.cube([0.5, 0.5, 0.0]) aPolys = a.toPolygons() b = CSG.sphere() bPolys = b.toPolygons() c = CSG.cylinder() cPolys = c.toPolygons()
def __init__(self, operation): self.faces = [] self.normals = [] self.vertices = [] self.colors = [] self.vnormals = [] self.list = -1 c = CSG.cube() b = CSG.sphere()#slices=32,stacks=16) a = CSG.tetra() #b = CSG.cylinder(radius=0.5, start=[0., 0., 0.], end=[0., 2., 0.])#,slices=16) #c = CSG.cylinder(radius=0.5, start=[0., 0., 0.], end=[0., 2., 0.]).rotate([0,0,1],90)#,slices=16) for p in a.polygons: p.shared = [1.0, 0.0, 0.0, 1.0] for p in b.polygons: p.shared = [0.0, 1.0, 0.0, 1.0] recursionlimit = sys.getrecursionlimit() sys.setrecursionlimit(10000) try: if operation == 'subtract': polygons = a.subtract(b).toPolygons() elif operation == 'union': polygons =a.union(c).toPolygons() elif operation == 'intersect': polygons = a.intersect(b).toPolygons() else: raise Exception('Unknown operation: \'%s\'' % operation) except RuntimeError as e: raise RuntimeError(e) sys.setrecursionlimit(recursionlimit) for polygon in polygons: n = polygon.plane.normal indices = [] for v in polygon.vertices: pos = [v.pos.x, v.pos.y, v.pos.z] if not pos in self.vertices: self.vertices.append(pos) self.vnormals.append([]) index = self.vertices.index(pos) indices.append(index) self.vnormals[index].append(v.normal) self.faces.append(indices) self.normals.append([n.x, n.y, n.z]) self.colors.append(polygon.shared) # setup vertex-normals ns = [] for vns in self.vnormals: n = Vector(0.0, 0.0, 0.0) for vn in vns: n = n.plus(vn) n = n.dividedBy(len(vns)) ns.append([a for a in n]) self.vnormals = ns
def __init__(self, operation): self.faces = [] self.normals = [] self.vertices = [] self.colors = [] self.vnormals = [] self.list = -1 a = CSG.cube() b = CSG.cylinder(radius=0.5, start=[0., -2., 0.], end=[0., 2., 0.]) for p in a.polygons: p.shared = [1.0, 0.0, 0.0, 1.0] for p in b.polygons: p.shared = [0.0, 1.0, 0.0, 1.0] recursionlimit = sys.getrecursionlimit() sys.setrecursionlimit(10000) try: if operation == 'subtract': polygons = a.subtract(b).toPolygons() elif operation == 'union': polygons = a.union(b).toPolygons() elif operation == 'intersect': polygons = a.intersect(b).toPolygons() else: raise Exception('Unknown operation: \'%s\'' % operation) except RuntimeError as e: raise RuntimeError(e) sys.setrecursionlimit(recursionlimit) for polygon in polygons: n = polygon.plane.normal indices = [] for v in polygon.vertices: pos = [v.pos.x, v.pos.y, v.pos.z] if not pos in self.vertices: self.vertices.append(pos) self.vnormals.append([]) index = self.vertices.index(pos) indices.append(index) self.vnormals[index].append(v.normal) self.faces.append(indices) self.normals.append([n.x, n.y, n.z]) self.colors.append(polygon.shared) # setup vertex-normals ns = [] for vns in self.vnormals: n = Vector(0.0, 0.0, 0.0) for vn in vns: n = n.plus(vn) n = n.dividedBy(len(vns)) ns.append([a for a in n]) self.vnormals = ns
def csgTSpin(rad, len, tolerance, scaling, detail): """ Generates a T-spin joint using the constructive solid geometry (csg) library pycsg. ---- ---- Top disk | | Top tube (Height is 'topheight') --- --- Mid disk | | Bottom tube (Height is 'botheight') ------- Bottom disk Horizontal axes are scaled with the joint width scaling factor (relative to r). Vertical axes are not (relative to rad). """ r = rad * scaling botheight, topheight = 0.4 * rad, 0.3 * rad #Need these two quantities to place visualization later. height = botheight + topheight bottom = -(height / 2) bottomdisk = CSG.cylinder(radius=0.8 * r + tolerance / 2, start=[0, bottom - tolerance / 2, 0], end=[0, bottom + tolerance / 2, 0], slices=detail) bottomtube = CSG.cylinder(radius = 0.8*r+tolerance/2, start = [0,bottom,0], end = [0,botheight,0], slices = detail) \ - CSG.cylinder(radius = 0.8*r-tolerance/2, start = [0,bottom,0], end = [0,botheight,0], slices = detail) middisk = CSG.cylinder(radius = 0.8*r+tolerance/2, start = [0,botheight-tolerance/2,0], end = [0,botheight+tolerance/2,0], slices = detail) \ - CSG.cylinder(radius = 0.6*r - tolerance/2, start = [0,botheight-tolerance/2,0], end = [0,botheight+tolerance/2,0], slices = detail) toptube = CSG.cylinder(radius = 0.6*r+tolerance/2, start = [0,botheight,0], end = [0,height,0], slices = detail) \ - CSG.cylinder(radius = 0.6*r-tolerance/2, start = [0,botheight,0], end = [0,height,0], slices = detail) topdisk = CSG.cylinder(radius = r+tolerance/2, start = [0,height-tolerance/2,0], end = [0,height+tolerance/2,0], slices = detail) \ - CSG.cylinder(radius = 0.6*r-tolerance/2, start = [0,height-tolerance/2,0], end = [0,height+tolerance/2,0], slices = detail) bond = CSG.cylinder(radius=r, start=[0, -(len / 2), 0], end=[0, len / 2, 0], slices=detail) minusjoint = bottomdisk + bottomtube + middisk + toptube + topdisk joint = bond - minusjoint return (csgtovtk(joint), botheight, topheight)
def translate_csg(inst): dim = len(inst) - 1 obj, desc = mesh_pop("translate") if dim == 2: off = inst[1:3] key = "%s%f%ftrans" % (desc, off[0], off[1]) else: off = inst[1:4] print(off) key = "%s%f%f%ftrans" % (desc, off[0], off[1], off[2]) newobj = cache_find(key) if newobj is not None: mesh_push(newobj, key) return if dim == 2: newobj = Object2d() newobj.vertices = np.empty([len(obj.vertices), 2], dtype=float) newobj.faces = obj.faces for i in range(len(obj.vertices)): newobj.vertices[i][0] = obj.vertices[i][0] + off[0] newobj.vertices[i][1] = obj.vertices[i][1] + off[1] else: newobj = CSG.clone(obj) newobj.translate(off) cache_put(key, newobj) mesh_push(newobj, key)
def shapeFromVTKPolyData(self, pdata, min_cell_area=1.e-8): """ Create a shape from a VTK PolyData object @param pdata vtkPolyData instance @param min_cell_area tolerance for cell areas @return shape @note field data will get lost """ # Store the cell connectivity as CSG polygons. numCells = pdata.GetNumberOfPolys() cells = pdata.GetPolys() cells.InitTraversal() ptIds = vtk.vtkIdList() polygons = [] for i in range(numCells): cells.GetNextCell(ptIds) npts = ptIds.GetNumberOfIds() verts = [] for j in range(npts): pointIndex = ptIds.GetId(j) pt = pdata.GetPoint(pointIndex) v = Vertex(Vector(pt[0], pt[1], pt[2])) verts.append(v) self.cleanPolygon(verts, min_cell_area) if len(verts) >= 3: polygons.append(Polygon(verts)) # Instantiate the shape. return CSG.fromPolygons(polygons)
def check_cylinder_intersections(filename): """Check if cylinder-cylinder intersections occur with CSG tools (BSP trees) Keyword arguments: filename -- the name of the file which should be checked for intersections """ edges = read_swc_to_edge_list(filename) numIntersections = 0 for combination in combinations(range(0, len(edges) - 2, 2), 2): p1 = edges[combination[0] + 0][0] p2 = edges[combination[0] + 1][1] d1 = edges[combination[0] + 0][2] d2 = edges[combination[0] + 1][2] p3 = edges[combination[1] + 0][0] p4 = edges[combination[1] + 1][1] d3 = edges[combination[1] + 0][2] d4 = edges[combination[1] + 1][2] t1 = edges[combination[0] + 0][3] t2 = edges[combination[0] + 1][3] t3 = edges[combination[1] + 0][3] t4 = edges[combination[1] + 1][3] # ignore soma cylinders (as they are error-prone) if (t1 == 1 or t2 == 1 or t3 == 1 or t4 == 1): continue # ignore self-loops (should actually never happen) if (p1 == p2 or p3 == p4): continue # ignore adjacent cylinders if (p1 == p3 or p1 == p4 or p2 == p3 or p2 == p4): continue # check for intersections a = CSG.cylinder(radius=0.5 * (d1 + d2), start=p1, end=p2, slices=4) b = CSG.cylinder(radius=0.5 * (d3 + d4), start=p3, end=p4, slices=4) polys = a.intersect(b) if polys.toPolygons(): print(polys.toPolygons()) print("Cylinders intersect in neuron with name %s" % filename) print("Cylinders: A (%f;%s;%s) and B(%f;%s;%s)" % (0.5 * (d1 + d2), p1, p2, 0.5 * (d3 + d4), p3, p4)) numIntersections = numIntersections + 1 return numIntersections > 0 and True or False
def to_csg(self): points = self.points polygons = [ CSGPolygon([CSGVertex(pos=points[:, i]) for i in face]) for face in self.faces_idxs ] return CSG.fromPolygons(polygons)
def Box(origin, lengths): """ Create box @param origin/low end of the box @param lengths lengths in x, y, and z """ center = [origin[i] + 0.5*lengths[i] for i in range(len(origin))] radius = [0.5*le for le in lengths] return CSG.cube(center=center, radius=radius)
def sequence_union(fov_polyhedrons): n = len(fov_polyhedrons) if n == 0: return CSG.fromPolygons([]) if n == 1: return fov_polyhedrons[0] unified_fov_polyhedron = fov_polyhedrons[0] for i in range(1, n): unified_fov_polyhedron = unified_fov_polyhedron.union(fov_polyhedrons[i]) return unified_fov_polyhedron
def cone_csg(inst): r, h, n = inst[1:4] key = "%f,%f%dcone" % (r, h, n) obj = cache_find(key) if obj is not None: mesh_push(obj, key) return obj = CSG.cone(start=[0, 0, 0], end=[0, 0, h], slices=n, radius=r) cache_put(key, obj) mesh_push(obj, key)
def get_fov_polyhedron_from_pts(pts): pts = [Vertex(p) for p in pts] face_index = [[0, 1, 2], [3, 0, 2], [4, 3, 2], [1, 4, 2], [1, 0, 4], [4, 0, 3]] # order is changed by pycsg faces = [] for face in face_index: faces.append(Polygon([pts[i].clone() for i in face])) polyhedron = CSG.fromPolygons(faces) return polyhedron
def form_mesh(vertices, faces): polygons = [] for face in faces: newvertices = [] for ind in face: newvertices.append( Vertex( Vector(vertices[ind][0], vertices[ind][1], vertices[ind][2]))) polygons.append(Polygon(newvertices)) return CSG.fromPolygons(polygons)
def getBoundarySurfaceInsideShape(self, shape, other): """ Return the portion of the surface that is inside another shape @param shape @param other other shape @return shape """ a = BSPNode(shape.clone().polygons) b = BSPNode(other.clone().polygons) b.invert() a.clipTo(b) return CSG.fromPolygons(a.allPolygons())
def cube_csg(inst): dim = inst[1:4] key = "%f,%f,%fcube" % (dim[0], dim[1], dim[2]) obj = cache_find(key) if obj is not None: mesh_push(obj, key) return half = [dim[0] / 2.0, dim[1] / 2.0, dim[2] / 2.0] obj = CSG.cube(center=half, radius=half) cache_put(key, obj) mesh_push(obj, key)
def sphere_csg(inst): r = inst[1] center = inst[2:] key = "%f,%f,%f,%f,sphere" % (r, center[0], center[1], center[2]) obj = cache_find(key) if obj is not None: mesh_push(obj, key) return obj = CSG.sphere(center=center, radius=r) cache_put(key, obj) mesh_push(obj, key)
def Sphere(radius, origin, n_theta=16, n_phi=8): """ Create sphere @param radius radius @param origin center of the sphere @param n_theta number of theta cells @param n_phi number of azimuthal cells """ return CSG.sphere(center=origin, radius=radius, slices=n_theta, stacks=n_phi)
def cascaded_union(fov_polyhedrons): # using divide and conquer to get union n = len(fov_polyhedrons) if n == 0: return CSG.fromPolygons([]) if n == 1: return fov_polyhedrons[0] left = cascaded_union(fov_polyhedrons[:n // 2]) right = cascaded_union(fov_polyhedrons[n // 2:]) unified_fov_polyhedron = left.union(right) return unified_fov_polyhedron
def ang_convert(span, steps): obj, desc = mesh_pop("ang_convert") #TODO Sliceing an object is overkill, just add more points result = None step = 1.0 * span / steps for i in range(steps): mask = CSG.generate_box_mesh([step * i, -100, -100], [step * (i + 1), 100, 100]) # TODO fix slice = CSG.boolean(obj, mask, "intersection") # TODO fix vertices = np.empty([len(slice.vertices), 3], dtype=float) for i in range(len(slice.vertices)): ang = slice.vertices[i][0] r = slice.vertices[i][1] vertices[i][0] = -r * math.cos(ang) vertices[i][1] = r * math.sin(ang) vertices[i][2] = slice.vertices[i][2] tmp = CSG.form_mesh(vertices, slice.faces) # TODO fix if result is None: result = tmp else: result = CSG.boolean(result, tmp, "union") # TODO fix mesh_push(result, "TODO")
def Cone(radius, origin, lengths, n_theta=16): """ Create cone @param radius radius @param origin location of the focal point @param lengths lengths of the cone @param n_theta number of theta cells """ ori = Vector(origin[0], origin[1], origin[2]) end = Vector(origin[0] + lengths[0], origin[1] + lengths[1], origin[2] + lengths[2]) return CSG.cone(start=ori, end=end, radius=radius, slices=n_theta)
def Cylinder(radius, origin, lengths, n_theta=16): """ Create cylinder @param radius radius @param origin center of low end disk @param lengths lengths of the cylinder along each axis @param n_theta number of theta cells """ ori = Vector(origin[0], origin[1], origin[2]) end = Vector(origin[0] + lengths[0], origin[1] + lengths[1], origin[2] + lengths[2]) return CSG.cylinder(start=ori, end=end, radius=radius, slices=n_theta)
def scale(s): obj, desc = mesh_pop("scale") dim = get_dimension(obj) if dim == 2: if type(s) is not list: s = [s, s] key = "%s%f%fscale" % (desc, s[0], s[1]) instructions.append(["scale", s[0], s[1]]) inst = instructions[-1] s = inst[1:3] else: if type(s) is not list: s = [s, s, s] key = "%s%f%f%fscale" % (desc, s[0], s[1], s[2]) instructions.append(["scale", s[0], s[1], s[2]]) inst = instructions[-1] s = inst[1:4] newobj = cache_find(key) if newobj is not None: mesh_push(newobj, key) return if dim == 3: newobj = CSG.clone(obj) for polygon in newobj.polygons: for vert in polygon.vertices: vert.pos.x *= s[0] vert.pos.y *= s[1] vert.pos.z *= s[2] cache_put(key, newobj) mesh_push(newobj, key) elif dim == 2: newobj = Object2d() newobj.vertices = np.empty([len(obj.vertices), 2], dtype=float) newobj.faces = obj.faces for i in range(len(newobj.vertices)): vertices[i][0] = obj.vertices[i][0] * s[0] vertices[i][1] = obj.vertices[i][1] * s[1] cache_put(key, newobj) mesh_push(newobj, key) else: message("Dimension %d not supported" % (dim))
def test_rotate_cylinder(self): b = CSG.cylinder() b.saveVTK('b.vtk') b.rotate(axis=[0.1, 0.2, 0.3], angleDeg=20.0) b.saveVTK('bRotated.vtk')
def test_translate_cylinder(self): b = CSG.cylinder() b.saveVTK('b.vtk') b.translate(disp=[0.1, 0.2, 0.3]) b.saveVTK('bTranslated.vtk')
def test_rotate_cube(self): a = CSG.cube() a.saveVTK('a.vtk') a.rotate(axis=[0.1, 0.2, 0.3], angleDeg=20.0) a.saveVTK('aRotated.vtk')
def load_stl(file): return CSG.readSTL(file)
def test_cube(self): a = CSG.cube(center=[0., 0., 0.], radius=[1., 2., 3.]) a.saveVTK('test_cube.vtk')
def test_cylinder(self): a = CSG.cylinder(start=[0., 0., 0.], end=[1., 2., 3.], radius=1.0, slices=8) a.saveVTK('test_cylinder.vtk')
from __future__ import print_function from icqsol.shapes.icqShapeManager import ShapeManager from csg.core import CSG from icqsol import util """ Test conversion from a shape to a list of polygons @author [email protected] """ shape_mgr = ShapeManager(file_format=util.VTK_FORMAT, vtk_dataset_type=util.POLYDATA) shp = shape_mgr.createShape('box', origin=[0., 0., 0.], lengths=[1., 1., 1.],) # check whether one can convert to a list of polygons polys = shape_mgr.shapeToPolygons(shp) # check whether each polygon can be cloned map(lambda p: p.clone(), polys) # check that we can load the polygons a = CSG.fromPolygons(polys) shp2 = shape_mgr.createShape('sphere', radius=1.0, origin=(0., 0., 0.), n_theta=5, n_phi=2) polys2 = shape_mgr.shapeToPolygons(shp2) a2 = CSG.fromPolygons(polys2)
def shapeFromPolygons(self, polys): return CSG.fromPolygons(polys)
def test_cube_subtract(self): a = CSG.cube() b = CSG.cube([0.5, 0.5, 0.0]) c = a - b c.saveVTK('test_cube_subtract.vtk')
def test_cube_intersect(self): a = CSG.cube() b = CSG.cube([0.5, 0.5, 0.0]) c = a * b c.saveVTK('test_cube_intersect.vtk')
def get_fov_polyhedron(pos, theta, alpha=45, R=0.1): # get polyhedron from pos and theta pts = [Vertex([0, 0, 0])] dx = [-1, 1] dz = [-1, 1] for i in range(2): for j in range(2): pts.append(Vertex([ dz[j] * R * np.sin(alpha / 2 * np.pi / 180), dx[i] * R * np.sin(alpha / 2 * np.pi / 180), R * np.cos(alpha / 2 * np.pi / 180), ])) face_index = [[2, 1, 0], [4, 2, 0], [3, 4, 0], [1, 3, 0], [1, 2, 3], [3, 2, 4]] faces = [] for face in face_index: faces.append(Polygon([pts[i].clone() for i in face])) polyhedron = CSG.fromPolygons(faces) # finish creating a polyhedron before rotation and translation r = Rotation.from_euler('ZYX', theta, degrees=False) rotvec = r.as_rotvec() angle = np.linalg.norm(rotvec) if angle == 0: rotvec = [0, 0, 1] else: rotvec /= angle # rotation and translation polyhedron.rotate(rotvec, angle * 180 / np.pi) polyhedron.rotate([0, 0, 1], 90) polyhedron.rotate([0, 1, 0], 180) polyhedron.translate(pos) # f = open("fovs.csv", "a") # polygons = polygon.toPolygons() # f.write(str(polygons[0].vertices[2].pos[0]) + ',' # + str(polygons[0].vertices[2].pos[1]) + ',' # + str(polygons[0].vertices[2].pos[2]) + ',') # f.write(str(polygons[0].vertices[1].pos[0]) + ',' # + str(polygons[0].vertices[1].pos[1]) + ',' # + str(polygons[0].vertices[1].pos[2]) + ',') # f.write(str(polygons[0].vertices[0].pos[0]) + ',' # + str(polygons[0].vertices[0].pos[1]) + ',' # + str(polygons[0].vertices[0].pos[2]) + ',') # f.write(str(polygons[2].vertices[0].pos[0]) + ',' # + str(polygons[2].vertices[0].pos[1]) + ',' # + str(polygons[2].vertices[0].pos[2]) + ',') # f.write(str(polygons[2].vertices[1].pos[0]) + ',' # + str(polygons[2].vertices[1].pos[1]) + ',' # + str(polygons[2].vertices[1].pos[2]) + '\n') return polyhedron
def test_sphere(self): a = CSG.sphere(center=[0.0, 0.0, 0.0], radius=1.0, slices=4, stacks=3) a.saveVTK("test_sphere.vtk") b = a.refine() b.saveVTK("test_sphere_refined.vtk")
def test_sphere(self): a = CSG.sphere(center=[0., 0., 0.], radius=1., slices=4, stacks=3) a.saveVTK('test_sphere.vtk') b = a.refine() b.saveVTK('test_sphere_refined.vtk')
def test_cube_union(self): a = CSG.cube() b = CSG.cube([0.5, 0.5, 0.0]) c = a + b c.saveVTK('test_cube_union.vtk')
def test_sphere_cylinder_subtract(self): a = CSG.sphere(center=[0.5, 0.5, 0.5], radius=0.5, slices=8, stacks=4) b = CSG.cylinder(start=[0.,0.,0.], end=[1.,0.,0.], radius=0.3, slices=16) a.subtract(b).saveVTK('test_sphere_cylinder_subtract.vtk')
def test_translate_cube(self): a = CSG.cube() a.saveVTK('a.vtk') a.translate(disp=[0.1, 0.2, 0.3]) a.saveVTK('aTranslated.vtk')
from __future__ import print_function from icqsol.shapes.icqShapeManager import ShapeManager from csg.core import CSG from icqsol import util """ Test construction of shape from a list of polygons @author [email protected] """ shape_mgr = ShapeManager(file_format=util.VTK_FORMAT, vtk_dataset_type=util.POLYDATA) cube = CSG.cube() # Should we test if shp == cube? shp = shape_mgr.shapeFromPolygons(cube)