def preview(self, event): """ @brief Load a downsampled version of the full ASDF """ params = self.get_params(get_bounds=False) if params is None: return for p in params: exec('{0} = params["{0}"]'.format(p)) voxels = ni * nj * nk shift = int(ceil(log(voxels / 128.**3, 8))) full = Region( (0, 0, 0), (ni-1, nj-1, nk-1), 1, dummy=True ) libfab.build_arrays( full, 0, 0, 0, ni*mm, nj*mm, nk*mm ) full.free_arrays = True asdf = ASDF( libfab.import_vol_region( self.filename, ni, nj, nk, full, shift, density, True, close_boundary ) ) mesh = asdf.triangulate() koko.FRAME.get_menu('View', '3D').Check(True) koko.APP.render_mode('3D') koko.GLCANVAS.load_mesh(mesh) koko.GLCANVAS.snap = True
def refine_math(self): """ @brief Refines a mesh based on a math tree @details Splits the mesh's bounding box then renders both subregions at a higher detail level, saving them in self.children """ region = Region( (self.X.lower / self.source.scale, self.Y.lower / self.source.scale, self.Z.lower / self.source.scale), (self.X.upper / self.source.scale, self.Y.upper / self.source.scale, self.Z.upper / self.source.scale), depth=self.source.depth+1 ) subregions = region.split() meshes = [] for s in subregions: asdf = self.source.expr.asdf( region=s, mm_per_unit=self.source.scale ) mesh = asdf.triangulate() mesh.source = Struct( type=MathTree, expr=self.source.expr.clone(), depth=self.source.depth+1, scale=self.source.scale ) meshes.append(mesh) self.children = meshes libfab.free_mesh(self.ptr) self.ptr = None
def make_flat_image(self, expr, scale): """ @brief Renders a flat single image @param expr MathTree expression @returns An Image object """ region = Region( (expr.xmin-self.cad.border*expr.dx, expr.ymin-self.cad.border*expr.dy, 0), (expr.xmax+self.cad.border*expr.dx, expr.ymax+self.cad.border*expr.dy, 0), scale ) koko.FRAME.status = 'Rendering with libfab' self.output += ">> Rendering image with libfab\n" start = datetime.now() img = expr.render(region, interrupt=self.c_event, mm_per_unit=self.cad.mm_per_unit) img.color = expr.color dT = datetime.now() - start self.output += "# libfab render time: %s\n" % dT return img
def run(self, cad): """ @brief Generates ASDF from cad structure @param cad Input cad data structure @returns Dictionary with 'asdf' defined """ koko.FRAME.status = 'Generating ASDF' values = self.get_values() if not values: return False # Render the expression into an image expr = cad.shape zmin = expr.zmin if expr.zmin else 0 zmax = expr.zmax if expr.zmax else 0 dz = zmax - zmin border = cad.border region = Region((expr.xmin - border * expr.dx, expr.ymin - border * expr.dy, zmin - border * dz), (expr.xmax + border * expr.dx, expr.ymax + border * expr.dy, zmax + border * dz), values['res'] * cad.mm_per_unit) self.asdf = expr.asdf(region=region, mm_per_unit=cad.mm_per_unit) koko.FRAME.status = '' return {'asdf': self.asdf}
def render(self, region=None, resolution=None, mm_per_unit=None, threads=8, interrupt=None): """ @brief Renders a math tree into an Image @param region Evaluation region (if None, taken from expression bounds) @param resolution Render resolution in voxels/unit @param mm_per_unit Real-world scale @param threads Number of threads to use @param interrupt threading.Event that aborts rendering if set @returns Image data structure """ if region is None: if self.dx is None or self.dy is None: raise Exception('Unknown render region!') elif resolution is None: raise Exception('Region or resolution must be provided!') region = Region( (self.xmin, self.ymin, self.zmin if self.zmin else 0), (self.xmax, self.ymax, self.zmax if self.zmax else 0), resolution) try: float(mm_per_unit) except ValueError, TypeError: raise ValueError('mm_per_unit must be a number')
def bounding_region(self, resolution, alpha=0, beta=0): """ @brief Finds a bounding region with the given rotation @param resolution Region resolution (voxels/unit) @param alpha Rotation about Z axis @param beta Rotation about X axis """ b = self.bounds(alpha, beta) return Region((b.xmin, b.ymin, b.zmin), (b.xmax, b.ymax, b.zmax), resolution)
def make_asdf(self, expr, flat=False): ''' Renders an expression to an ASDF ''' if flat: region = Region((expr.xmin - self.cad.border * expr.dx, expr.ymin - self.cad.border * expr.dy, 0), (expr.xmax + self.cad.border * expr.dx, expr.ymax + self.cad.border * expr.dy, 0), self.resolution * self.cad.mm_per_unit) else: region = Region((expr.xmin - self.cad.border * expr.dx, expr.ymin - self.cad.border * expr.dy, expr.zmin - self.cad.border * expr.dz), (expr.xmax + self.cad.border * expr.dx, expr.ymax + self.cad.border * expr.dy, expr.zmax + self.cad.border * expr.dz), self.resolution * self.cad.mm_per_unit) asdf = expr.asdf(region=region, mm_per_unit=self.cad.mm_per_unit, interrupt=self.c_event) return asdf
def make_mesh(self, expr): """ @brief Converts an expression into a mesh. @returns The mesh, or False if failure. """ self.output += '>> Generating triangulated mesh\n' DEPTH = 0 while DEPTH <= 4: region = Region( (expr.xmin - self.cad.border*expr.dx, expr.ymin - self.cad.border*expr.dy, expr.zmin - self.cad.border*expr.dz), (expr.xmax + self.cad.border*expr.dx, expr.ymax + self.cad.border*expr.dy, expr.zmax + self.cad.border*expr.dz), depth=DEPTH ) koko.FRAME.status = 'Rendering to ASDF' start = datetime.now() asdf = expr.asdf(region=region, mm_per_unit=self.cad.mm_per_unit, interrupt=self.c_event) self.output += '# ASDF render time: %s\n' % (datetime.now() - start) if self.event.is_set(): return koko.FRAME.output = self.output koko.FRAME.status = 'Triangulating' start = datetime.now() mesh = asdf.triangulate(interrupt=self.c_event) if mesh.vcount: break else: DEPTH += 1 mesh.source = Struct( type=MathTree, expr=expr.clone(), depth=DEPTH, scale=self.cad.mm_per_unit ) if self.event.is_set(): return self.output += '# Meshing time: %s\n' % (datetime.now() - start) self.output += "Generated {:,} vertices and {:,} triangles\n".format( mesh.vcount if mesh else 0, mesh.tcount if mesh else 0) koko.FRAME.output = self.output return mesh
def make_image(self, expr): ''' Renders a single expression, returning the image ''' zmin = self.cad.zmin if self.cad.zmin else 0 zmax = self.cad.zmax if self.cad.zmax else 0 region = Region((self.cad.xmin, self.cad.ymin, zmin), (self.cad.xmax, self.cad.ymax, zmax), self.resolution * self.cad.mm_per_unit) img = expr.render(region, mm_per_unit=self.cad.mm_per_unit, interrupt=self.c_event) img.color = expr.color return img
def collapse_tree(self): """ @brief Re-renders from the source math tree """ region = Region( (self.X.lower / self.source.scale, self.Y.lower / self.source.scale, self.Z.lower / self.source.scale), (self.X.upper / self.source.scale, self.Y.upper / self.source.scale, self.Z.upper / self.source.scale), depth=self.source.depth ) asdf = self.source.expr.asdf( region=region, mm_per_unit=self.source.scale ) return asdf.triangulate()
def make_image(self, expr, zmin, zmax): """ @brief Renders an expression @param expr MathTree expression @param zmin Minimum Z value (arbitrary units) @param zmax Maximum Z value (arbitrary units) @returns None for a null image, False for a failure, the Image if success. """ # Adjust view bounds based on cad file scale # (since view bounds are in mm, we have to convert to the cad # expression's unitless measure) xmin = self.view.xmin xmax = self.view.xmax ymin = self.view.ymin ymax = self.view.ymax if expr.xmin is None: xmin = xmin else: xmin = max(xmin, expr.xmin - self.cad.border*expr.dx) if expr.xmax is None: xmax = xmax else: xmax = min(xmax, expr.xmax + self.cad.border*expr.dx) if expr.ymin is None: ymin = ymin else: ymin = max(ymin, expr.ymin - self.cad.border*expr.dy) if expr.ymax is None: ymax = ymax else: ymax = min(ymax, expr.ymax + self.cad.border*expr.dy) region = Region( (xmin, ymin, zmin), (xmax, ymax, zmax), self.view.pixels_per_unit ) koko.FRAME.status = 'Rendering with libfab' self.output += ">> Rendering image with libfab\n" start = datetime.now() img = expr.render(region, interrupt=self.c_event, mm_per_unit=self.cad.mm_per_unit) img.color = expr.color dT = datetime.now() - start self.output += "# libfab render time: %s\n" % dT return img
def run(self, event): params = self.get_params() for p in params: exec('{0} = params["{0}"]'.format(p)) full = Region( (imin, jmin, kmin), (imax, jmax, kmax), 1, dummy=True ) libfab.build_arrays( full, imin*mm, jmin*mm, kmin*mm, (imax+1)*mm, (jmax+1)*mm, (kmax+1)*mm ) # Position the lower corner based on imin, jmin, kmin full.imin = imin full.jmin = jmin full.kmin = kmin full.free_arrays = True koko.FRAME.status = 'Importing ASDF' wx.Yield() asdf = ASDF( libfab.import_vol_region( self.filename, ni, nj, nk, full, 0, density, True, close_boundary ) ) koko.FRAME.status = 'Triangulating' wx.Yield() mesh = asdf.triangulate() koko.FRAME.get_menu('View', '3D').Check(True) koko.APP.render_mode('3D') koko.GLCANVAS.load_mesh(mesh) koko.GLCANVAS.snap = True koko.FAB.set_input(asdf) koko.FRAME.status = '' koko.APP.mode = 'asdf' koko.APP.filename = None koko.APP.savepoint(False)
def asdf(self, region=None, resolution=None, mm_per_unit=None, merge_leafs=True, interrupt=None): """ @brief Constructs an ASDF from a math tree. @details Runs in up to eight threads. @param region Evaluation region (if None, taken from expression bounds) @param resolution Render resolution in voxels/unit @param mm_per_unit Real-world scale @param merge_leafs Boolean determining whether leaf cells are combined @param interrupt threading.Event that aborts rendering if set @returns ASDF data structure """ if region is None: if not self.bounded: raise Exception('Unknown render region!') elif resolution is None: raise Exception('Region or resolution must be provided!') region = Region( (self.xmin, self.ymin, self.zmin if self.zmin else 0), (self.xmax, self.ymax, self.zmax if self.zmax else 0), resolution ) if interrupt is None: interrupt = threading.Event() # Shared flag to interrupt rendering halt = ctypes.c_int(0) # Split the region into up to 8 sections split = region.octsect(all=True) subregions = [split[i] for i in range(8) if split[i] is not None] ids = [i for i in range(8) if split[i] is not None] threads = len(subregions) clones = [self.clone() for i in range(threads)] packed = [libfab.make_packed(c.ptr) for c in clones] # Generate a root for the tree asdf = ASDF(libfab.asdf_root(packed[0], region), color=self.color) # Multithread the solver process q = Queue.Queue() args = zip(packed, ids, subregions, [q]*threads) # Helper function to construct a single branch def construct_branch(ptree, id, region, queue): asdf = libfab.build_asdf(ptree, region, merge_leafs, halt) queue.put((id, asdf)) # Run the constructor in parallel to make the branches multithread(construct_branch, args, interrupt, halt) for p in packed: libfab.free_packed(p) # Attach the branches to the root for s in subregions: try: id, branch = q.get_nowait() except Queue.Empty: break else: asdf.ptr.contents.branches[id] = branch libfab.get_d_from_children(asdf.ptr) libfab.simplify(asdf.ptr, merge_leafs) # Set a scale on the ASDF if one was provided if mm_per_unit is not None: asdf.rescale(mm_per_unit) return asdf
def asdf(self, region=None, resolution=None, mm_per_unit=None, merge_leafs=True, interrupt=None): """ @brief Constructs an ASDF from a math tree. @details Runs in up to eight threads. @param region Evaluation region (if None, taken from expression bounds) @param resolution Render resolution in voxels/unit @param mm_per_unit Real-world scale @param merge_leafs Boolean determining whether leaf cells are combined @param interrupt threading.Event that aborts rendering if set @returns ASDF data structure """ if region is None: if not self.bounded: raise Exception('Unknown render region!') elif resolution is None: raise Exception('Region or resolution must be provided!') region = Region( (self.xmin, self.ymin, self.zmin if self.zmin else 0), (self.xmax, self.ymax, self.zmax if self.zmax else 0), resolution) if interrupt is None: interrupt = threading.Event() # Shared flag to interrupt rendering halt = ctypes.c_int(0) # Split the region into up to 8 sections split = region.octsect(all=True) subregions = [split[i] for i in range(8) if split[i] is not None] ids = [i for i in range(8) if split[i] is not None] threads = len(subregions) clones = [self.clone() for i in range(threads)] packed = [libfab.make_packed(c.ptr) for c in clones] # Generate a root for the tree asdf = ASDF(libfab.asdf_root(packed[0], region), color=self.color) asdf.lock.acquire() # Multithread the solver process q = Queue.Queue() args = zip(packed, ids, subregions, [q] * threads) # Helper function to construct a single branch def construct_branch(ptree, id, region, queue): asdf = libfab.build_asdf(ptree, region, merge_leafs, halt) queue.put((id, asdf)) # Run the constructor in parallel to make the branches multithread(construct_branch, args, interrupt, halt) for p in packed: libfab.free_packed(p) # Attach the branches to the root for s in subregions: try: id, branch = q.get_nowait() except Queue.Empty: break else: # Make sure we didn't get a NULL pointer back # (which could occur if the halt flag was raised) try: branch.contents except ValueError: asdf.lock.release() return None asdf.ptr.contents.branches[id] = branch libfab.get_d_from_children(asdf.ptr) libfab.simplify(asdf.ptr, merge_leafs) asdf.lock.release() # Set a scale on the ASDF if one was provided if mm_per_unit is not None: asdf.rescale(mm_per_unit) return asdf