def views(force_update): print("Views") print("---") scad_dir = "views" render_dir = "images" if not os.path.isdir(render_dir): os.makedirs(render_dir) # List of "view" scad files # scads = [i for i in os.listdir(scad_dir) if i[-5:] == ".scad"] for scad in scads: scad_name = scad_dir + os.sep + scad png_name = render_dir + os.sep + scad[:-4] + "png" print("Checking: " + scad_name) view_count = 0 for line in open(scad_name, "r").readlines(): words = line.split() if len(words) > 10 and words[0] == "//": cmd = words[1] if cmd == "view": view_count += 1 # Up-sample images w = int(words[2]) * 2 h = int(words[3]) * 2 dx = float(words[4]) dy = float(words[5]) dz = float(words[6]) rx = float(words[7]) ry = float(words[8]) rz = float(words[9]) d = float(words[10]) camera = "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f" % ( dx, dy, dz, rx, ry, rz, d) if (force_update) or (not os.path.isfile(png_name) or os.path.getmtime(png_name) < os.path.getmtime(scad_name)): openscad.run("--projection=p", ("--imgsize=%d,%d" % (w, h)), "--camera=" + camera, "-o", png_name, scad_name) print polish(png_name, w / 2, h / 2) else: print(" Up to date") if view_count < 1: print(" No views found - you need to define at least one view")
def _extract_colors(self): """Returns a list of color expressions used within the input_file or any dependencies thereof""" self.logger.info("Extracting color information...") # Create a mutator that defines a color_extractor() module that prints the passed color argument and replaces # all color(xyz) declarations with color_extractor(xyz) modules instances. def replace_with_color_collector(contents): contents = COLOR_REGEX.sub(' color_extractor(', contents) return contents + ''' module color_extractor(c) { echo(extracted_color=c); children(); }''' intermediate_subfolder = os.path.join(self.intermediate_folder, 'color_extraction') self.walk_and_mutate_scad_files(replace_with_color_collector, intermediate_subfolder) # Compile the mutated scad model and collect the echo output for processing echo_file = os.path.join(self.intermediate_folder, 'color_extractor.echo') openscad.run( os.path.join(intermediate_subfolder, ColoredStlExporter.get_transformed_file_path(self.input_file)), echo_file, variables=self.openscad_variables, capture_output=True ) # Parse the color values from the output color_values = set() with open(echo_file, 'rb') as f: for line in f: match = EXTRACTED_COLOR_REGEX.search(line) if match: color_values.add(match.group('color')) return color_values
def _export_stl(self, color): """Exports an .stl file containing only objects of the specified color from the input model""" # Create a mutator that defines a color_selector() module that conditionally includes its children only if the # passed color argument is the color we're currently trying to export. def replace_with_color_selector(contents): contents = COLOR_REGEX.sub(' color_selector(', contents) return contents + ''' module color_selector(c) {{ if (c == {}) children(); }} '''.format(color) color_hash = hashlib.sha256(color).hexdigest() intermediate_subfolder = os.path.join(self.intermediate_folder, 'color_' + color_hash) self.walk_and_mutate_scad_files(replace_with_color_selector, intermediate_subfolder) # Name the stl model after its color (but use a hash function to make sure it's a valid filename) file_name = color_hash + '.stl' self.logger.info('Exporting STL for color {} as {}...'.format(color, file_name)) openscad.run( os.path.join(intermediate_subfolder, ColoredStlExporter.get_transformed_file_path(self.input_file)), os.path.join(self.output_folder, file_name), variables=self.openscad_variables, capture_output=True ) return file_name
def parse_machine(scadfile, logfile, errorfile): openscad.run('-D', '$ShowBOM=true', '-o', 'dummy.csg', scadfile) # remove dummy.csg os.remove('dummy.csg') js = '' errorlevel = 0 for line in open(logfile, "rt").readlines(): # errors r = re.search(r".*syntax error$", line, re.I) if r: print(" Syntax error!") print(line) errorlevel = 2 continue # echo lines r = re.search(r'^.*ECHO\:\s\"(.*)\"$', line, re.I) if r: s = r.group(1) # rewrite single quotes to double quotes, except for where they are used in words, e.g. isn't s = re.sub(r"((\w)['](\W|$))", "\g<2>\"\g<3>", s) s = re.sub(r"((\W|^)['](\w))", "\g<2>\"\g<3>", s) s = re.sub(r"((\W)['](\W))", "\g<2>\"\g<3>", s) js += s + '\n' if errorlevel == 0: # Get rid of any empty objects js = js.replace("{}", "") # get rid of trailing commas js = re.sub(r",(\s*(\}|\]))", "\g<1>", js) js = re.sub(r",\s*$", "", js) try: jso = json.loads(js) # prettify js = json.dumps(jso, sort_keys=False, indent=4, separators=(',', ': ')) except Exception as e: print(e) print("See " + errorfile + " for malformed json") with open(errorfile, 'w') as f: f.write(js) # Stop malformed machine json screwing up everything else! js = '' else: raise Exception("Syntax error") return js
def views(force_update): print("Views") print("---") scad_dir = "views" render_dir = "images" if not os.path.isdir(render_dir): os.makedirs(render_dir) # List of "view" scad files # scads = [i for i in os.listdir(scad_dir) if i[-5:] == ".scad"] for scad in scads: scad_name = scad_dir + os.sep + scad png_name = render_dir + os.sep + scad[:-4] + "png" print("Checking: " + scad_name) view_count = 0 for line in open(scad_name, "r").readlines(): words = line.split() if len(words) > 10 and words[0] == "//": cmd = words[1] if cmd == "view": view_count += 1 # Up-sample images w = int(words[2]) * 2 h = int(words[3]) * 2 dx = float(words[4]) dy = float(words[5]) dz = float(words[6]) rx = float(words[7]) ry = float(words[8]) rz = float(words[9]) d = float(words[10]) camera = "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f" % (dx, dy, dz, rx, ry, rz, d) if (force_update) or (not os.path.isfile(png_name) or os.path.getmtime(png_name) < os.path.getmtime(scad_name)): openscad.run("--projection=p", ("--imgsize=%d,%d" % (w, h)), "--camera=" + camera, "-o", png_name, scad_name) print polish(png_name, w/2, h/2) else: print(" Up to date") if view_count < 1: print(" No views found - you need to define at least one view")
def sheets(machine): # # Make the target directory # target_dir = machine + "/sheets" if os.path.isdir(target_dir): try: shutil.rmtree(target_dir) os.makedirs(target_dir) except: pass else: os.makedirs(target_dir) # # Set the target machine # f = open("scad/conf/machine.scad", "wt") f.write("include <%s_config.scad>\n" % machine) f.close() # # Find all the scad files # for filename in os.listdir(source_dir): if filename[-5:] == ".scad": # # find any modules ending in _dxf # for line in open(source_dir + "/" + filename, "r").readlines(): words = line.split() if (len(words) and words[0] == "module"): module = words[1].split('(')[0] if module[-4:] == "_dxf": # # make a file to use the module # dxf_maker_name = target_dir + "/" + module + ".scad" f = open(dxf_maker_name, "w") f.write("use <../../%s/%s>\n" % (source_dir, filename)) f.write("%s();\n" % module) f.close() # # Run openscad on the created file # base_name = target_dir + "/" + module[:-4] dxf_name = base_name + ".dxf" openscad.run("-o", dxf_name, dxf_maker_name) # # Make SVG drill template # dxf_to_svg(dxf_name) # # Make PDF for printing # InkCL.run("-f", base_name + ".svg", "-A", base_name + ".pdf")
def parse_machine(scadfile, logfile, errorfile): openscad.run('-D','$ShowBOM=true','-o','dummy.csg',scadfile); # remove dummy.csg os.remove('dummy.csg') js = '' errorlevel = 0 for line in open(logfile, "rt").readlines(): # errors r = re.search(r".*syntax error$", line, re.I) if r: print(" Syntax error!") print(line) errorlevel = 2 continue # echo lines r = re.search(r'^.*ECHO\:\s\"(.*)\"$', line, re.I) if r: s = r.group(1) # rewrite single quotes to double quotes, except for where they are used in words, e.g. isn't s = re.sub(r"((\w)['](\W|$))","\g<2>\"\g<3>", s) s = re.sub(r"((\W|^)['](\w))","\g<2>\"\g<3>", s) s = re.sub(r"((\W)['](\W))","\g<2>\"\g<3>", s) js += s + '\n' if errorlevel == 0: # Get rid of any empty objects js = js.replace("{}","") # get rid of trailing commas js = re.sub(r",(\s*(\}|\]))","\g<1>", js) js = re.sub(r",\s*$","", js) try: jso = json.loads(js) # prettify js = json.dumps(jso, sort_keys=False, indent=4, separators=(',', ': ')) except Exception as e: print(e) print("See "+errorfile+" for malformed json") with open(errorfile, 'w') as f: f.write(js) # Stop malformed machine json screwing up everything else! js = '' else: raise Exception("Syntax error") return js
def boms(target=None, assembly=None): try: bom_dir = set_config(target, usage) + "bom" if assembly: bom_dir += "/accessories" if not os.path.isdir(bom_dir): os.makedirs(bom_dir) else: assembly = "main_assembly" if os.path.isdir(bom_dir): shutil.rmtree(bom_dir) sleep(0.1) os.makedirs(bom_dir) # # Find the scad file that makes the module # scad_file = find_scad_file(assembly) if not scad_file: raise Exception("can't find source for " + assembly) # # make a file to use the module # bom_maker_name = source_dir + "/bom.scad" with open(bom_maker_name, "w") as f: f.write("use <%s>\n" % scad_file) f.write("%s();\n" % assembly) # # Run openscad # openscad.run("-D", "$bom=2", "-D", "$preview=true", "--hardwarnings", "-o", "openscad.echo", "-d", bom_dir + "/bom.deps", bom_maker_name) os.remove(bom_maker_name) print("Generating bom ...", end=" ") main = parse_bom("openscad.echo", assembly) if assembly == "main_assembly": main.print_bom(True, open(bom_dir + "/bom.txt", "wt")) for ass in main.assemblies: with open(bom_dir + "/" + ass + ".txt", "wt") as f: bom = main.assemblies[ass] print(bom.make_name(ass) + ":", file=f) bom.print_bom(False, f) data = [ main.assemblies[ass].flat_data() for ass in main.ordered_assemblies ] with open(bom_dir + "/bom.json", 'w') as outfile: json.dump(data, outfile, indent=4) print("done") except Exception as e: print(str(e)) sys.exit(1)
def views(machine): scad_dir = "views" render_dir = machine + os.sep + "views" if not os.path.isdir(render_dir): os.makedirs(render_dir) # # Set the target machine # f = open("scad/conf/machine.scad", "wt") f.write("include <%s_config.scad>\n" % machine) f.close() # # List of individual part files # scads = [i for i in os.listdir(scad_dir) if i[-5:] == ".scad"] for scad in scads: scad_name = scad_dir + os.sep + scad png_name = render_dir + os.sep + scad[:-4] + "png" for line in open(scad_name, "r").readlines(): words = line.split() if len(words) > 10 and words[0] == "//": cmd = words[1] if cmd == "view" or cmd == "assembled" or cmd == "assembly": w = int(words[2]) * 2 h = int(words[3]) * 2 dx = -float(words[4]) dy = -float(words[5]) dz = -float(words[6]) rx = 360.0 - float(words[7]) + 90 ry = 360.0 - float(words[8]) rz = 360.0 - float(words[9]) d = float(words[10]) camera = "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f" % ( dx, dy, dz, rx, ry, rz, d) exploded = "0" if cmd == "assembly": exploded = "1" if cmd == "assembled": png_name = png_name.replace("assembly", "assembled") if not os.path.isfile(png_name) or os.path.getmtime( png_name) < os.path.getmtime(scad_name): openscad.run("--projection=p", ("--imgsize=%d,%d" % (w, h)), "--camera=" + camera, "-D$exploded=" + exploded, "-o", png_name, scad_name) print
def sheets(machine): # # Make the target directory # target_dir = machine + "/sheets" if os.path.isdir(target_dir): try: shutil.rmtree(target_dir) os.makedirs(target_dir) except: pass else: os.makedirs(target_dir) # # Set the target machine # f = open("scad/conf/machine.scad","wt") f. write("include <%s_config.scad>\n" % machine); f.close() # # Find all the scad files # for filename in os.listdir(source_dir): if filename[-5:] == ".scad": # # find any modules ending in _dxf # for line in open(source_dir + "/" + filename, "r").readlines(): words = line.split() if(len(words) and words[0] == "module"): module = words[1].split('(')[0] if module[-4:] == "_dxf": # # make a file to use the module # dxf_maker_name = target_dir + "/" + module + ".scad" f = open(dxf_maker_name, "w") f.write("use <../../%s/%s>\n" % (source_dir, filename)) f.write("%s();\n" % module); f.close() # # Run openscad on the created file # base_name = target_dir + "/" + module[:-4] dxf_name = base_name + ".dxf" openscad.run("-o", dxf_name, dxf_maker_name) # # Make SVG drill template # dxf_to_svg(dxf_name) # # Make PDF for printing # InkCL.run("-f", base_name + ".svg", "-A", base_name + ".pdf")
def views(machine): scad_dir = "views" render_dir = machine + os.sep + "views" if not os.path.isdir(render_dir): os.makedirs(render_dir) # # Set the target machine # f = open("scad/conf/machine.scad","wt") f.write("include <%s_config.scad>\n" % machine); f.close() # # List of individual part files # scads = [i for i in os.listdir(scad_dir) if i[-5:] == ".scad"] for scad in scads: scad_name = scad_dir + os.sep + scad png_name = render_dir + os.sep + scad[:-4] + "png" for line in open(scad_name, "r").readlines(): words = line.split() if len(words) > 10 and words[0] == "//": cmd = words[1] if cmd == "view" or cmd == "assembled" or cmd == "assembly": w = int(words[2]) * 2 h = int(words[3]) * 2 dx = -float(words[4]) dy = -float(words[5]) dz = -float(words[6]) rx = 360.0 - float(words[7]) + 90 ry = 360.0 - float(words[8]) rz = 360.0 - float(words[9]) d = float(words[10]) camera = "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f" % (dx, dy, dz, rx, ry, rz, d) exploded = "0" if cmd == "assembly": exploded = "1" if cmd == "assembled": png_name = png_name.replace("assembly","assembled") if not os.path.isfile(png_name) or os.path.getmtime(png_name) < os.path.getmtime(scad_name): openscad.run("--projection=p", ("--imgsize=%d,%d" % (w, h)), "--camera=" + camera, "-D$exploded=" + exploded, "-o", png_name, scad_name) print
def render_frame(i): angle = 135 + i * 360 / num_frames openscad.run( input_name, os.path.join(output_folder, 'frame_%05d.png' % (start_frame + i)), output_size=[640, 480], camera_translation=[0, 0, 0], camera_rotation=[60, 0, angle], camera_distance=200, variables=variables, colorscheme='Nature', )
def render_frame(i): angle = 135 + i * 360 / num_frames openscad.run( 'splitflap.scad', os.path.join(output_folder, 'frame_%05d.png' % (start_frame + i)), output_size = [320, 240], camera_translation = [0, 0, 0], camera_rotation = [60, 0, angle], camera_distance = 600, variables = variables, colorscheme = 'Nature', )
def _export_stl(self, color): """Exports an .stl file containing only objects of the specified color from the input model""" # Create a mutator that defines a color_selector() module that conditionally includes its children only if the # passed color argument is the color we're currently trying to export. def replace_with_color_selector(contents): contents = COLOR_REGEX.sub(' color_selector(', contents) return contents + ''' module color_selector(c) {{ precision = 0.0000001; // arbitrary function compare_floats(x, y, i=0) = (len(x) != len(y)) ? false // if arrays differ in length, they can't be equal : (i >= len(x)) ? true // if we've hit the end of the arrays without issue, we're equal : (x[i] - precision <= y[i]) && x[i] + precision >= y[i] ? compare_floats(x, y, i+1) : false; // not equal, short circuit if (c == {0} || compare_floats(c, {0})) children(); }} '''.format(color) color_hash = hashlib.sha256(color.encode('utf-8')).hexdigest() intermediate_subfolder = os.path.join(self.intermediate_folder, 'color_' + color_hash) self.walk_and_mutate_scad_files(replace_with_color_selector, intermediate_subfolder) # Name the stl model after its color (but use a hash function to make sure it's a valid filename) file_name = color_hash + '.stl' self.logger.info('Exporting STL for color {} as {}...'.format( color, file_name)) try: openscad.run(os.path.join( intermediate_subfolder, ColoredStlExporter.get_transformed_file_path(self.input_file)), os.path.join(self.output_folder, file_name), variables=self.openscad_variables, capture_output=True) except openscad.OpenSCADException as e: if b'Current top level object is empty.' in e.stderr: # No geometry in this color return None else: raise return file_name
def _render_component(self, i): output_file = self._get_component_file(i) for style in ('cut', 'etch'): logging.debug('Rendering component %d, %s', i, style) try: stdout, stderr = openscad.run( self.input_file, output_file, variables = self._get_variables({ 'render_3d': False, 'render_index': i, 'render_etch': style == 'etch', }), capture_output=True, ) except openscad.OpenSCADException as e: if 'Current top level object is not a 2D object.' in e.stderr: # This is expected if we try rendering an etch layer as a # cut, since there will be nothing to export continue else: raise processor = SvgProcessor(output_file) processor.fix_dimens() if style == 'cut': processor.apply_laser_cut_style() elif style == 'etch': processor.apply_laser_etch_style() break else: raise ValueError("Invalid component!", i) return processor
def boms(machine): bom_dir = machine + "/bom" if os.path.isdir(bom_dir): shutil.rmtree(bom_dir) os.makedirs(bom_dir) f = open("scad/conf/machine.scad", "wt") f.write("include <%s_config.scad>\n" % machine) f.close() openscad.run("-o", "dummy.csg", "scad/bom.scad") print "Generating bom ...", main = BOM() stack = [] for line in open("openscad.log"): if line[:7] == 'ECHO: "': s = line[7:-2] if s[-1] == '/': ass = s[:-1] if stack: main.assemblies[stack[-1]].add_assembly( ass) #add to nested BOM stack.append(ass) main.add_assembly(ass) #add to flat BOM else: if s[0] == '/': if s[1:] != stack[-1]: raise Exception, "Mismatched assembly " + s[1:] + str( stack) stack.pop() else: main.add_part(s) if stack: main.assemblies[stack[-1]].add_part(s) main.print_bom(True, open(bom_dir + "/bom.txt", "wt")) for ass in sorted(main.assemblies): f = open(bom_dir + "/" + ass + ".txt", "wt") bom = main.assemblies[ass] print >> f, bom.make_name(ass) + ":" bom.print_bom(False, f) f.close() print " done"
def render_view_using_file(obj_title, scadfile, dir, view, hashchanged, hash=""): png_name = dir + '/' + view_filename(obj_title + '_' + view['title']) view['filepath'] = png_name temp_name = 'temp.scad' oldhashes = read_hashes_from_png(png_name) viewstr = str(view['size']) + str(view['translate']) + str( view['rotate']) + str(view['dist']) viewhash = hashlib.md5(viewstr).hexdigest() if (not os.path.isfile(png_name) or (hash != oldhashes['csghash']) or (viewhash != oldhashes['viewhash'])): print(" Updating: " + png_name) # Up-sample images w = view['size'][0] * 2 h = view['size'][1] * 2 dx = float(view['translate'][0]) dy = float(view['translate'][1]) dz = float(view['translate'][2]) rx = float(view['rotate'][0]) ry = float(view['rotate'][1]) rz = float(view['rotate'][2]) d = float(view['dist']) camera = "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f" % (dx, dy, dz, rx, ry, rz, d) openscad.run("--imgsize=%d,%d" % (w, h), "--projection=p", "--camera=" + camera, "-o", png_name, scadfile) polish(png_name, w / 2, h / 2, hash, viewhash) print else: print(" View up to date")
def boms(machine): bom_dir = machine + "/bom" if os.path.isdir(bom_dir): shutil.rmtree(bom_dir) os.makedirs(bom_dir) f = open("scad/conf/machine.scad","wt") f. write("include <%s_config.scad>\n" % machine); f.close() openscad.run("-D","$bom=2","-o", "dummy.csg", "scad/bom.scad") print "Generating bom ...", main = BOM() stack = [] for line in open("openscad.log"): if line[:7] == 'ECHO: "': s = line[7:-2] if s[-1] == '/': ass = s[:-1] if stack: main.assemblies[stack[-1]].add_assembly(ass) #add to nested BOM stack.append(ass) main.add_assembly(ass) #add to flat BOM else: if s[0] == '/': if s[1:] != stack[-1]: raise Exception, "Mismatched assembly " + s[1:] + str(stack) stack.pop() else: main.add_part(s) if stack: main.assemblies[stack[-1]].add_part(s) main.print_bom(True, open(bom_dir + "/bom.txt","wt")) for ass in sorted(main.assemblies): f = open(bom_dir + "/" + ass + ".txt", "wt"); bom = main.assemblies[ass] print >> f, bom.make_name(ass) + ":" bom.print_bom(False, f) f.close() print " done"
def render(target, type): # # Make the target directory # top_dir = set_config(target, usage) target_dir = top_dir + type + 's' bom_dir = top_dir + 'bom' if not os.path.isdir(target_dir): os.makedirs(target_dir) # # Find all the parts # parts = bom_to_parts(bom_dir, type) # # Remove unused png files # for file in os.listdir(target_dir): if file.endswith('.png'): if not file[:-4] + '.' + type in parts: print("Removing %s" % file) os.remove(target_dir + '/' + file) for part in parts: part_file = target_dir + '/' + part png_name = target_dir + '/' + part[:-4] + '.png' # # make a file to import the stl # if mtime(part_file) > mtime(png_name): png_maker_name = "png.scad" with open(png_maker_name, "w") as f: f.write('color([0, 146/255, 0]) import("%s");\n' % part_file) cam = "--camera=0,0,0,70,0,315,500" if type == 'stl' else "--camera=0,0,0,0,0,0,500" render = "--preview" if type == 'stl' else "--render" tmp_name = 'tmp.png' openscad.run(colour_scheme, "--projection=p", "--imgsize=4096,4096", cam, render, "--autocenter", "--viewall", "-o", tmp_name, png_maker_name) do_cmd(( "magick " + tmp_name + " -trim -resize 280x280 -background %s -gravity Center -extent 280x280 -bordercolor %s -border 10 %s" % (background, background, tmp_name)).split()) update_image(tmp_name, png_name) os.remove(png_maker_name)
def render_view_using_file(obj_title, scadfile, dir, view, hashchanged, hash=""): png_name = dir + '/' + view_filename(obj_title + '_'+view['title']) view['filepath'] = png_name temp_name = 'temp.scad' oldhashes = read_hashes_from_png(png_name) viewstr = str(view['size']) + str(view['translate']) + str(view['rotate']) + str(view['dist']) viewhash = hashlib.md5(viewstr).hexdigest(); if (not os.path.isfile(png_name) or (hash != oldhashes['csghash']) or (viewhash != oldhashes['viewhash'])): print(" Updating: "+png_name) # Up-sample images w = view['size'][0] * 2 h = view['size'][1] * 2 dx = float(view['translate'][0]) dy = float(view['translate'][1]) dz = float(view['translate'][2]) rx = float(view['rotate'][0]) ry = float(view['rotate'][1]) rz = float(view['rotate'][2]) d = float(view['dist']) camera = "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f" % (dx, dy, dz, rx, ry, rz, d) openscad.run( "--imgsize=%d,%d" % (w, h), "--projection=p", "--camera=" + camera, "-o", png_name, scadfile) polish(png_name, w/2, h/2, hash, viewhash) print else: print(" View up to date")
def _get_num_components(self): stdout, stderr = openscad.run( self.input_file, os.path.join(self.output_folder, 'dummy.png'), output_size=[1,1], variables = self._get_variables({ 'render_3d': False, 'render_index': 0, }), capture_output=True, ) return openscad.extract_values(stderr)['num_components']
def _get_num_components(self): stdout, stderr = openscad.run( self.input_file, os.path.join(self.output_folder, 'dummy.png'), output_size=[1, 1], variables=self._get_variables({ 'render_3d': False, 'render_index': 0, }), capture_output=True, ) return openscad.extract_values(stderr)['num_components']
def _get_num_components(self): stdout, stderr = openscad.run( self.input_file, os.path.join(self.output_folder, 'dummy.png'), output_size=[1, 1], variables=self._get_variables({ '_is_projection_rendering': True, 'render_3d': False, 'render_index': 0, }), capture_output=True, ) outputs = openscad.extract_values(stderr) for key, value in outputs.items(): if key.startswith('debug_'): logging.debug('DEBUG VALUE %r = %r', key, value) return outputs['num_components']
def _render_component(self, i, panel_horizontal, panel_vertical): output_file = self._get_component_file(i) style_options = ['cut'] if self.etch_enabled: style_options.append('etch') for style in (style_options): logging.debug('Rendering component %d, %s', i, style) try: _ = openscad.run( self.input_file, output_file, variables=self._get_variables({ '_is_projection_rendering': True, 'render_3d': False, 'render_index': i, 'render_etch': style == 'etch', 'panel_horizontal': panel_horizontal, 'panel_vertical': panel_vertical, }), capture_output=True, ) except openscad.OpenSCADException as e: if b'Current top level object is not a 2D object.' in e.stderr: # This is expected if we try rendering an etch layer as a # cut, since there will be nothing to export continue else: raise processor = SvgProcessor(output_file) if style == 'cut': processor.apply_laser_cut_style() elif style == 'etch': processor.apply_laser_etch_style() return processor logging.debug('Component %d has no geometry', i) return None
def _render_component(self, i, panel_horizontal, panel_vertical): output_file = self._get_component_file(i) for style in ('cut', 'etch'): logging.debug('Rendering component %d, %s', i, style) try: _ = openscad.run( self.input_file, output_file, variables=self._get_variables({ 'render_3d': False, 'render_index': i, 'render_etch': style == 'etch', 'panel_horizontal': panel_horizontal, 'panel_vertical': panel_vertical, }), capture_output=True, ) except openscad.OpenSCADException as e: if 'Current top level object is not a 2D object.' in e.stderr: # This is expected if we try rendering an etch layer as a # cut, since there will be nothing to export continue else: raise processor = SvgProcessor(output_file) processor.fix_dimens() if style == 'cut': processor.apply_laser_cut_style() elif style == 'etch': processor.apply_laser_etch_style() break else: raise ValueError("Invalid component!", i) return processor
def plateup(target, part_type, usage=None): # # Make the target directory # top_dir = set_config(target, usage) parts_dir = top_dir + part_type + 's' target_dir = parts_dir + '/' + target_dirs[part_type] source_dir1 = source_dirs[part_type] source_dir2 = top_dir + source_dirs[part_type] # # Loop through source directories # used = [] all_sources = [] for dir in [source_dir1, source_dir2]: if not os.path.isdir(dir): continue if not os.path.isdir(target_dir): os.makedirs(target_dir) # # Make the deps dir # deps_dir = dir + "/deps" if not os.path.isdir(deps_dir): os.makedirs(deps_dir) # # Decide which files to make # sources = [file for file in os.listdir(dir) if file.endswith('.scad')] all_sources += sources # # Run OpenSCAD on the source files to make the targets # for src in sources: src_file = dir + '/' + src part_file = target_dir + '/' + src[:-4] + part_type dname = deps_name(deps_dir, src) changed = check_deps(part_file, dname) if changed: print(changed) openscad.run("-D$bom=1", "-d", dname, "-o", part_file, src_file) if part_type == 'stl': c14n_stl.canonicalise(part_file) log_name = 'openscad.log' else: log_name = 'openscad.echo' openscad.run_silent("-D$bom=1", "-o", log_name, src_file) # # Add the files on the BOM to the used list # with open(log_name) as file: for line in file.readlines(): match = re.match(r'^ECHO: "~(.*?\.' + part_type + r').*"$', line) if match: used.append(match.group(1)) # # Copy file that are not included # copied = [] for file in os.listdir(parts_dir): if file.endswith('.' + part_type) and not file in used: src = parts_dir + '/' + file dst = target_dir + '/' + file if mtime(src) > mtime(dst): print("Copying %s to %s" % (src, dst)) copyfile(src, dst) copied.append(file) # # Remove any cruft # targets = [file[:-4] + part_type for file in all_sources] for file in os.listdir(target_dir): if file.endswith('.' + part_type): if not file in targets and not file in copied: print("Removing %s" % file) os.remove(target_dir + '/' + file)
def generate_stl(input_name, output_folder, output_name, variables): openscad.run( input_name, os.path.join(output_folder, output_name), # variables = variables, )
def plateup(target, part_type, usage=None): # # Make the target directory # top_dir = set_config(target, usage) parts_dir = top_dir + part_type + 's' target_dir = parts_dir + '/' + target_dirs[part_type] source_dir = top_dir + source_dirs[part_type] deps_dir = source_dir + "/deps" if not os.path.isdir(source_dir): return if not os.path.isdir(target_dir): os.makedirs(target_dir) if not os.path.isdir(deps_dir): os.makedirs(deps_dir) # # Decide which files to make # sources = [ file for file in os.listdir(source_dir) if file.endswith('.scad') ] # # Run OpenSCAD on the source files to make the targets # used = [] for src in sources: src_file = source_dir + '/' + src part_file = target_dir + '/' + src[:-4] + part_type dname = deps_name(deps_dir, src) changed = check_deps(part_file, dname) if changed: print(changed) openscad.run("-D$bom=1", "-d", dname, "-o", part_file, src_file) if part_type == 'stl': c14n_stl.canonicalise(part_file) log_name = 'openscad.log' else: log_name = 'openscad.echo' openscad.run_silent("-D$bom=1", "-o", log_name, src_file) # # Add the files on the BOM to the used list # with open(log_name) as file: for line in file.readlines(): if line.startswith('ECHO: "~') and line.endswith('.' + part_type + '"\n'): used.append(line[8:-2]) # # Copy file that are not included # copied = [] for file in os.listdir(parts_dir): if file.endswith('.' + part_type) and not file in used: src = parts_dir + '/' + file dst = target_dir + '/' + file if mtime(src) > mtime(dst): print("Copying %s to %s" % (src, dst)) copyfile(src, dst) copied.append(file) # # Remove any cruft # targets = [file[:-4] + part_type for file in sources] for file in os.listdir(target_dir): if file.endswith('.' + part_type): if not file in targets and not file in copied: print("Removing %s" % file) os.remove(target_dir + '/' + file)
def parse_vitamin(vitaminscad, use_catalogue_call=False): tempscadfile = "temp.scad" logfile = 'openscad.log' vitamincall = vitaminscad[:-5]; print(" Calling: "+ vitamincall + "();") # Generate a wrapper scad file for the vitamin file with open(tempscadfile, "w") as f: f.write("include <../config/config.scad>\n") f.write("include <../vitamins/"+vitaminscad+">\n") if use_catalogue_call: f.write(vitamincall + "_Catalogue();\n"); else: f.write(vitamincall + "();\n"); openscad.run('-D','$ShowBOM=true','-o','dummy.csg',tempscadfile); js = '' errorlevel = 0 for line in open(logfile, "rt").readlines(): # errors r = re.search(r".*syntax error$", line, re.I) if r: print(" Syntax error!") print(line) errorlevel = 2 continue # echo lines r = re.search(r'^.*ECHO\:\s\"(.*)\"$', line, re.I) if r: s = r.group(1) # rewrite single quotes to double quotes, except for where they are used in words, e.g. isn't s = re.sub(r"((\w)['](\W|$))","\g<2>\"\g<3>", s) s = re.sub(r"((\W|^)['](\w))","\g<2>\"\g<3>", s) s = re.sub(r"((\W)['](\W))","\g<2>\"\g<3>", s) js += s + '\n' if errorlevel == 0: # Get rid of any empty objects js = js.replace("{}","") # get rid of trailing commas js = re.sub(r",(\s*(\}|\]))","\g<1>", js) js = re.sub(r",\s*$","", js) # catalogue calls return a set of values but without at array enclosure so will fail syntax check if not use_catalogue_call: try: jso = json.loads(js) # prettify js = json.dumps(jso, sort_keys=False, indent=4, separators=(',', ': ')) except Exception as e: print(" "+e) # Stop malformed machine json screwing up everything else! js = '' else: raise Exception("Syntax error") return js
def tests(tests): scad_dir = "tests" deps_dir = scad_dir + "/deps" png_dir = scad_dir + "/png" bom_dir = scad_dir + "/bom" for dir in [deps_dir, png_dir, bom_dir]: if not os.path.isdir(dir): os.makedirs(dir) index = {} bodies = {} done = [] times.read_times() options.check_options(deps_dir) # # Make cover pic if does not exist as very slow. Delete it to force an update. # png_name = "libtest.png" scad_name = "libtest.scad" if os.path.isfile(scad_name): libtest = True lib_blurb = scrape_blurb(scad_name) if not os.path.isfile(png_name): openscad.run(colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,50,0,340,500", "--autocenter", "--viewall", "-o", png_name, scad_name) do_cmd([ "magick", png_name, "-trim", "-resize", "1280", "-bordercolor", background, "-border", "10", png_name ]) else: # # Project tests so just a title # libtest = False project = ' '.join( word[0].upper() + word[1:] for word in os.path.basename(os.getcwd()).split('_')) lib_blurb = '#' + project + ' Tests\n' doc_base_name = "readme" if libtest else "tests" doc_name = doc_base_name + ".md" # # List of individual part files # scads = [ i for i in sorted(os.listdir(scad_dir), key=lambda s: s.lower()) if i[-5:] == ".scad" ] types = [] for scad in scads: base_name = scad[:-5] if not tests or base_name in tests: done.append(base_name) print('\n' + base_name) cap_name = base_name[0].capitalize() + base_name[1:] base_name = base_name.lower() scad_name = scad_dir + '/' + scad png_name = png_dir + '/' + base_name + '.png' bom_name = bom_dir + '/' + base_name + '.json' objects_name = None vits_name = 'vitamins/' + base_name + '.scad' if is_plural(base_name) and os.path.isfile(vits_name): objects_name = vits_name locations = [] if os.path.isdir('vitamins'): locations.append( ('vitamins/' + depluralise(base_name) + '.scad', 'Vitamins')) if os.path.isdir('printed'): locations.append(('printed/' + base_name + '.scad', 'Printed')) if os.path.isdir('utils'): locations.append(('utils/' + base_name + '.scad', 'Utilities')) if libtest and os.path.isdir('utils/core'): locations.append( ('utils/core/' + base_name + '.scad', 'Core Utilities')) for name, type in locations: if os.path.isfile(name): impl_name = name break else: if libtest: print("Can't find implementation!") continue else: type = 'Tests' # OK when testing part of a project impl_name = None if libtest: vsplit = "AJR" + chr(ord('Z') + 1) vtype = locations[0][1] types = [ vtype + ' ' + vsplit[i] + '-' + chr(ord(vsplit[i + 1]) - 1) for i in range(len(vsplit) - 1) ] + [loc[1] for loc in locations[1:]] if type == vtype: for i in range(1, len(vsplit)): if cap_name[0] < vsplit[i]: type = types[i - 1] break else: if not types: types = [loc[1] for loc in locations ] # No need to split up the vitamin list if not type in types: # Will happen when implementation is not found and type is set to Tests types.append(type) for t in types: if not t in bodies: bodies[t] = [] index[t] = [] body = bodies[type] index[type] += [cap_name] body += ['<a name="%s"></a>' % cap_name] body += ["## " + cap_name] doc = None if impl_name: doc = scrape_code(impl_name) blurb = doc["blurb"] else: blurb = scrape_blurb(scad_name) if not len(blurb): print("Blurb not found!") else: body += [blurb] if objects_name: body += [ "[%s](%s) Object definitions.\n" % (objects_name, objects_name) ] if impl_name: body += ["[%s](%s) Implementation.\n" % (impl_name, impl_name)] body += [ "[%s](%s) Code for this example.\n" % (scad_name.replace('\\', '/'), scad_name) ] if doc: for thing, heading in [("properties", "Function"), ("functions", "Function"), ("modules", "Module")]: things = doc[thing] if things: body += [ '### %s\n| %s | Description |\n|:--- |:--- |' % (thing.title(), heading) ] for item in sorted(things): body += ['| `%s` | %s |' % (item, things[item])] body += [''] body += ["![%s](%s)\n" % (base_name, png_name)] dname = deps_name(deps_dir, scad.lower()) oldest = png_name if mtime(png_name) < mtime( bom_name) else bom_name changed = check_deps(oldest, dname) changed = times.check_have_time(changed, scad_name) changed = options.have_changed(changed, oldest) if changed: print(changed) t = time.time() tmp_name = 'tmp.png' openscad.run_list(options.list() + [ "-D$bom=2", colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,70,0,315,500", "--autocenter", "--viewall", "-d", dname, "-o", tmp_name, scad_name ]) times.add_time(scad_name, t) do_cmd([ "magick", tmp_name, "-trim", "-resize", "1000x600", "-bordercolor", background, "-border", "10", tmp_name ]) update_image(tmp_name, png_name) BOM = bom.parse_bom() with open(bom_name, 'wt') as outfile: json.dump(BOM.flat_data(), outfile, indent=4) with open(bom_name, "rt") as bom_file: BOM = json.load(bom_file) for thing, heading in [("vitamins", "Module call | BOM entry"), ("printed", "Filename"), ("routed", "Filename"), ("assemblies", "Name")]: things = BOM[thing] if things: body += [ '### %s\n| Qty | %s |\n| ---:|:--- |%s' % (thing.title(), heading, ':---|' if '|' in heading else '') ] for item in sorted(things, key=lambda s: s.split(":")[-1]): name = item desc = '' if thing == "vitamins": vit = item.split(':') name = '`' + vit[0] + '`' if vit[0] else '' while '[[' in name and ']]' in name: i = name.find('[[') j = name.find(']]') + 2 name = name.replace(name[i:j], '[ ... ]') desc = vit[1] body += [ '| %3d | %s | %s |' % (things[item]["count"], name, desc) ] else: count = things[ item] if thing == 'assemblies' else things[ item]["count"] body += ['| %3d | %s |' % (count, name)] body += [''] body += ['\n<a href="#top">Top</a>'] body += ["\n---"] for test in done: if test in tests: tests.remove(test) if tests: for test in tests: print(Fore.MAGENTA + "Could not find a test called", test, Fore.WHITE) usage() with open(doc_name, "wt") as doc_file: print(lib_blurb, file=doc_file) print('## Table of Contents<a name="top"/>', file=doc_file) print('<table><tr>', file=doc_file) n = 0 for type in types: print('<th align="left"> %s </th>' % type, end='', file=doc_file) n = max(n, len(index[type])) print('</tr>', file=doc_file) for i in range(n): print('<tr>', file=doc_file, end='') for type in types: if i < len(index[type]): name = sorted(index[type])[i] print('<td> <a href = "#' + name + '">' + name + '</a> </td>', file=doc_file, end='') else: print('<td></td>', file=doc_file, end='') print('</tr>', file=doc_file) print('</table>\n\n---', file=doc_file) for type in types: for line in bodies[type]: print(line, file=doc_file) with open(doc_base_name + ".html", "wt") as html_file: do_cmd(("python -m markdown -x tables " + doc_name).split(), html_file) times.print_times() do_cmd(('codespell -L od ' + doc_name).split())
def boms(machine, assembly=None): bom_dir = machine + "/bom" if assembly: bom_dir += "/accessories" if not os.path.isdir(bom_dir): os.makedirs(bom_dir) else: assembly = "machine_assembly" if os.path.isdir(bom_dir): shutil.rmtree(bom_dir) sleep(0.1) os.makedirs(bom_dir) f = open("scad/conf/machine.scad", "wt") f.write("include <%s_config.scad>\n" % machine) f.close() # # Find the scad file that makes the module # scad_file = find_scad_file(assembly) if not scad_file: raise Exception("can't find source for " + assembly) # # make a file to use the module # bom_maker_name = source_dir + "/bom.scad" f = open(bom_maker_name, "w") f.write("use <%s>\n" % scad_file) f.write("%s();\n" % assembly) f.close() # # Run openscad # openscad.run("-D", "$bom=2", "-o", "dummy.csg", bom_maker_name) print("Generating bom ...", end=" ") main = BOM() stack = [] for line in open("openscad.log"): pos = line.find('ECHO: "') if pos > -1: s = line[pos + 7:line.rfind('"')] if s[-1] == '/': ass = s[:-1] if stack: main.assemblies[stack[-1]].add_assembly( ass) #add to nested BOM stack.append(ass) main.add_assembly(ass) #add to flat BOM else: if s[0] == '/': if s[1:] != stack[-1]: raise Exception("Mismatched assembly " + s[1:] + str(stack)) stack.pop() else: main.add_part(s) if stack: main.assemblies[stack[-1]].add_part(s) if assembly == "machine_assembly": main.print_bom(True, open(bom_dir + "/bom.txt", "wt")) for ass in sorted(main.assemblies): f = open(bom_dir + "/" + ass + ".txt", "wt") bom = main.assemblies[ass] print(bom.make_name(ass) + ":", file=f) bom.print_bom(False, f) f.close() print(" done")
def boms(machine, assembly = None): bom_dir = machine + "/bom" if assembly: bom_dir += "/accessories" if not os.path.isdir(bom_dir): os.makedirs(bom_dir) else: assembly = "machine_assembly" if os.path.isdir(bom_dir): shutil.rmtree(bom_dir) sleep(0.1) os.makedirs(bom_dir) f = open("scad/conf/machine.scad","wt") f. write("include <%s_config.scad>\n" % machine); f.close() # # Find the scad file that makes the module # scad_file = find_scad_file(assembly) if not scad_file: raise Exception("can't find source for " + assembly) # # make a file to use the module # bom_maker_name = source_dir + "/bom.scad" f = open(bom_maker_name, "w") f.write("use <%s>\n" % scad_file) f.write("%s();\n" % assembly); f.close() # # Run openscad # openscad.run("-D","$bom=2","-o", "dummy.csg", bom_maker_name) print("Generating bom ...", end=" ") main = BOM() stack = [] for line in open("openscad.log"): pos = line.find('ECHO: "') if pos > -1: s = line[pos + 7 : line.rfind('"')] if s[-1] == '/': ass = s[:-1] if stack: main.assemblies[stack[-1]].add_assembly(ass) #add to nested BOM stack.append(ass) main.add_assembly(ass) #add to flat BOM else: if s[0] == '/': if s[1:] != stack[-1]: raise Exception("Mismatched assembly " + s[1:] + str(stack)) stack.pop() else: main.add_part(s) if stack: main.assemblies[stack[-1]].add_part(s) if assembly == "machine_assembly": main.print_bom(True, open(bom_dir + "/bom.txt","wt")) for ass in sorted(main.assemblies): f = open(bom_dir + "/" + ass + ".txt", "wt"); bom = main.assemblies[ass] print(bom.make_name(ass) + ":", file=f) bom.print_bom(False, f) f.close() print(" done")
def stls(machine, parts = None): # # Make the target directory # target_dir = machine + "/stls" if os.path.isdir(target_dir): if not parts: shutil.rmtree(target_dir) #if making the BOM clear the directory first os.makedirs(target_dir) else: os.makedirs(target_dir) # # Set the target machine # f = open("scad/conf/machine.scad","wt") f. write("include <%s_config.scad>\n" % machine); f.close() # # Decide which files to make # if parts: targets = list(parts) #copy the list so we dont modify the list passed in else: targets = bom_to_stls(machine) # # Find all the scad files # used = [] for filename in os.listdir(source_dir): if filename[-5:] == ".scad": # # find any modules ending in _stl # for line in open(source_dir + "/" + filename, "r").readlines(): words = line.split() if(len(words) and words[0] == "module"): module = words[1].split('(')[0] stl = module.replace("_stl", ".stl") if stl in targets: # # make a file to use the module # stl_maker_name = source_dir + "/stl.scad" f = open(stl_maker_name, "w") f.write("use <%s>\n" % filename) f.write("%s();\n" % module); f.close() # # Run openscad on the created file # stl_name = target_dir + "/" + module[:-4] + ".stl" openscad.run("-o", stl_name, stl_maker_name) c14n_stl.canonicalise(stl_name) targets.remove(stl) # # Add the files on the BOM to the used list for plates.py # for line in open("openscad.log"): if line[:7] == 'ECHO: "' and line[-6:] == '.stl"\n': used.append(line[7:-2]) # # List the ones we didn't find # for module in targets: print "Could not find", module return used
def parse_vitamin(vitaminscad, use_catalogue_call=False): tempscadfile = "temp.scad" logfile = 'openscad.log' vitamincall = vitaminscad[:-5] print(" Calling: " + vitamincall + "();") # Generate a wrapper scad file for the vitamin file with open(tempscadfile, "w") as f: f.write("include <../config/config.scad>\n") f.write("include <../vitamins/" + vitaminscad + ">\n") if use_catalogue_call: f.write(vitamincall + "_Catalogue();\n") else: f.write(vitamincall + "();\n") openscad.run('-D', '$ShowBOM=true', '-o', 'dummy.csg', tempscadfile) js = '' errorlevel = 0 for line in open(logfile, "rt").readlines(): # errors r = re.search(r".*syntax error$", line, re.I) if r: print(" Syntax error!") print(line) errorlevel = 2 continue # echo lines r = re.search(r'^.*ECHO\:\s\"(.*)\"$', line, re.I) if r: s = r.group(1) # rewrite single quotes to double quotes, except for where they are used in words, e.g. isn't s = re.sub(r"((\w)['](\W|$))", "\g<2>\"\g<3>", s) s = re.sub(r"((\W|^)['](\w))", "\g<2>\"\g<3>", s) s = re.sub(r"((\W)['](\W))", "\g<2>\"\g<3>", s) js += s + '\n' if errorlevel == 0: # Get rid of any empty objects js = js.replace("{}", "") # get rid of trailing commas js = re.sub(r",(\s*(\}|\]))", "\g<1>", js) js = re.sub(r",\s*$", "", js) # catalogue calls return a set of values but without at array enclosure so will fail syntax check if not use_catalogue_call: try: jso = json.loads(js) # prettify js = json.dumps(jso, sort_keys=False, indent=4, separators=(',', ': ')) except Exception as e: print(" " + e) # Stop malformed machine json screwing up everything else! js = '' else: raise Exception("Syntax error") return js
def stls(machine): # # Make the target directory # target_dir = machine + "/stls" if os.path.isdir(target_dir): shutil.rmtree(target_dir) os.makedirs(target_dir) # # Set the target machine # f = open("scad/conf/machine.scad","wt") f. write("include <%s_config.scad>\n" % machine); f.close() # # Make a list of all the stls in the BOM # targets = [] for line in open(machine + "/bom/bom.txt", "rt").readlines(): words = line.split() if words: last_word = words[-1] if len(last_word) > 4 and last_word[-4:] == ".stl": targets.append(last_word.replace(".stl", "_stl")) # # Find all the scad files # for filename in os.listdir(source_dir): if filename[-5:] == ".scad": # # find any modules ending in _stl # for line in open(source_dir + "/" + filename, "r").readlines(): words = line.split() if(len(words) and words[0] == "module"): module = words[1].split('(')[0] if module in targets: # # make a file to use the module # stl_maker_name = source_dir + "/stl.scad" f = open(stl_maker_name, "w") f.write("use <%s/%s>\n" % (source_dir, filename)) f.write("%s();\n" % module); f.close() # # Run openscad on the created file # stl_name = target_dir + "/" + module[:-4] + ".stl" openscad.run("-o", stl_name, stl_maker_name) targets.remove(module) # # List the ones we didn't find # for module in targets: print "Could not find", module
def stls(machine, parts=None): # # Make the target directory # target_dir = machine + "/stls" if os.path.isdir(target_dir): if not parts: shutil.rmtree( target_dir) #if making the BOM clear the directory first os.makedirs(target_dir) else: os.makedirs(target_dir) # # Set the target machine # f = open(source_dir + "/conf/machine.scad", "wt") f.write("include <%s_config.scad>\n" % machine) f.close() # # Decide which files to make # if parts: targets = list( parts) #copy the list so we dont modify the list passed in else: targets = bom_to_stls(machine) # # Find all the scad files # used = [] for filename in os.listdir(source_dir): if filename[-5:] == ".scad": # # find any modules ending in _stl # for line in open(source_dir + "/" + filename, "r").readlines(): words = line.split() if (len(words) and words[0] == "module"): module = words[1].split('(')[0] stl = module.replace("_stl", ".stl") if stl in targets: # # make a file to use the module # stl_maker_name = source_dir + "/stl.scad" f = open(stl_maker_name, "w") f.write("use <%s>\n" % filename) f.write("%s();\n" % module) f.close() # # Run openscad on the created file # stl_name = target_dir + "/" + module[:-4] + ".stl" openscad.run("-D$bom=1", "-o", stl_name, stl_maker_name) c14n_stl.canonicalise(stl_name) targets.remove(stl) # # Add the files on the BOM to the used list for plates.py # for line in open("openscad.log"): if line[:7] == 'ECHO: "' and line[-6:] == '.stl"\n': used.append(line[7:-2]) # # List the ones we didn't find # for module in targets: print("Could not find", module) return used
from __future__ import print_function import errno import logging import os import openscad logging.basicConfig(level=logging.DEBUG) output_folder = os.path.join('build') try: os.makedirs(output_folder) except OSError as e: if e.errno == errno.EEXIST and os.path.isdir(output_folder): pass else: raise openscad.run( 'splitflap.scad', os.path.join(output_folder, 'snapshot.png'), output_size = [1280, 1024], camera_translation = [0, 0, 0], camera_rotation = [60, 0, 135], camera_distance = 600, colorscheme = 'Nature', )
def render(target, type): # # Make the target directory # top_dir = set_config(target, usage) tmp_dir = mktmpdir(top_dir) target_dir = top_dir + type + 's' bom_dir = top_dir + 'bom' if not os.path.isdir(target_dir): os.makedirs(target_dir) # # Find all the parts # parts = bom_to_parts(bom_dir, type) # # Read the json bom to get the colours # bom_file = bom_dir + "/bom.json" with open(bom_file) as json_file: flat_bom = json.load(json_file) things = {'stl': 'printed', 'dxf': 'routed'}[type] colours = {} for ass in flat_bom: for part in ass[things]: obj = ass[things][part] if "colour" in obj: colours[part] = obj["colour"] # # Remove unused png files # for file in os.listdir(target_dir): if file.endswith('.png'): if not file[:-4] + '.' + type in parts: print("Removing %s" % file) os.remove(target_dir + '/' + file) # # Render the parts # for part in parts: part_file = target_dir + '/' + part png_name = target_dir + '/' + part[:-4] + '.png' # # make a file to import the stl # if mtime(part_file) > mtime(png_name): png_maker_name = tmp_dir + "/png.scad" pp1 = [0, 146 / 255, 0] colour = pp1 if part in colours: colour = colours[part] if not '[' in colour: colour = '"' + colour + '"' with open(png_maker_name, "w") as f: f.write('color(%s) import("%s");\n' % (colour, reltmp(part_file, target))) cam = "--camera=0,0,0,70,0,315,500" if type == 'stl' else "--camera=0,0,0,0,0,0,500" render = "--preview" if type == 'stl' or colour != pp1 else "--render" tmp_name = tmp_dir + '/' + part[:-4] + '.png' openscad.run("-o", tmp_name, png_maker_name, colour_scheme, "--projection=p", "--imgsize=4096,4096", cam, render, "--autocenter", "--viewall") do_cmd(( "magick " + tmp_name + " -trim -resize 280x280 -background %s -gravity Center -extent 280x280 -bordercolor %s -border 10 %s" % (background, background, tmp_name)).split()) update_image(tmp_name, png_name) os.remove(png_maker_name) # # Remove tmp dir # rmtmpdir(tmp_dir)
def tests(tests): scad_dir = "tests" deps_dir = scad_dir + "/deps" png_dir = scad_dir + "/png" bom_dir = scad_dir + "/bom" for dir in [deps_dir, png_dir, bom_dir]: if not os.path.isdir(dir): os.makedirs(dir) doc_name = "readme.md" index = {} bodies = {} times.read_times() options.check_options(deps_dir) # # Make cover pic if does not exist as very slow. Delete it to force an update. # png_name = "libtest.png" scad_name = "libtest.scad" if not os.path.isfile(png_name): openscad.run(colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,50,0,340,500", "--autocenter", "--viewall", "-o", png_name, scad_name) do_cmd([ "magick", png_name, "-trim", "-resize", "1280", "-bordercolor", background, "-border", "10", png_name ]) # # List of individual part files # scads = [i for i in sorted(os.listdir(scad_dir)) if i[-5:] == ".scad"] for scad in scads: base_name = scad[:-5] if not tests or base_name in tests: print(base_name) cap_name = base_name[0].capitalize() + base_name[1:] scad_name = scad_dir + '/' + scad png_name = png_dir + '/' + base_name + '.png' bom_name = bom_dir + '/' + base_name + '.json' objects_name = None vits_name = 'vitamins/' + base_name + '.scad' if is_plural(base_name) and os.path.isfile(vits_name): objects_name = vits_name locations = [ ('vitamins/' + depluralise(base_name) + '.scad', 'Vitamins'), ('printed/' + base_name + '.scad', 'Printed'), ('utils/' + base_name + '.scad', 'Utilities'), ('utils/core/' + base_name + '.scad', 'Core Utilities'), ] for name, type in locations: if os.path.isfile(name): impl_name = name break else: print("Can't find implementation!") continue vsplit = "M" vtype = locations[0][1] types = [ vtype + ' A-' + vsplit[0], vtype + ' ' + chr(ord(vsplit) + 1) + '-Z' ] + [loc[1] for loc in locations[1:]] if type == vtype: type = types[0] if cap_name[0] <= vsplit else types[1] for t in types: if not t in bodies: bodies[t] = [] index[t] = [] body = bodies[type] index[type] += [cap_name] body += ['<a name="%s"></a>' % cap_name] body += ["## " + cap_name] doc = None if impl_name: doc = scrape_code(impl_name) blurb = doc["blurb"] else: blurb = scrape_blurb(scad_name) if not len(blurb): print("Blurb not found!") else: body += [blurb] if objects_name: body += [ "[%s](%s) Object definitions.\n" % (objects_name, objects_name) ] if impl_name: body += ["[%s](%s) Implementation.\n" % (impl_name, impl_name)] body += [ "[%s](%s) Code for this example.\n" % (scad_name.replace('\\', '/'), scad_name) ] if doc: for thing, heading in [("properties", "Function"), ("functions", "Function"), ("modules", "Module")]: things = doc[thing] if things: body += [ '### %s\n| %s | Description |\n|:--- |:--- |' % (thing.title(), heading) ] for item in sorted(things): body += [ '| ```%s``` | %s |' % (item, things[item]) ] body += [''] body += ["![%s](%s)\n" % (base_name, png_name)] dname = deps_name(deps_dir, scad) oldest = png_name if mtime(png_name) < mtime( bom_name) else bom_name changed = check_deps(oldest, dname) changed = times.check_have_time(changed, scad_name) changed = options.have_changed(changed, oldest) if changed: print(changed) t = time.time() tmp_name = 'tmp.png' openscad.run_list(options.list() + [ "-D$bom=2", colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,70,0,315,500", "--autocenter", "--viewall", "-d", dname, "-o", tmp_name, scad_name ]) times.add_time(scad_name, t) do_cmd([ "magick", tmp_name, "-trim", "-resize", "1000x600", "-bordercolor", background, "-border", "10", tmp_name ]) update_image(tmp_name, png_name) BOM = bom.parse_bom() with open(bom_name, 'wt') as outfile: json.dump(BOM.flat_data(), outfile, indent=4) with open(bom_name, "rt") as bom_file: BOM = json.load(bom_file) for thing, heading in [("vitamins", "Module call | BOM entry"), ("printed", "Filename"), ("routed", "Filename"), ("assemblies", "Name")]: things = BOM[thing] if things: body += [ '### %s\n| Qty | %s |\n| ---:|:--- |%s' % (thing.title(), heading, ':---|' if '|' in heading else '') ] for item in sorted(things, key=lambda s: s.split(":")[-1]): name = item desc = '' if thing == "vitamins": vit = item.split(':') name = '```' + vit[0] + '```' if vit[0] else '' while '[[' in name and ']]' in name: i = name.find('[[') j = name.find(']]') + 2 name = name.replace(name[i:j], '[ ... ]') desc = vit[1] body += [ '| %3d | %s | %s |' % (things[item], name, desc) ] else: body += ['| %3d | %s |' % (things[item], name)] body += [''] body += ['\n<a href="#top">Top</a>'] body += ["\n---"] with open(doc_name, "wt") as doc_file: print('# NopSCADlib', file=doc_file) print('''\ An ever expanding library of parts modelled in OpenSCAD useful for 3D printers and enclosures for electronics, etc. It contains lots of vitamins (the RepRap term for non-printed parts), some general purpose printed parts and some utilities. There are also Python scripts to generate Bills of Materials (BOMs), STL files for all the printed parts, DXF files for CNC routed parts in a project and a manual containing assembly instructions and exploded views by scraping markdown embedded in OpenSCAD comments, [see scripts](scripts/readme.md). A simple example project can be found [here](examples/MainsBreakOutBox/readme.md). For more examples of what it can make see the [gallery](gallery/readme.md). The license is GNU General Public License v3.0, see [COPYING](COPYING). See [usage](docs/usage.md) for requirements, installation instructions and a usage guide. <img src="libtest.png" width="100%"/>\n ''', file=doc_file) print('## Table of Contents<a name="top"/>', file=doc_file) print('<table><tr>', file=doc_file) n = 0 for type in types: print('<th align="left"> %s </th>' % type, end='', file=doc_file) n = max(n, len(index[type])) print('</tr>', file=doc_file) for i in range(n): print('<tr>', file=doc_file, end='') for type in types: if i < len(index[type]): name = sorted(index[type])[i] print('<td> <a href = "#' + name + '">' + name + '</a> </td>', file=doc_file, end='') else: print('<td></td>', file=doc_file, end='') print('</tr>', file=doc_file) print('</table>\n\n---', file=doc_file) for type in types: for line in bodies[type]: print(line, file=doc_file) with open("readme.html", "wt") as html_file: do_cmd("python -m markdown -x tables readme.md".split(), html_file) times.print_times() do_cmd('codespell -L od readme.md'.split())
def views(target, do_assemblies=None): done_assemblies = [] # # Make the target directory # top_dir = set_config(target) target_dir = top_dir + 'assemblies' deps_dir = top_dir + "deps" bom_dir = top_dir + "bom" if not os.path.isdir(target_dir): os.makedirs(target_dir) if not os.path.isdir(deps_dir): os.makedirs(deps_dir) times.read_times(target_dir) bounds_fname = top_dir + 'stls/bounds.json' with open(bounds_fname) as json_file: bounds_map = json.load(json_file) # # Find all the assemblies # assemblies = bom_to_assemblies(bom_dir, bounds_map) for file in os.listdir(target_dir): if file.endswith('.png'): assembly = file[:-4].replace('_assembled', '_assembly') if assembly.endswith('_tn'): assembly = assembly[:-3] if not assembly in assemblies: print("Removing %s" % file) os.remove(target_dir + '/' + file) # # Find all the scad files # main_blurb = None lib_dir = os.environ['OPENSCADPATH'] + '/NopSCADlib/printed' for dir in [source_dir, lib_dir]: for filename in os.listdir(dir): if filename.endswith('.scad'): # # find any modules with names ending in _assembly # with open(dir + "/" + filename, "r") as f: lines = f.readlines() line_no = 0 for line in lines: words = line.split() if len(words) and words[0] == "module": module = words[1].split('(')[0] if is_assembly(module): if module in assemblies: # # Scrape the assembly instructions # for ass in flat_bom: if ass["name"] == module: if not "blurb" in ass: ass["blurb"] = blurb.scrape_module_blurb( lines[:line_no]) break if not do_assemblies or module in do_assemblies: # # make a file to use the module # png_maker_name = 'png.scad' with open(png_maker_name, "w") as f: f.write("use <%s/%s>\n" % (dir, filename)) f.write("%s();\n" % module) # # Run openscad on th created file # dname = deps_name(deps_dir, filename) for explode in [0, 1]: png_name = target_dir + '/' + module + '.png' if not explode: png_name = png_name.replace( '_assembly', '_assembled') changed = check_deps(png_name, dname) changed = times.check_have_time( changed, png_name) tmp_name = 'tmp.png' if changed: print(changed) t = time.time() openscad.run( "-D$pose=1", "-D$explode=%d" % explode, colour_scheme, "--projection=p", "--imgsize=4096,4096", "--autocenter", "--viewall", "-d", dname, "-o", tmp_name, png_maker_name) times.add_time(png_name, t) do_cmd([ "magick", tmp_name, "-trim", "-resize", "1004x1004", "-bordercolor", background, "-border", "10", tmp_name ]) update_image(tmp_name, png_name) tn_name = png_name.replace( '.png', '_tn.png') if mtime(png_name) > mtime(tn_name): do_cmd(( "magick " + png_name + " -trim -resize 280x280 -background " + background + " -gravity Center -extent 280x280 -bordercolor " + background + " -border 10 " + tmp_name).split()) update_image(tmp_name, tn_name) os.remove(png_maker_name) done_assemblies.append(module) else: if module == 'main_assembly': main_blurb = blurb.scrape_module_blurb( lines[:line_no]) line_no += 1 times.print_times() # # Build the document # for print_mode in [True, False]: doc_name = top_dir + "readme.md" with open(doc_name, "wt") as doc_file: # # Title, description and picture # project = ' '.join( word[0].upper() + word[1:] for word in os.path.basename(os.getcwd()).split('_')) print('<a name="TOP"></a>\n# %s' % project, file=doc_file) main_file = bom.find_scad_file('main_assembly') if not main_file: raise Exception("can't find source for main_assembly") text = blurb.scrape_blurb(source_dir + '/' + main_file) if len(text): print(text, file=doc_file, end='') else: if print_mode: print(Fore.MAGENTA + "Missing project description" + Fore.WHITE) print('![Main Assembly](assemblies/%s.png)\n' % flat_bom[-1]["name"].replace('_assembly', '_assembled'), file=doc_file) eop(print_mode, doc_file, first=True) # # Build TOC # print('## Table of Contents', file=doc_file) print('1. [Parts list](#Parts_list)', file=doc_file) for ass in flat_bom: name = ass["name"] cap_name = name.replace('_', ' ').title() print('1. [%s](#%s)' % (cap_name, name), file=doc_file) print(file=doc_file) eop(print_mode, doc_file) # # Global BOM # print('<a name="Parts_list"></a>\n## Parts list', file=doc_file) types = ["vitamins", "printed", "routed"] headings = { "vitamins": "vitamins", "printed": "3D printed parts", "routed": "CNC routed parts" } things = {} for t in types: things[t] = {} for ass in flat_bom: for t in types: for thing in ass[t]: if thing in things[t]: things[t][thing] += ass[t][thing] else: things[t][thing] = ass[t][thing] for ass in flat_bom: name = ass["name"][:-9].replace('_', ' ').title().replace( ' ', ' ') print( '| <span style="writing-mode: vertical-rl; text-orientation: mixed;">%s</span> ' % name, file=doc_file, end='') print( '| <span style="writing-mode: vertical-rl; text-orientation: mixed;">TOTALS</span> | |', file=doc_file) print(('|--:' * len(flat_bom) + '|--:|:--|'), file=doc_file) for t in types: if things[t]: totals = {} heading = headings[t][0:1].upper() + headings[t][1:] print(('| ' * len(flat_bom) + '| | **%s** |') % heading, file=doc_file) for thing in sorted(things[t], key=lambda s: s.split(":")[-1]): for ass in flat_bom: count = ass[t][thing] if thing in ass[t] else 0 print('| %s ' % pad(count if count else '.', 2, 1), file=doc_file, end='') name = ass["name"] if name in totals: totals[name] += count else: totals[name] = count print('| %s | %s |' % (pad(things[t][thing], 2, 1), pad(thing.split(":")[-1], 2)), file=doc_file) grand_total = 0 for ass in flat_bom: name = ass["name"] total = totals[name] if name in totals else 0 print('| %s ' % pad(total if total else '.', 2, 1), file=doc_file, end='') grand_total += total print("| %s | %s |" % (pad(grand_total, 2, 1), pad('Total %s count' % headings[t], 2)), file=doc_file) print(file=doc_file) eop(print_mode, doc_file) # # Assembly instructions # for ass in flat_bom: name = ass["name"] cap_name = name.replace('_', ' ').title() if ass["count"] > 1: print('<a name="%s"></a>\n## %d x %s' % (name, ass["count"], cap_name), file=doc_file) else: print('<a name="%s"></a>\n## %s' % (name, cap_name), file=doc_file) vitamins = ass["vitamins"] if vitamins: print("### Vitamins", file=doc_file) print("|Qty|Description|", file=doc_file) print("|--:|:----------|", file=doc_file) for v in sorted(vitamins, key=lambda s: s.split(":")[-1]): print("|%d|%s|" % (vitamins[v], v.split(":")[1]), file=doc_file) print("\n", file=doc_file) printed = ass["printed"] if printed: print('### 3D Printed parts', file=doc_file) keys = sorted(list(printed.keys())) for i in range(len(keys)): p = keys[i] print('%s %d x %s |' % ('\n|' if not (i % 3) else '', printed[p], p), file=doc_file, end='') if (i % 3) == 2 or i == len(printed) - 1: n = (i % 3) + 1 print('\n|%s' % ('--|' * n), file=doc_file) for j in range(n): part = keys[i - n + j + 1] print('| ![%s](stls/%s) %s' % (part, part.replace('.stl', '.png'), '|\n' if j == j - 1 else ''), end='', file=doc_file) print('\n', file=doc_file) print('\n', file=doc_file) routed = ass["routed"] if routed: print("### CNC Routed parts", file=doc_file) keys = sorted(list(routed.keys())) for i in range(len(keys)): r = keys[i] print('%s %d x %s |' % ('\n|' if not (i % 3) else '', routed[r], r), file=doc_file, end='') if (i % 3) == 2 or i == len(routed) - 1: n = (i % 3) + 1 print('\n|%s' % ('--|' * n), file=doc_file) for j in range(n): part = keys[i - n + j + 1] print('| ![%s](dxfs/%s) %s' % (part, part.replace('.dxf', '.png'), '|\n' if j == j - 1 else ''), end='', file=doc_file) print('\n', file=doc_file) print('\n', file=doc_file) sub_assemblies = ass["assemblies"] if sub_assemblies: print("### Sub-assemblies", file=doc_file) keys = sorted(list(sub_assemblies.keys())) for i in range(len(keys)): a = keys[i] print('%s %d x %s |' % ('\n|' if not (i % 3) else '', sub_assemblies[a], a), file=doc_file, end='') if (i % 3) == 2 or i == len(keys) - 1: n = (i % 3) + 1 print('\n|%s' % ('--|' * n), file=doc_file) for j in range(n): a = keys[i - n + j + 1].replace( '_assembly', '_assembled') print('| ![%s](assemblies/%s) %s' % (a, a + '_tn.png', '|\n' if j == j - 1 else ''), end='', file=doc_file) print('\n', file=doc_file) print('\n', file=doc_file) small = not ass["big"] suffix = '_tn.png' if small else '.png' print('### Assembly instructions', file=doc_file) print('![%s](assemblies/%s)\n' % (name, name + suffix), file=doc_file) if "blurb" in ass and ass["blurb"]: print(ass["blurb"], file=doc_file) else: if print_mode: print( Fore.MAGENTA + "Missing instructions for %s" % name, Fore.WHITE) name = name.replace('_assembly', '_assembled') print('![%s](assemblies/%s)\n' % (name, name + suffix), file=doc_file) eop(print_mode, doc_file, last=ass == flat_bom[-1] and not main_blurb) # # If main module is suppressed print any blurb here # if main_blurb: print(main_blurb, file=doc_file) eop(print_mode, doc_file, last=True) # # Convert to HTML # html_name = "printme.html" if print_mode else "readme.html" with open(top_dir + html_name, "wt") as html_file: do_cmd(("python -m markdown -x tables -x sane_lists " + doc_name).split(), html_file) # # Spell check # do_cmd('codespell -L od readme.md'.split()) # # List the ones we didn't find # missing = set() for assembly in assemblies + (do_assemblies if do_assemblies else []): if assembly not in done_assemblies: missing.add(assembly) if missing: for assembly in missing: print(Fore.MAGENTA + "Could not find a module called", assembly, Fore.WHITE) sys.exit(1)
def make_parts(target, part_type, parts=None): # # Check list of parts is the correct type # if parts: for p in parts: if not p.endswith('.' + part_type): usage(part_type) # # Make the target directory # top_dir = set_config(target, lambda: usage(part_type)) target_dir = top_dir + part_type + 's' deps_dir = target_dir + "/deps" bom_dir = top_dir + "bom" tmp_dir = mktmpdir(top_dir) if not os.path.isdir(target_dir): os.makedirs(target_dir) if not os.path.isdir(deps_dir): os.makedirs(deps_dir) if os.path.isdir(top_dir + '/deps'): #old location shutil.rmtree(top_dir + '/deps') times.read_times(target_dir) # # Decide which files to make # all_parts = bom_to_parts(bom_dir, part_type) if parts: targets = list( parts) #copy the list so we dont modify the list passed in else: targets = list(all_parts) for file in os.listdir(target_dir): if file.endswith('.' + part_type): if not file in targets: print("Removing %s" % file) os.remove(target_dir + '/' + file) # # Read existing STL bounds # if part_type == 'stl': bounds_fname = target_dir + '/bounds.json' try: with open(bounds_fname) as json_file: bounds_map = json.load(json_file) except: bounds_map = {} # # Find all the scad files # module_suffix = '_dxf' if part_type == 'svg' else '_' + part_type for dir in source_dirs(bom_dir): if targets and os.path.isdir(dir): for filename in os.listdir(dir): if targets and filename[-5:] == ".scad": # # find any modules ending in _<part_type> # with open(dir + "/" + filename, "r") as f: for line in f.readlines(): words = line.split() if (len(words) and words[0] == "module"): module = words[1].split('(')[0] if module.endswith(module_suffix): base_name = module[:-4] part = base_name + '.' + part_type if part in targets: # # Run openscad on the created file # part_file = target_dir + "/" + part dname = deps_name(deps_dir, filename) changed = check_deps(part_file, dname) changed = times.check_have_time( changed, part) if part_type == 'stl' and not changed and not part in bounds_map: changed = "No bounds" if changed: print(changed) # # make a file to use the module # part_maker_name = tmp_dir + '/' + part_type + ".scad" with open(part_maker_name, "w") as f: f.write("use <%s/%s>\n" % (reltmp(dir, target), filename)) f.write("%s();\n" % module) t = time.time() openscad.run( "-D$bom=1", "-d", dname, "-o", part_file, part_maker_name) times.add_time(part, t) if part_type == 'stl': bounds = c14n_stl.canonicalise( part_file) bounds_map[part] = bounds os.remove(part_maker_name) targets.remove(part) # # Write new bounds file # if part_type == 'stl': with open(bounds_fname, 'w') as outfile: json.dump(bounds_map, outfile, indent=4) # # Remove tmp dir # rmtmpdir(tmp_dir) # # List the ones we didn't find # if targets: for part in targets: print("Could not find a module called", part[:-4] + module_suffix, "to make", part) usage(part_type) times.print_times(all_parts)
def make_parts(target, part_type, parts=None): # # Make the target directory # top_dir = set_config(target) target_dir = top_dir + part_type + 's' deps_dir = top_dir + "deps" if not os.path.isdir(target_dir): os.makedirs(target_dir) if not os.path.isdir(deps_dir): os.makedirs(deps_dir) times.read_times(target_dir) # # Decide which files to make # if parts: targets = list( parts) #copy the list so we dont modify the list passed in else: targets = bom_to_parts(target_dir, part_type) for file in os.listdir(target_dir): if file.endswith('.' + part_type): if not file in targets: print("Removing %s" % file) os.remove(target_dir + '/' + file) # # Read existing STL bounds # if part_type == 'stl': bounds_fname = target_dir + '/bounds.json' try: with open(bounds_fname) as json_file: bounds_map = json.load(json_file) except: bounds_map = {} # # Find all the scad files # lib_dir = os.environ['OPENSCADPATH'] + '/NopSCADlib/printed' module_suffix = '_dxf' if part_type == 'svg' else '_' + part_type for dir in [source_dir, lib_dir]: for filename in os.listdir(dir): if filename[-5:] == ".scad": # # find any modules ending in _<part_type> # with open(dir + "/" + filename, "r") as f: for line in f.readlines(): words = line.split() if (len(words) and words[0] == "module"): module = words[1].split('(')[0] if module.endswith(module_suffix): base_name = module[:-4] part = base_name + '.' + part_type if part in targets: # # make a file to use the module # part_maker_name = part_type + ".scad" with open(part_maker_name, "w") as f: f.write("use <%s/%s>\n" % (dir, filename)) f.write("%s();\n" % module) # # Run openscad on the created file # part_file = target_dir + "/" + part dname = deps_name(deps_dir, filename) changed = check_deps(part_file, dname) changed = times.check_have_time( changed, part) if part_type == 'stl' and not changed and not part in bounds_map: changed = "No bounds" if changed: print(changed) t = time.time() openscad.run("-D$bom=1", "-d", dname, "-o", part_file, part_maker_name) times.add_time(part, t) if part_type == 'stl': bounds = c14n_stl.canonicalise( part_file) bounds_map[part] = bounds targets.remove(part) os.remove(part_maker_name) # # Write new bounds file # if part_type == 'stl': with open(bounds_fname, 'w') as outfile: json.dump(bounds_map, outfile, indent=4) # # List the ones we didn't find # if targets: for part in targets: if part[-4:] != '.' + part_type: print(part, "is not a", part_type, "file") else: print("Could not find a module called", part[:-4] + module_suffix, "to make", part) sys.exit(1) times.print_times()
def views(machine): scad_dir = "views" render_dir = machine + os.sep + "views" if not os.path.isdir(render_dir): os.makedirs(render_dir) # # Set the target machine # set_machine(machine) # # List of individual part files # scads = [i for i in os.listdir(scad_dir) if i[-5:] == ".scad"] for scad in scads: scad_name = scad_dir + os.sep + scad png_name = render_dir + os.sep + scad[:-4] + "png" dx = None rx = None d = None for line in open(scad_name, "r").readlines(): m = re.match(r'\$vpt *= *\[ *(.*) *, *(.*) *, *(.*) *\].*', line[:-1]) if m: dx = float(m.group(1)) dy = float(m.group(2)) dz = float(m.group(3)) m = re.match(r'\$vpr *= *\[ *(.*) *, *(.*) *, *(.*) *\].*', line[:-1]) if m: rx = float(m.group(1)) ry = float(m.group(2)) rz = float(m.group(3)) m = re.match(r'\$vpd *= * *(.*) *;.*', line[:-1]) if m: d = float(m.group(1)) words = line.split() if len(words) > 3 and words[0] == "//": cmd = words[1] if cmd == "view" or cmd == "assembled" or cmd == "assembly": w = int(words[2]) * 2 h = int(words[3]) * 2 if len(words) > 10: dx = float(words[4]) dy = float(words[5]) dz = float(words[6]) rx = float(words[7]) ry = float(words[8]) rz = float(words[9]) d = float(words[10]) if dx == None or rx == None or d == None: print "Missing camera data in " + scad_name sys.exit(1) camera = "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f" % (dx, dy, dz, rx, ry, rz, d) exploded = "0" if cmd == "assembly": exploded = "1" if cmd == "assembled": png_name = png_name.replace("assembly","assembled") if not os.path.isfile(png_name) or os.path.getmtime(png_name) < os.path.getmtime(scad_name): openscad.run("--projection=p", ("--imgsize=%d,%d" % (w, h)), "--camera=" + camera, "-D$exploded=" + exploded, "-o", png_name, scad_name) print