def generateBitmap(operation, orientation, regionmap, z): """ generate bitmap for an operation's z-height level """ global scale length = len(regionmap) width = len(regionmap[0]) bitmap = [[False for x in range(width) ]for y in range(length)] operationList = [int(region) for region in operation["regionlist"].split(" ") if region != ""] for x in range(length): for y in range(width): if regionmap[x][y] in operationList: if toBeMachined(orientation, x, y, z): bitmap[x][y] = [0] #shoundnt be cut else: bitmap[x][y] = [1] #should be cut else: bitmap[x][y] = [1] # print int(bitmap[x][y][0]), # print "-" # print "---------------------" img = Image(length, width, scale, channels=1, depth=8) img.array = np.array(bitmap, dtype=np.uint8) return img
def generate_bitmap(operation, orientation, regionmap, z): """ generate bitmap for an operation's z-height level """ global SCALE length = len(regionmap) width = len(regionmap[0]) bitmap = [[False for x in range(width)]for y in range(length)] operationList = [int(region) for region in operation["regionlist"].split(" ") if region != ""] for x in range(length): for y in range(width): if regionmap[x][y] in operationList: if to_be_machined(orientation, x, y, z): bitmap[x][y] = [0] #shoundnt be cut else: bitmap[x][y] = [1] #should be cut else: bitmap[x][y] = [1] # print int(bitmap[x][y][0]), # print "-" # print "---------------------" img = Image(length, width, SCALE, channels=1, depth=8) img.array = np.array(bitmap, dtype=np.uint8) return img
def render_multi(self, region=None, threads=8, alpha=0, beta=0, resolution=10): """ @brief Renders to an image @param region Render region (default bounding box) @param threads Threads to use (default 8) @param alpha Rotation about Z axis (default 0) @param beta Rotation about X axis (default 0) @resolution Resolution in voxels per mm @returns A tuple with a height-map, shaded image, and image with colored normals """ if region is None: region = self.bounding_region(resolution, alpha, beta) depth = Image( region.ni, region.nj, channels=1, depth=16, ) shaded = Image( region.ni, region.nj, channels=1, depth=16, ) normals = Image( region.ni, region.nj, channels=3, depth=8, ) subregions = region.split_xy(threads) M = (ctypes.c_float * 4)(cos(radians(alpha)), sin(radians(alpha)), cos(radians(beta)), sin(radians(beta))) args = [(self.ptr, s, M, depth.pixels, shaded.pixels, normals.pixels) for s in subregions] multithread(libfab.render_asdf_shaded, args) for image in [depth, shaded, normals]: image.xmin = region.X[0] image.xmax = region.X[region.ni] image.ymin = region.Y[0] image.ymax = region.Y[region.nj] image.zmin = region.Z[0] image.zmax = region.Z[region.nk] return depth, shaded, normals
def generate_gcode(*args): """ generate gcode for heightmap in a given orientation note: [toBeImproved]heightmap passed as formatted string isnt a good way to structure the code """ arg = args[0][0] orientation = args[0][1] global diameter, step, feed command = ["./scripts/operationPlanner"] p = Popen(command, stdout=PIPE, stdin=PIPE, stderr=STDOUT) generatorOutput = p.communicate(input=arg)[0] plan = json.loads(generatorOutput) length, width, zmax, scale, array = get_heightmap(arg) values = { 'diameter': diameter, 'offsets': -1, 'overlap': 0.5, 'step': step, 'feed': feed, 'plunge': 2.5, 'jog': 2, 'zmax' : zmax, 'zmin' : 0 } img = Image(length, width, scale, zmax=zmax, array=array, provides_array=True, channels=1, depth=16) #getting paths paths = run_rough(img, values, plan, orientation) #generating gcode from paths gcode = generate_gcode_from_path(paths, values["feed"], values["jog"], values["plunge"]) return gcode
def __init__(self, parent): FabPanel.__init__(self, parent) self.construct('Lattice', [ ('Resolution (pixels/mm)\n', 'res', float, lambda f: f > 0)]) self.res.Bind(wx.EVT_TEXT, self.parent.update) self.img = Image(0,0)
def load(self): """ @brief Loads the current design file @details The file is defined by self.directory and self.filename """ self.clear() path = os.path.join(self.directory, self.filename) if path[-4:] == '.cad': with open(path, 'r') as f: text = f.read() if text.split('\n')[0] == '## Geometry header ##': koko.PRIMS.reconstruct(eval(text.split('\n')[1])) koko.PRIMS.undo_stack = [koko.PRIMS.reconstructor()] text = '\n'.join(text.split('\n')[3:]) koko.EDITOR.text = text koko.FRAME.status = 'Loaded .cad file' self.mode = 'cad/cam' self.first_render = True self.savepoint(True) elif path[-4:] == '.png': self.mode = 'cam' img = Image.load(path) koko.CANVAS.load_image(img) koko.GLCANVAS.load_image(img) koko.FAB.set_input(img) wx.CallAfter(self.snap_bounds) elif path[-4:] == '.stl': self.mode = 'cam' mesh = Mesh.load(path) koko.GLCANVAS.load_mesh(mesh) wx.CallAfter(self.snap_bounds) elif path[-5:] == '.asdf': self.render_mode('3D') self.mode = 'cam' msg = dialogs.display_message('Loading...', 'Loading ASDF.') msg.Raise() wx.Yield() asdf = ASDF.load(path) msg.txt.SetLabel('Triangulating') wx.Yield() mesh = asdf.triangulate() mesh.source = Struct(type=ASDF, file=path, depth=0) msg.Destroy() koko.GLCANVAS.load_mesh(mesh) koko.FAB.set_input(asdf)
def render_distance(self, resolution=10): """ @brief Draws the ASDF as a distance field (used for debugging) @param resolution Image resolution @returns An 16-bit, 1-channel Image showing the distance field """ region = self.bounding_region(resolution) image = Image(region.ni, region.nj, channels=1, depth=16) image.xmin = region.X[0] image.xmax = region.X[region.ni] image.ymin = region.Y[0] image.ymax = region.Y[region.nj] image.zmin = region.Z[0] image.zmax = region.Z[region.nk] minimum = libfab.asdf_get_min(self.ptr) maximum = libfab.asdf_get_max(self.ptr) libfab.draw_asdf_distance(self.ptr, region, minimum, maximum, image.pixels) return image
def export_png(self): ''' Exports a png using libtree. ''' if self.make_heightmap: out = self.make_image(self.cad.shape) else: i = 0 imgs = [] for e in self.cad.shapes: if self.event.is_set(): return img = self.make_image(e) if img is not None: imgs.append(img) i += 1 self.window.progress = i*90/len(self.cad.shapes) out = Image.merge(imgs) if self.event.is_set(): return self.window.progress = 90 out.save(self.filename) self.window.progress = 100
def export_png(self): ''' Exports a png using libtree. ''' if self.make_heightmap: out = self.make_image(self.cad.shape) else: i = 0 imgs = [] for e in self.cad.shapes: if self.event.is_set(): return img = self.make_image(e) if img is not None: imgs.append(img) i += 1 self.window.progress = i * 90 / len(self.cad.shapes) out = Image.merge(imgs) if self.event.is_set(): return self.window.progress = 90 out.save(self.filename) self.window.progress = 100
def render_distance(self, resolution=10): """ @brief Draws the ASDF as a distance field (used for debugging) @param resolution Image resolution @returns An 16-bit, 1-channel Image showing the distance field """ region = self.bounding_region(resolution) image = Image(region.ni, region.nj, channels=1, depth=16) image.xmin = region.X[0] image.xmax = region.X[region.ni] image.ymin = region.Y[0] image.ymax = region.Y[region.nj] image.zmin = region.Z[0] image.zmax = region.Z[region.nk] minimum = libfab.asdf_get_min(self.ptr) maximum = libfab.asdf_get_max(self.ptr) libfab.draw_asdf_distance( self.ptr, region, minimum, maximum, image.pixels ) return image
def load(self): """ @brief Loads the current design file @details The file is defined by self.directory and self.filename """ self.clear() path = os.path.join(self.directory, self.filename) if path[-4:] == '.cad': with open(path, 'r') as f: text = f.read() if text.split('\n')[0] == '## Geometry header ##': koko.PRIMS.reconstruct(eval(text.split('\n')[1])) koko.PRIMS.undo_stack = [koko.PRIMS.reconstructor()] text = '\n'.join(text.split('\n')[3:]) koko.EDITOR.text = text koko.FRAME.status = 'Loaded .cad file' self.mode = 'cad' self.first_render = True self.savepoint(True) elif path[-4:] == '.png': self.mode = 'png' img = Image.load(path) koko.CANVAS.load_image(img) koko.GLCANVAS.load_image(img) koko.FAB.set_input(img) wx.CallAfter(self.snap_bounds) elif path[-4:] == '.stl': self.mode = 'stl' mesh = Mesh.load(path) koko.FRAME.get_menu('View', '3D').Check(True) self.render_mode('3D') koko.GLCANVAS.load_mesh(mesh) wx.CallAfter(self.snap_bounds) elif path[-5:] == '.asdf': self.mode = 'asdf' koko.FRAME.status = 'Loading ASDF' wx.Yield() asdf = ASDF.load(path) koko.FRAME.status = 'Triangulating' wx.Yield() mesh = asdf.triangulate() mesh.source = Struct(type=ASDF, file=path, depth=0) koko.FRAME.status = '' koko.FRAME.get_menu('View', '3D').Check(True) self.render_mode('3D') koko.GLCANVAS.load_mesh(mesh) koko.FAB.set_input(asdf) elif path[-4:] == '.vol': self.mode = 'vol' koko.IMPORT.set_target(self.directory, self.filename)
class MathTree(object): """ @class MathTree @brief Represents a distance metric math expression. @details Arithmetic operators are overloaded to extend the tree with either distance metric arithmetic or shape logical expressions, depending on the value of the instance variable 'shape' """ def __init__(self, math, shape=False, color=None): """ @brief MathTree constructor @param math Math string (in prefix notation) @param shape Boolean modifying arithmetic operators @param color Color tuple or None """ ## @var math # Math string (in sparse prefix syntax) if type(math) in [int, float]: self.math = 'f' + str(math) else: self.math = math ## @var shape # Boolean modify the behavior of arithmetic operators self.shape = shape ## @var color # Assigned color, or None self.color = color self._str = None self._ptr = None ## @var bounds # X, Y, Z bounds (or None) self.bounds = [None] * 6 self.lock = threading.Lock() @threadsafe def __del__(self): """ @brief MathTree destructor """ if self._ptr is not None and libfab is not None: libfab.free_tree(self.ptr) @property def ptr(self): """ @brief Parses self.math and returns a pointer to a MathTree structure """ if self._ptr is None: self._ptr = libfab.parse(self.math) return self._ptr ############################################################################ @property def dx(self): try: return self.xmax - self.xmin except TypeError: return None @property def dy(self): try: return self.ymax - self.ymin except TypeError: return None @property def dz(self): try: return self.zmax - self.zmin except TypeError: return None @property def bounds(self): return [ self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax ] @bounds.setter def bounds(self, value): for b in ['xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax']: setattr(self, b, value.pop(0)) @property def xmin(self): return self._xmin @xmin.setter def xmin(self, value): if value is None: self._xmin = None else: try: self._xmin = float(value) except: raise ValueError('xmin must be a float') @property def xmax(self): return self._xmax @xmax.setter def xmax(self, value): if value is None: self._xmax = None else: try: self._xmax = float(value) except: raise ValueError('xmax must be a float') @property def ymin(self): return self._ymin @ymin.setter def ymin(self, value): if value is None: self._ymin = None else: try: self._ymin = float(value) except: raise ValueError('ymin must be a float') @property def ymax(self): return self._ymax @ymax.setter def ymax(self, value): if value is None: self._ymax = None else: try: self._ymax = float(value) except: raise ValueError('ymax must be a float') @property def zmin(self): return self._zmin @zmin.setter def zmin(self, value): if value is None: self._zmin = None else: try: self._zmin = float(value) except: raise ValueError('zmin must be a float') @property def zmax(self): return self._zmax @zmax.setter def zmax(self, value): if value is None: self._zmax = None else: try: self._zmax = float(value) except: raise ValueError('zmax must be a float') @property def bounded(self): return all(d is not None for d in [self.dx, self.dy, self.dz]) ############################################################################ @property def color(self): return self._color @color.setter def color(self, rgb): named = { 'red': (255, 0, 0), 'blue': (0, 0, 255), 'green': (0, 255, 0), 'white': (255, 255, 255), 'grey': (128, 128, 128), 'black': (0, 0, 0), 'yellow': (255, 255, 0), 'cyan': (0, 255, 255), 'magenta': (255, 0, 255), 'teal': (0, 255, 255), 'pink': (255, 0, 255), 'brown': (145, 82, 45), 'tan': (125, 90, 60), 'navy': (0, 0, 128) } if type(rgb) is str and rgb in named: self._color = named[rgb] elif type(rgb) in [tuple, list] and len(rgb) == 3: self._color = tuple(rgb) elif rgb is None: self._color = rgb else: raise ValueError( 'Invalid color (must be integer 3-value tuple or keyword)') ############################################################################ @staticmethod def wrap(value): ''' Converts a value to a MathTree. None values are left alone, Strings are assumed to be valid math strings and wrapped Floats / ints are converted''' if isinstance(value, MathTree): return value elif value is None: return value elif type(value) is str: return MathTree(value) elif type(value) is not float: try: value = float(value) except (ValueError, TypeError): raise TypeError('Wrong type for MathTree arithmetic (%s)' % type(value)) return MathTree.Constant(value) @classmethod @forcetree def min(cls, A, B): return cls('i' + A.math + B.math) @classmethod @forcetree def max(cls, A, B): return cls('a' + A.math + B.math) @classmethod @forcetree def pow(cls, A, B): return cls('p' + A.math + B.math) @classmethod @forcetree def sqrt(cls, A): return cls('r' + A.math) @classmethod @forcetree def abs(cls, A): return cls('b' + A.math) @classmethod @forcetree def square(cls, A): return cls('q' + A.math) @classmethod @forcetree def sin(cls, A): return cls('s' + A.math) @classmethod @forcetree def cos(cls, A): return cls('c' + A.math) @classmethod @forcetree def tan(cls, A): return cls('t' + A.math) @classmethod @forcetree def asin(cls, A): return cls('S' + A.math) @classmethod @forcetree def acos(cls, A): return cls('C' + A.math) @classmethod @forcetree def atan(cls, A): return cls('T' + A.math) ######################### # MathTree Arithmetic # ######################### # If shape is set, then + and - perform logical combination; # otherwise, they perform arithmeic. @matching @forcetree def __add__(self, rhs): if self.shape or (rhs and rhs.shape): if rhs is None: return self.clone() t = MathTree('i' + self.math + rhs.math, True) if self.dx is not None and rhs.dx is not None: t.xmin = min(self.xmin, rhs.xmin) t.xmax = max(self.xmax, rhs.xmax) if self.dx is not None and rhs.dy is not None: t.ymin = min(self.ymin, rhs.ymin) t.ymax = max(self.ymax, rhs.ymax) if self.dz is not None and rhs.dz is not None: t.zmin = min(self.zmin, rhs.zmin) t.zmax = max(self.zmax, rhs.zmax) return t else: return MathTree('+' + self.math + rhs.math) @matching @forcetree def __radd__(self, lhs): if lhs is None: return self.clone() if self.shape or (lhs and lhs.shape): t = MathTree('i' + lhs.math + self.math) if self.dx is not None and lhs.dx is not None: t.xmin = min(self.xmin, lhs.xmin) t.xmax = max(self.xmax, lhs.xmax) if self.dy is not None and lhs.dy is not None: t.ymin = min(self.ymin, lhs.ymin) t.ymax = max(self.ymax, lhs.ymax) if self.dz is not None and lhs.dz is not None: t.zmin = min(self.zmin, lhs.zmin) t.zmax = max(self.zmax, lhs.zmax) return t else: return MathTree('+' + lhs.math + self.math) @matching @forcetree def __sub__(self, rhs): if self.shape or (rhs and rhs.shape): if rhs is None: return self.clone() t = MathTree('a' + self.math + 'n' + rhs.math, True) for i in ['xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax']: setattr(t, i, getattr(self, i)) return t else: return MathTree('-' + self.math + rhs.math) @matching @forcetree def __rsub__(self, lhs): if self.shape or (lhs and lhs.shape): if lhs is None: return MathTree('n' + self.math) t = MathTree('a' + lhs.math + 'n' + self.math, True) for i in ['xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax']: setattr(t, i, getattr(lhs, i)) return t else: return MathTree('-' + lhs.math + self.math) @matching @forcetree def __and__(self, rhs): if self.shape or rhs.shape: t = MathTree('a' + self.math + rhs.math, True) if self.dx is not None and rhs.dx is not None: t.xmin = max(self.xmin, rhs.xmin) t.xmax = min(self.xmax, rhs.xmax) if self.dy is not None and rhs.dy is not None: t.ymin = max(self.ymin, rhs.ymin) t.ymax = min(self.ymax, rhs.ymax) if self.dz is not None and rhs.dz is not None: t.zmin = max(self.zmin, rhs.zmin) t.zmax = min(self.zmax, rhs.zmax) return t else: raise NotImplementedError( '& operator is undefined for non-shape math expressions.') @matching @forcetree def __rand__(self, lhs): if self.shape or lhs.shape: t = MathTree('a' + lhs.math + self.math, True) if self.dx is not None and lhs.dx is not None: t.xmin = max(self.xmin, lhs.xmin) t.xmax = min(self.xmax, lhs.xmax) if self.dy is not None and lhs.dy is not None: t.ymin = max(self.ymin, lhs.ymin) t.ymax = min(self.ymax, lhs.ymax) if self.dz is not None and lhs.dz is not None: t.zmin = max(self.zmin, lhs.zmin) t.zmax = min(self.zmax, lhs.zmax) return t else: raise NotImplementedError( '& operator is undefined for non-shape math expressions.') @matching @forcetree def __or__(self, rhs): if self.shape or rhs.shape: t = MathTree('i' + self.math + rhs.math, True) if self.dx is not None and rhs.dx is not None: t.xmin = min(self.xmin, rhs.xmin) t.xmax = max(self.xmax, rhs.xmax) if self.dy is not None and rhs.dy is not None: t.ymin = min(self.ymin, rhs.ymin) t.ymax = max(self.ymax, rhs.ymax) if self.dz is not None and rhs.dz is not None: t.zmin = min(self.zmin, rhs.zmin) t.zmax = max(self.zmax, rhs.zmax) return t else: raise NotImplementedError( '| operator is undefined for non-shape math expressions.') @matching @forcetree def __ror__(self, lhs): if self.shape or lhs.shape: t = MathTree('i' + lhs.math + self.math, True) if self.dx is not None and lhs.dx is not None: t.xmin = min(self.xmin, lhs.xmin) t.xmax = max(self.xmax, lhs.xmax) if self.dy is not None and lhs.dy is not None: t.ymin = min(self.ymin, lhs.ymin) t.ymax = max(self.ymax, lhs.ymax) if self.dz is not None and lhs.dz is not None: t.zmin = min(self.zmin, lhs.zmin) t.zmax = max(self.zmax, lhs.zmax) return t else: raise NotImplementedError( '| operator is undefined for non-shape math expressions.') @forcetree def __mul__(self, rhs): return MathTree('*' + self.math + rhs.math) @forcetree def __rmul__(self, lhs): return MathTree('*' + lhs.math + self.math) @forcetree def __div__(self, rhs): return MathTree('/' + self.math + rhs.math) @forcetree def __rdiv__(self, lhs): return MathTree('/' + lhs.math + self.math) @forcetree def __neg__(self): return MathTree('n' + self.math, shape=self.shape) ############################### ## String and representation ## ############################### def __str__(self): if self._str is None: self._str = self.make_str() return self._str def make_str(self, verbose=False): """ @brief Converts the object into an infix-notation string @details Creates a OS pipe, instructs the object to print itself into the pipe, and reads the output in chunks of maximum size 1024. """ # Create a pipe to get the printout read, write = os.pipe() # Start the print function running in a separate thread # (so that we can eat the output and avoid filling the pipe) if verbose: printer = libfab.fdprint_tree_verbose else: printer = libfab.fdprint_tree t = threading.Thread(target=printer, args=(self.ptr, write)) t.daemon = True t.start() s = r = os.read(read, 1024) while r: r = os.read(read, 1024) s += r t.join() os.close(read) return s def __repr__(self): return "'%s' (tree at %s)" % (self, hex(self.ptr.value)) def verbose(self): return self.make_str(verbose=True) def save_dot(self, filename, arrays=False): """ @brief Converts math expression to .dot graph description """ if arrays: libfab.dot_arrays(self.ptr, filename) else: libfab.dot_tree(self.ptr, filename) @property def node_count(self): return libfab.count_nodes(self.ptr) ################################# ## Tree manipulation functions ## ################################# @forcetree def map(self, X=None, Y=None, Z=None): """ @brief Applies a map operator to a tree @param X New X function or None @param Y New Y function or None @param Z New Z function or None """ return MathTree('m' + (X.math if X else ' ') + (Y.math if Y else ' ') + (Z.math if Z else ' ') + self.math, shape=self.shape, color=self.color) @forcetree def map_bounds(self, X=None, Y=None, Z=None): """ @brief Calculates remapped bounds @returns Array of remapped bounds @param X New X function or None @param Y New Y function or None @param Z New Z function or None @details Note that X, Y, and Z should be the inverse of a coordinate mapping to properly transform bounds. """ if self.dx is not None: x = Interval(self.xmin, self.xmax) else: x = Interval(float('nan')) if self.dy is not None: y = Interval(self.ymin, self.ymax) else: y = Interval(float('nan')) if self.dz is not None: z = Interval(self.zmin, self.zmax) else: z = Interval(float('nan')) if self.dx is not None: a = Interval(self.xmin, self.xmax) else: a = Interval(float('nan')) if self.dy is not None: b = Interval(self.ymin, self.ymax) else: b = Interval(float('nan')) if self.dz is not None: c = Interval(self.zmin, self.zmax) else: c = Interval(float('nan')) if X: X_p = libfab.make_packed(X.ptr) a = libfab.eval_i(X_p, x, y, z) libfab.free_packed(X_p) if Y: Y_p = libfab.make_packed(Y.ptr) b = libfab.eval_i(Y_p, x, y, z) libfab.free_packed(Y_p) if Z: Z_p = libfab.make_packed(Z.ptr) c = libfab.eval_i(Z_p, x, y, z) libfab.free_packed(Z_p) bounds = [] for i in [a, b, c]: if math.isnan(i.lower) or math.isnan(i.upper): bounds += [None, None] else: bounds += [i.lower, i.upper] return bounds @threadsafe def clone(self): m = MathTree(self.math, shape=self.shape, color=self.color) m.bounds = [b for b in self.bounds] if self._ptr is not None: m._ptr = libfab.clone_tree(self._ptr) return m ################################# # Rendering functions # ################################# 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') if interrupt is None: interrupt = threading.Event() halt = ctypes.c_int(0) # flag to abort render image = Image( region.ni, region.nj, channels=1, depth=16, ) # Divide the task to share among multiple threads clones = [self.clone() for i in range(threads)] packed = [libfab.make_packed(c.ptr) for c in clones] subregions = region.split_xy(threads) # Solve each region in a separate thread args = zip(packed, subregions, [image.pixels] * threads, [halt] * threads) multithread(libfab.render16, args, interrupt, halt) for p in packed: libfab.free_packed(p) image.xmin = region.X[0] * mm_per_unit image.xmax = region.X[region.ni] * mm_per_unit image.ymin = region.Y[0] * mm_per_unit image.ymax = region.Y[region.nj] * mm_per_unit image.zmin = region.Z[0] * mm_per_unit image.zmax = region.Z[region.nk] * mm_per_unit return image
def load(self): """ @brief Loads the current design file @details The file is defined by self.directory and self.filename """ self.clear() path = os.path.join(self.directory, self.filename) if path[-3:] == '.ko' or path[-4:] == '.cad': with open(path, 'r') as f: text = f.read() if text.split('\n')[0] == '## Geometry header ##': koko.PRIMS.reconstruct(eval(text.split('\n')[1])) koko.PRIMS.undo_stack = [koko.PRIMS.reconstructor()] text = '\n'.join(text.split('\n')[3:]) koko.EDITOR.text = text koko.FRAME.status = 'Loaded design file' if path[-4:] == '.cad': dialogs.warning(""" This file has a '.cad' extension, which was superceded by '.ko'. It may use deprecated features or syntax. If it is an example file, the 'kokopelli/examples' folder may include an updated version with a '.ko' extension""" ) self.mode = 'cad' self.first_render = True self.savepoint(True) elif path[-4:] == '.png': self.mode = 'png' img = Image.load(path) koko.CANVAS.load_image(img) koko.GLCANVAS.load_image(img) koko.FAB.set_input(img) wx.CallAfter(self.snap_bounds) elif path[-4:] == '.stl': self.mode = 'stl' mesh = Mesh.load(path) koko.FRAME.get_menu('View', '3D').Check(True) self.render_mode('3D') koko.GLCANVAS.load_mesh(mesh) wx.CallAfter(self.snap_bounds) elif path[-5:] == '.asdf': self.mode = 'asdf' koko.FRAME.status = 'Loading ASDF' wx.Yield() asdf = ASDF.load(path) koko.FRAME.status = 'Triangulating' wx.Yield() mesh = asdf.triangulate() mesh.source = Struct(type=ASDF, file=path, depth=0) koko.FRAME.status = '' koko.FRAME.get_menu('View', '3D').Check(True) self.render_mode('3D') koko.GLCANVAS.load_mesh(mesh) koko.FAB.set_input(asdf) elif path[-4:] == '.vol': self.mode = 'vol' koko.IMPORT.set_target(self.directory, self.filename)
def load(self): """ @brief Loads the current design file @details The file is defined by self.directory and self.filename """ self.clear() path = os.path.join(self.directory, self.filename) if path[-3:] == '.ko' or path[-4:] == '.cad': with open(path, 'r') as f: text = f.read() if text.split('\n')[0] == '## Geometry header ##': koko.PRIMS.reconstruct(eval(text.split('\n')[1])) koko.PRIMS.undo_stack = [koko.PRIMS.reconstructor()] text = '\n'.join(text.split('\n')[3:]) koko.EDITOR.text = text koko.FRAME.status = 'Loaded design file' if path[-4:] == '.cad': dialogs.warning(""" This file has a '.cad' extension, which was superceded by '.ko'. It may use deprecated features or syntax. If it is an example file, the 'kokopelli/examples' folder may include an updated version with a '.ko' extension""") self.mode = 'cad' self.first_render = True self.savepoint(True) elif path[-4:] == '.png': self.mode = 'png' img = Image.load(path) koko.CANVAS.load_image(img) koko.GLCANVAS.load_image(img) koko.FAB.set_input(img) wx.CallAfter(self.snap_bounds) elif path[-4:] == '.stl': self.mode = 'stl' mesh = Mesh.load(path) koko.FRAME.get_menu('View', '3D').Check(True) self.render_mode('3D') koko.GLCANVAS.load_mesh(mesh) wx.CallAfter(self.snap_bounds) elif path[-5:] == '.asdf': self.mode = 'asdf' koko.FRAME.status = 'Loading ASDF' wx.Yield() asdf = ASDF.load(path) koko.FRAME.status = 'Triangulating' wx.Yield() mesh = asdf.triangulate() mesh.source = Struct(type=ASDF, file=path, depth=0) koko.FRAME.status = '' koko.FRAME.get_menu('View', '3D').Check(True) self.render_mode('3D') koko.GLCANVAS.load_mesh(mesh) koko.FAB.set_input(asdf) elif path[-4:] == '.vol': self.mode = 'vol' koko.IMPORT.set_target(self.directory, self.filename)