def template(templatefn, incfn, name, vertices): minx, maxx, miny, maxy, minz, maxz = bbox.makebb(vertices) with open(templatefn, "r") as template: lines = template.readlines() lines = [line.replace('$name', name) for line in lines] lines = [line.replace('$incfn', incfn) for line in lines] lines = [ line.replace('$minx', '{}'.format(round(minx.item(), 3))) for line in lines ] lines = [ line.replace('$maxx', '{}'.format(round(maxx.item(), 3))) for line in lines ] lines = [ line.replace('$miny', '{}'.format(round(miny.item(), 3))) for line in lines ] lines = [ line.replace('$maxy', '{}'.format(round(maxy.item(), 3))) for line in lines ] lines = [ line.replace('$minz', '{}'.format(round(minz.item(), 3))) for line in lines ] lines = [ line.replace('$maxz', '{}'.format(round(maxz.item(), 3))) for line in lines ] return '\n'.join(lines)
def main(argv): """Main program. Keyword arguments: argv -- command line arguments (without program name!) """ parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-t', '--text', action='store_true', help='print text representation of the file') parser.add_argument('-b', '--binary', action='store_true', help='write binary representation of the file') parser.add_argument('-v', '--version', action='version', version=__version__) parser.add_argument('file', nargs='*', help='one or more file names') args = parser.parse_args(argv) if not args.file: parser.print_help() sys.exit(0) for fn in args.file: if not fn.lower().endswith('.stl'): w = 'The file "{}" is probably not an STL file, skipping.' print w.format(fn) continue try: vertices, name = stl.readstl(fn) if args.text or args.binary: facets, points = stl.toindexed(vertices) normals, vectors = stl.normals(facets, points) except ValueError as e: print fn + ':', e continue print "# Information for:", fn print "# Generated by stlinfo {}".format(__version__) print "# on {}.".format(time.asctime()) print '# Name: "{}"'.format(name) print '# Number of facets:', len(vertices)/3 minx, maxx, miny, maxy, minz, maxz = bbox.makebb(vertices) print '# Bounding box:' print '# {} ≤ x ≤ {}'.format(minx, maxx) print '# {} ≤ y ≤ {}'.format(miny, maxy) print '# {} ≤ z ≤ {}'.format(minz, maxz) if args.text: print '# Text representation:' print stl.text(name, facets, points, normals, vectors) if args.binary: on = utils.outname(fn, '.stl', '_bin') print '# Writing binary represtation to "{}".'.format(on) with open(on, 'w+b') as of: of.write(stl.binary(name, facets, points, normals, vectors))
def main(args): """Main program. Keyword arguments: argv -- command line arguments (without program name!) """ msg = utils.Msg() canvas_size = 200 infile, outfile, tr = utils.processargs(args, '.pdf', usage) msg.say('Reading STL file') try: vertices, _ = stl.readstl(infile) except ValueError as e: print((infile + ':', e)) sys.exit(1) msg.say('Calculating normal vectors') facets = vertices.reshape((-1, 3, 3)) normals = np.array([vecops.normal(a, b, c) for a, b, c in facets]) msg.say('Apply transformations to world coordinates') vertices = vecops.xform(tr, vertices) normals = vecops.xform(tr[0:3, 0:3], normals) msg.say('Making model-view matrix') minx, maxx, miny, maxy, _, maxz = bbox.makebb(vertices) width = maxx - minx height = maxy - miny dx = -(minx + maxx)/2 dy = -(miny + maxy)/2 dz = -maxz m = matrix.trans([dx, dy, dz]) sf = min(canvas_size/width, canvas_size/height) v = matrix.scale(sf, -sf) v[0, 3], v[1, 3] = canvas_size/2, canvas_size/2 mv = matrix.concat(m, v) msg.say('Transforming to view space') vertices = vecops.xform(mv, vertices) facets = vertices.reshape((-1, 3, 3)) # In the ortho projection on the z=0 plane, z+ is _towards_ the viewer msg.say('Determine visible facets') vf = [(f, n, 0.4*n[2]+0.5) for f, n in zip(facets, normals) if n[2] > 0] msg.say('{:.2f}% of facets is visible'.format(100*len(vf)/len(facets))) # Next, depth-sort the facets using the largest z-value of the # three vertices. msg.say('Depth-sorting visible facets') def fkey(t): (a, b, c), _, _ = t return max(a[2], b[2], c[2]) vf.sort(None, fkey) msg.say('Initialize drawing surface') out = cairo.PDFSurface(outfile, canvas_size, canvas_size) ctx = cairo.Context(out) ctx.set_line_cap(cairo.LINE_CAP_ROUND) ctx.set_line_join(cairo.LINE_JOIN_ROUND) ctx.set_line_width(0.25) msg.say('Drawing the triangles') for (a, b, c), _, i in vf: ctx.new_path() ctx.move_to(a[0], a[1]) ctx.line_to(b[0], b[1]) ctx.line_to(c[0], c[1]) ctx.close_path() ctx.set_source_rgb(i, i, i) ctx.fill_preserve() ctx.stroke() # Send output. out.show_page() out.finish() msg.say('Done')
def main(argv): """ Entry point of stl2pdf. Arguments: argv: Command line arguments (without program name!) """ parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--log', default='warning', choices=['debug', 'info', 'warning', 'error'], help="logging level (defaults to 'warning')") parser.add_argument('-c', '--canvas', dest='canvas_size', type=int, help="canvas size, defaults to 200 PostScript points", default=200) parser.add_argument( '-f', '--foreground', dest='fg', type=str, help="foreground color in 6-digit hexdecimal RGB (default E6E6E6)", default='E6E6E6') parser.add_argument( '-b', '--background', dest='bg', type=str, help="background color in 6-digit hexdecimal RGB (default FFFFFF)", default='FFFFFF') parser.add_argument('-o', '--output', dest='outfile', type=str, help="output file name", default="") parser.add_argument('-x', type=float, action=utils.RotateAction, help="rotation around X axis in degrees") parser.add_argument('-y', type=float, action=utils.RotateAction, help="rotation around Y axis in degrees") parser.add_argument('-z', type=float, action=utils.RotateAction, help="rotation around X axis in degrees") parser.add_argument('file', nargs=1, type=str, help='name of the file to process') args = parser.parse_args(argv) logging.basicConfig(level=getattr(logging, args.log.upper(), None), format='%(levelname)s: %(message)s') args.file = args.file[0] args.fg = int(args.fg, 16) f_red, f_green, f_blue = utils.num2rgb(args.fg) args.bg = int(args.bg, 16) b_red, b_green, b_blue = utils.num2rgb(args.bg) if 'rotations' not in args: logging.info('no rotations specified') tr = matrix.I() else: tl = [] which = {'x': matrix.rotx, 'y': matrix.roty, 'z': matrix.rotz} for axis, rot in args.rotations: tl.append(which[axis](rot)) tr = matrix.concat(*tl) logging.info('rotation matrix:\n{}'.format(tr)) if not args.outfile: args.outfile = utils.outname(args.file, '.pdf') ofs = "no output filename given, using '{}'" logging.info(ofs.format(args.outfile)) logging.info("reading STL file '{}'".format(args.file)) try: vertices, _ = stl.readstl(args.file) except ValueError as e: logging.error('{}: {}'.format(args.file, e)) sys.exit(1) logging.info('calculating normal vectors') facets = vertices.reshape((-1, 3, 3)) normals = np.array([vecops.normal(a, b, c) for a, b, c in facets]) logging.info('applying transformations to world coordinates') vertices = vecops.xform(tr, vertices) normals = vecops.xform(tr[0:3, 0:3], normals) logging.info('making model-view matrix') minx, maxx, miny, maxy, _, maxz = bbox.makebb(vertices) width = maxx - minx height = maxy - miny dx = -(minx + maxx) / 2 dy = -(miny + maxy) / 2 dz = -maxz m = matrix.trans([dx, dy, dz]) sf = min(args.canvas_size / width, args.canvas_size / height) v = matrix.scale(sf, -sf) v[0, 3], v[1, 3] = args.canvas_size / 2, args.canvas_size / 2 mv = matrix.concat(m, v) logging.info('transforming to view space') vertices = vecops.xform(mv, vertices) facets = vertices.reshape((-1, 3, 3)) # In the ortho projection on the z=0 plane, z+ is _towards_ the viewer logging.info('Determining visible facets') vf = [(f, n, 0.4 * n[2] + 0.5) for f, n in zip(facets, normals) if n[2] > 0] vfs = '{:.2f}% of facets is visible' logging.info(vfs.format(100 * len(vf) / len(facets))) # Next, depth-sort the facets using the largest z-value of the # three vertices. logging.info('depth-sorting visible facets') def fkey(t): (a, b, c), _, _ = t return max(a[2], b[2], c[2]) vf.sort(key=fkey) logging.info('initializing drawing surface') out = cairo.PDFSurface(args.outfile, args.canvas_size, args.canvas_size) ctx = cairo.Context(out) ctx.set_source_rgb(b_red, b_green, b_blue) ctx.rectangle(0, 0, args.canvas_size, args.canvas_size) ctx.fill() ctx.set_line_cap(cairo.LINE_CAP_ROUND) ctx.set_line_join(cairo.LINE_JOIN_ROUND) ctx.set_line_width(0.25) logging.info('drawing the triangles') for (a, b, c), _, i in vf: ctx.new_path() ctx.move_to(a[0], a[1]) ctx.line_to(b[0], b[1]) ctx.line_to(c[0], c[1]) ctx.close_path() ctx.set_source_rgb(f_red * i, f_green * i, f_blue * i) ctx.fill_preserve() ctx.stroke() # Send output. out.show_page() out.finish() logging.info('done')
def main(argv): """ Entry point for stlinfo. Arguments: argv: command line arguments (without program name!) """ parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-t', '--text', action='store_true', help='print text representation of the file') parser.add_argument('-b', '--binary', action='store_true', help='write binary representation of the file') parser.add_argument( '-e', '--encoding', type=str, help="encoding for the name of the STL object (default utf-8)", default='utf-8') parser.add_argument('-v', '--version', action='version', version=__version__) parser.add_argument('--log', default='warning', choices=['debug', 'info', 'warning', 'error'], help="logging level (defaults to 'warning')") parser.add_argument('file', nargs='*', help='one or more file names') args = parser.parse_args(argv) logging.basicConfig(level=getattr(logging, args.log.upper(), None), format='%(levelname)s: %(message)s') if not args.file: parser.print_help() sys.exit(0) for fn in args.file: if not fn.lower().endswith('.stl'): w = 'The file "{}" is probably not an STL file, skipping.' logging.warning(w.format(fn)) continue try: vertices, name = stl.readstl(fn, args.encoding) if args.text or args.binary: facets, points = stl.toindexed(vertices) normals, vectors = stl.normals(facets, points) except ValueError as e: logging.error('{}: {}'.format(fn, e)) continue print("# Information for:", fn) print("# Generated by stlinfo {}".format(__version__)) print("# on {}.".format(time.asctime())) print('# Name: "{}"'.format(name)) print('# Number of facets: {:.0f}'.format(len(vertices) / 3)) minx, maxx, miny, maxy, minz, maxz = bbox.makebb(vertices) print('# Bounding box:') print('# {} ≤ x ≤ {}'.format(minx, maxx)) print('# {} ≤ y ≤ {}'.format(miny, maxy)) print('# {} ≤ z ≤ {}'.format(minz, maxz)) if args.text: print('# Text representation:') print(stl.text(name, facets, points, normals, vectors)) if args.binary: on = utils.outname(fn, '.stl', '_bin') print('# Writing binary represtation to "{}".'.format(on)) with open(on, 'w+b') as of: of.write(stl.binary(name, facets, points, normals, vectors))
def main(argv): """ Entry point for stlinfo. Arguments: argv: command line arguments (without program name!) """ parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( '-t', '--text', action='store_true', help='print text representation of the file' ) parser.add_argument( '-b', '--binary', action='store_true', help='write binary representation of the file' ) parser.add_argument( '-e', '--encoding', type=str, help="encoding for the name of the STL object (default utf-8)", default='utf-8' ) parser.add_argument('-v', '--version', action='version', version=__version__) parser.add_argument( '--log', default='warning', choices=['debug', 'info', 'warning', 'error'], help="logging level (defaults to 'warning')" ) parser.add_argument('file', nargs='*', help='one or more file names') args = parser.parse_args(argv) logging.basicConfig( level=getattr(logging, args.log.upper(), None), format='%(levelname)s: %(message)s' ) if not args.file: parser.print_help() sys.exit(0) for fn in args.file: if not fn.lower().endswith('.stl'): w = 'The file "{}" is probably not an STL file, skipping.' logging.warning(w.format(fn)) continue try: vertices, name = stl.readstl(fn, args.encoding) if args.text or args.binary: facets, points = stl.toindexed(vertices) normals, vectors = stl.normals(facets, points) except ValueError as e: logging.error('{}: {}'.format(fn, e)) continue print("# Information for:", fn) print("# Generated by stlinfo {}".format(__version__)) print("# on {}.".format(time.asctime())) print('# Name: "{}"'.format(name)) print('# Number of facets: {:.0f}'.format(len(vertices) / 3)) minx, maxx, miny, maxy, minz, maxz = bbox.makebb(vertices) print('# Bounding box:') print('# {} ≤ x ≤ {}'.format(minx, maxx)) print('# {} ≤ y ≤ {}'.format(miny, maxy)) print('# {} ≤ z ≤ {}'.format(minz, maxz)) if args.text: print('# Text representation:') print(stl.text(name, facets, points, normals, vectors)) if args.binary: on = utils.outname(fn, '.stl', '_bin') print('# Writing binary represtation to "{}".'.format(on)) with open(on, 'w+b') as of: of.write(stl.binary(name, facets, points, normals, vectors))
def main(args): """Main program. :argv: command line arguments (without program name!) """ msg = utils.Msg() canvas_size = 200 infile, outfile, tr = utils.processargs(args, '.eps', usage) msg.say('Reading STL file') try: vertices, _ = stl.readstl(infile) except ValueError as e: print((infile + ':', e)) sys.exit(1) origbb = bbox.makebb(vertices) msg.say('Calculating normal vectors') facets = vertices.reshape((-1, 3, 3)) normals = np.array([vecops.normal(a, b, c) for a, b, c in facets]) msg.say('Apply transformations to world coordinates') vertices = vecops.xform(tr, vertices) normals = vecops.xform(tr[0:3, 0:3], normals) msg.say('Making model-view matrix') minx, maxx, miny, maxy, _, maxz = bbox.makebb(vertices) width = maxx - minx height = maxy - miny dx = -(minx + maxx)/2 dy = -(miny + maxy)/2 dz = -maxz m = matrix.trans([dx, dy, dz]) sf = min(canvas_size/width, canvas_size/height) v = matrix.scale(sf, sf) v[0, 3], v[1, 3] = canvas_size/2, canvas_size/2 mv = matrix.concat(m, v) msg.say('Transforming to view space') vertices = vecops.xform(mv, vertices) facets = vertices.reshape((-1, 3, 3)) # In the ortho projection on the z=0 plane, z+ is _towards_ the viewer msg.say('Determine visible facets') vf = [(f, n, 0.4*n[2]+0.5) for f, n in zip(facets, normals) if n[2] > 0] msg.say('{:.2f}% of facets is visible'.format(100*len(vf)/len(facets))) # Next, depth-sort the facets using the largest z-value of the # three vertices. msg.say('Depth-sorting visible facets') def fkey(t): (a, b, c), _, _ = t return max(a[2], b[2], c[2]) vf.sort(None, fkey) minx, maxx, miny, maxy, _, maxz = bbox.makebb(vertices) msg.say('Creating PostScript header') s1 = "% The scale factor used is: {:.2f} PostScript points/STL-unit" s2 = "% This becomes a picture of {:.0f}×{:.0f} PostScript points;"\ " {:.0f}×{:.0f} mm." cs = "% {:.2f} ≤ {} ≤ {:.2f}" lines = ["%!PS-Adobe-1.0", "%%BoundingBox: 0 0 {:.0f} {:.0f}".format(maxx, maxy), "% Generated by stl2ps {}".format(__version__), "% on {}.".format(time.asctime()), "% Bounding box (STL units)", cs.format(origbb[0], 'x', origbb[1]), cs.format(origbb[2], 'y', origbb[3]), cs.format(origbb[4], 'z', origbb[5]), s1.format(sf), s2.format(maxx, maxy, maxx/72*25.4, maxy/72*25.4), "% {} of {} facets are visible.".format(len(vf), len(facets)) ] # PostScript settings and macros. lines += ["% Settings", ".5 setlinewidth", ".5 setlinewidth", "% Defining drawing commands", "/g {setgray} def", "/f {moveto} def", "/s {lineto} def", "/t {lineto closepath gsave fill grestore stroke} def", "% Start drawing"] s3 = "{:4.2f} g {:.3f} {:.3f} f {:.3f} {:.3f} s {:.3f} {:.3f} t" msg.say('Rendering triangles') lines += [s3.format(i, a[0], a[1], b[0], b[1], c[0], c[1]) for (a, b, c), z, i in vf] lines += ["showpage", '%%EOF'] outs = '\n'.join(lines) try: with open(outfile, "w+") as outf: msg.say('Writing output file "{}"'.format(outfile)) outf.write(outs) msg.say('Done') except: msg.say('Error: Cannot write output file "{}"'.format())
def main(argv): """ Entry point of stl2pdf. Arguments: argv: Command line arguments (without program name!) """ parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( '--log', default='warning', choices=['debug', 'info', 'warning', 'error'], help="logging level (defaults to 'warning')" ) parser.add_argument( '-c', '--canvas', dest='canvas_size', type=int, help="canvas size, defaults to 200 PostScript points", default=200 ) parser.add_argument( '-f', '--foreground', dest='fg', type=str, help="foreground color in 6-digit hexdecimal RGB (default E6E6E6)", default='E6E6E6' ) parser.add_argument( '-b', '--background', dest='bg', type=str, help="background color in 6-digit hexdecimal RGB (default FFFFFF)", default='FFFFFF' ) parser.add_argument( '-e', '--encoding', type=str, help="encoding for the name of the STL object (default utf-8)", default='utf-8' ) parser.add_argument( '-o', '--output', dest='outfile', type=str, help="output file name", default="" ) parser.add_argument( '-x', type=float, action=utils.RotateAction, help="rotation around X axis in degrees" ) parser.add_argument( '-y', type=float, action=utils.RotateAction, help="rotation around Y axis in degrees" ) parser.add_argument( '-z', type=float, action=utils.RotateAction, help="rotation around X axis in degrees" ) parser.add_argument('-v', '--version', action='version', version=__version__) parser.add_argument('file', nargs=1, type=str, help='name of the file to process') args = parser.parse_args(argv) logging.basicConfig( level=getattr(logging, args.log.upper(), None), format='%(levelname)s: %(message)s' ) args.file = args.file[0] args.fg = int(args.fg, 16) f_red, f_green, f_blue = utils.num2rgb(args.fg) args.bg = int(args.bg, 16) b_red, b_green, b_blue = utils.num2rgb(args.bg) if 'rotations' not in args: logging.info('no rotations specified') tr = matrix.I() else: tl = [] which = {'x': matrix.rotx, 'y': matrix.roty, 'z': matrix.rotz} for axis, rot in args.rotations: tl.append(which[axis](rot)) tr = matrix.concat(*tl) logging.info('rotation matrix:\n{}'.format(tr)) if not args.outfile: args.outfile = utils.outname(args.file, '.pdf') ofs = "no output filename given, using '{}'" logging.info(ofs.format(args.outfile)) logging.info("reading STL file '{}'".format(args.file)) try: vertices, _ = stl.readstl(args.file, args.encoding) except ValueError as e: logging.error('{}: {}'.format(args.file, e)) sys.exit(1) logging.info('calculating normal vectors') facets = vertices.reshape((-1, 3, 3)) normals = np.array([vecops.normal(a, b, c) for a, b, c in facets]) logging.info('applying transformations to world coordinates') vertices = vecops.xform(tr, vertices) normals = vecops.xform(tr[0:3, 0:3], normals) logging.info('making model-view matrix') minx, maxx, miny, maxy, _, maxz = bbox.makebb(vertices) width = maxx - minx height = maxy - miny dx = -(minx + maxx) / 2 dy = -(miny + maxy) / 2 dz = -maxz m = matrix.trans([dx, dy, dz]) sf = min(args.canvas_size / width, args.canvas_size / height) v = matrix.scale(sf, -sf) v[0, 3], v[1, 3] = args.canvas_size / 2, args.canvas_size / 2 mv = matrix.concat(m, v) logging.info('transforming to view space') vertices = vecops.xform(mv, vertices) facets = vertices.reshape((-1, 3, 3)) # In the ortho projection on the z=0 plane, z+ is _towards_ the viewer logging.info('Determining visible facets') vf = [(f, n, 0.4 * n[2] + 0.5) for f, n in zip(facets, normals) if n[2] > 0] vfs = '{:.2f}% of facets is visible' logging.info(vfs.format(100 * len(vf) / len(facets))) # Next, depth-sort the facets using the largest z-value of the # three vertices. logging.info('depth-sorting visible facets') def fkey(t): (a, b, c), _, _ = t return max(a[2], b[2], c[2]) vf.sort(key=fkey) logging.info('initializing drawing surface') out = cairo.PDFSurface(args.outfile, args.canvas_size, args.canvas_size) ctx = cairo.Context(out) ctx.set_source_rgb(b_red, b_green, b_blue) ctx.rectangle(0, 0, args.canvas_size, args.canvas_size) ctx.fill() ctx.set_line_cap(cairo.LINE_CAP_ROUND) ctx.set_line_join(cairo.LINE_JOIN_ROUND) ctx.set_line_width(0.25) logging.info('drawing the triangles') for (a, b, c), _, i in vf: ctx.new_path() ctx.move_to(a[0], a[1]) ctx.line_to(b[0], b[1]) ctx.line_to(c[0], c[1]) ctx.close_path() ctx.set_source_rgb(f_red * i, f_green * i, f_blue * i) ctx.fill_preserve() ctx.stroke() # Send output. out.show_page() out.finish() logging.info('done')
def main(argv): """ Entry point of stl2ps. Arguments: argv: Command line arguments (without program name!) """ parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--log', default='warning', choices=['debug', 'info', 'warning', 'error'], help="logging level (defaults to 'warning')") parser.add_argument('-c', '--canvas', dest='canvas_size', type=int, help="canvas size, defaults to 200 PostScript points", default=200) parser.add_argument( '-f', '--foreground', dest='fg', type=str, help="foreground color in 6-digit hexdecimal RGB (default E6E6E6)", default='E6E6E6') parser.add_argument( '-b', '--background', dest='bg', type=str, help= "background color in 6-digit hexdecimal RGB (default white FFFFFF)", default='FFFFFF') parser.add_argument('-o', '--output', dest='outfile', type=str, help="output file name", default="") parser.add_argument('-x', type=float, action=utils.RotateAction, help="rotation around X axis in degrees") parser.add_argument('-y', type=float, action=utils.RotateAction, help="rotation around Y axis in degrees") parser.add_argument('-z', type=float, action=utils.RotateAction, help="rotation around Z axis in degrees") parser.add_argument('file', nargs=1, type=str, help='name of the file to process') args = parser.parse_args(argv) logging.basicConfig(level=getattr(logging, args.log.upper(), None), format='%(levelname)s: %(message)s') args.file = args.file[0] args.fg = int(args.fg, 16) f_red, f_green, f_blue = utils.num2rgb(args.fg) args.bg = int(args.bg, 16) b_red, b_green, b_blue = utils.num2rgb(args.bg) if 'rotations' not in args: logging.info('no rotations specified') tr = matrix.I() else: tl = [] which = {'x': matrix.rotx, 'y': matrix.roty, 'z': matrix.rotz} for axis, rot in args.rotations: tl.append(which[axis](rot)) tr = matrix.concat(*tl) logging.info('rotation matrix:\n{}'.format(tr)) if not args.outfile: args.outfile = utils.outname(args.file, '.eps') ofs = "no output filename given, using '{}'" logging.info(ofs.format(args.outfile)) logging.info("reading STL file '{}'".format(args.file)) try: vertices, _ = stl.readstl(args.file) except ValueError as e: logging.error('{}: {}'.format(args.file, e)) sys.exit(1) origbb = bbox.makebb(vertices) logging.info('calculating normal vectors') facets = vertices.reshape((-1, 3, 3)) normals = np.array([vecops.normal(a, b, c) for a, b, c in facets]) logging.info('applying transformations to world coordinates') vertices = vecops.xform(tr, vertices) normals = vecops.xform(tr[0:3, 0:3], normals) logging.info('making model-view matrix') minx, maxx, miny, maxy, _, maxz = bbox.makebb(vertices) width = maxx - minx height = maxy - miny dx = -(minx + maxx) / 2 dy = -(miny + maxy) / 2 dz = -maxz m = matrix.trans([dx, dy, dz]) sf = min(args.canvas_size / width, args.canvas_size / height) v = matrix.scale(sf, sf) v[0, 3], v[1, 3] = args.canvas_size / 2, args.canvas_size / 2 mv = matrix.concat(m, v) logging.info('transforming to view space') vertices = vecops.xform(mv, vertices) facets = vertices.reshape((-1, 3, 3)) # In the ortho projection on the z=0 plane, z+ is _towards_ the viewer logging.info('determine visible facets') vf = [(f, n, 0.4 * n[2] + 0.5) for f, n in zip(facets, normals) if n[2] > 0] fvs = '{:.2f}% of facets is visible' logging.info(fvs.format(100 * len(vf) / len(facets))) # Next, depth-sort the facets using the largest z-value of the # three vertices. logging.info('depth-sorting visible facets') def fkey(t): (a, b, c), _, _ = t return max(a[2], b[2], c[2]) vf.sort(key=fkey) minx, maxx, miny, maxy, _, maxz = bbox.makebb(vertices) logging.info('creating PostScript header') s1 = "% The scale factor used is: {:.2f} PostScript points/STL-unit" s2 = "% This becomes a picture of {:.0f}×{:.0f} PostScript points;"\ " {:.0f}×{:.0f} mm." cs = "% {:.2f} ≤ {} ≤ {:.2f}" lines = [ "%!PS-Adobe-3.0 EPSF-3.0", "%%BoundingBox: 0 0 {:.0f} {:.0f}".format(maxx, maxy), "%%EndComments", "% Generated by stl2ps {}".format(__version__), "% on {}.".format(time.asctime()), "% Bounding box (STL units)", cs.format(origbb[0], 'x', origbb[1]), cs.format(origbb[2], 'y', origbb[3]), cs.format(origbb[4], 'z', origbb[5]), s1.format(sf), s2.format(maxx, maxy, maxx / 72 * 25.4, maxy / 72 * 25.4), "% {} of {} facets are visible.".format(len(vf), len(facets)) ] # PostScript settings and macros. lines += [ "% Settings", ".5 setlinewidth", "1 setlinejoin", "% Defining drawing commands", "/c {setrgbcolor} def", "/f {moveto} def", "/s {lineto} def", "/t {lineto closepath gsave fill grestore stroke} def", "% Start drawing" ] # Draw background. if b_red < 1 or b_green < 1 or b_blue < 1: lines += [ '% Fill background', '{:4.2f} {:4.2f} {:4.2f} c'.format(b_red, b_green, b_blue), '0 0 f', '{:.0f} 0 s'.format(maxx), '{:.0f} {:.0f} s'.format(maxx, maxy), '0 {:.0f} t'.format(maxy) ] # Draw triangles. lines += ['% Rendering triangles'] s3 = "{:4.2f} {:4.2f} {:4.2f} c {:.3f} {:.3f} f {:.3f} {:.3f} s {:.3f} {:.3f} t" logging.info('rendering triangles') lines += [ s3.format(f_red * i, f_green * i, f_blue * i, a[0], a[1], b[0], b[1], c[0], c[1]) for (a, b, c), z, i in vf ] lines += ["showpage", '%%EOF'] outs = '\n'.join(lines) try: with open(args.outfile, "w+") as outf: logging.info('writing output file "{}"'.format(args.outfile)) outf.write(outs) logging.info('done') except Exception: logging.error('cannot write output file "{}"'.format())
def main(argv): """ Entry point of stl2ps. Arguments: argv: Command line arguments (without program name!) """ parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( '--log', default='warning', choices=['debug', 'info', 'warning', 'error'], help="logging level (defaults to 'warning')" ) parser.add_argument( '-c', '--canvas', dest='canvas_size', type=int, help="canvas size, defaults to 200 PostScript points", default=200 ) parser.add_argument( '-f', '--foreground', dest='fg', type=str, help="foreground color in 6-digit hexdecimal RGB (default E6E6E6)", default='E6E6E6' ) parser.add_argument( '-b', '--background', dest='bg', type=str, help="background color in 6-digit hexdecimal RGB (default white FFFFFF)", default='FFFFFF' ) parser.add_argument( '-e', '--encoding', type=str, help="encoding for the name of the STL object (default utf-8)", default='utf-8' ) parser.add_argument( '-o', '--output', dest='outfile', type=str, help="output file name", default="" ) parser.add_argument( '-x', type=float, action=utils.RotateAction, help="rotation around X axis in degrees" ) parser.add_argument( '-y', type=float, action=utils.RotateAction, help="rotation around Y axis in degrees" ) parser.add_argument( '-z', type=float, action=utils.RotateAction, help="rotation around Z axis in degrees" ) parser.add_argument('-v', '--version', action='version', version=__version__) parser.add_argument('file', nargs=1, type=str, help='name of the file to process') args = parser.parse_args(argv) logging.basicConfig( level=getattr(logging, args.log.upper(), None), format='%(levelname)s: %(message)s' ) args.file = args.file[0] args.fg = int(args.fg, 16) f_red, f_green, f_blue = utils.num2rgb(args.fg) args.bg = int(args.bg, 16) b_red, b_green, b_blue = utils.num2rgb(args.bg) if 'rotations' not in args: logging.info('no rotations specified') tr = matrix.I() else: tl = [] which = {'x': matrix.rotx, 'y': matrix.roty, 'z': matrix.rotz} for axis, rot in args.rotations: tl.append(which[axis](rot)) tr = matrix.concat(*tl) logging.info('rotation matrix:\n{}'.format(tr)) if not args.outfile: args.outfile = utils.outname(args.file, '.eps') ofs = "no output filename given, using '{}'" logging.info(ofs.format(args.outfile)) logging.info("reading STL file '{}'".format(args.file)) try: vertices, _ = stl.readstl(args.file, args.encoding) except ValueError as e: logging.error('{}: {}'.format(args.file, e)) sys.exit(1) origbb = bbox.makebb(vertices) logging.info('calculating normal vectors') facets = vertices.reshape((-1, 3, 3)) normals = np.array([vecops.normal(a, b, c) for a, b, c in facets]) logging.info('applying transformations to world coordinates') vertices = vecops.xform(tr, vertices) normals = vecops.xform(tr[0:3, 0:3], normals) logging.info('making model-view matrix') minx, maxx, miny, maxy, _, maxz = bbox.makebb(vertices) width = maxx - minx height = maxy - miny dx = -(minx + maxx) / 2 dy = -(miny + maxy) / 2 dz = -maxz m = matrix.trans([dx, dy, dz]) sf = min(args.canvas_size / width, args.canvas_size / height) v = matrix.scale(sf, sf) v[0, 3], v[1, 3] = args.canvas_size / 2, args.canvas_size / 2 mv = matrix.concat(m, v) logging.info('transforming to view space') vertices = vecops.xform(mv, vertices) facets = vertices.reshape((-1, 3, 3)) # In the ortho projection on the z=0 plane, z+ is _towards_ the viewer logging.info('determine visible facets') vf = [(f, n, 0.4 * n[2] + 0.5) for f, n in zip(facets, normals) if n[2] > 0] fvs = '{:.2f}% of facets is visible' logging.info(fvs.format(100 * len(vf) / len(facets))) # Next, depth-sort the facets using the largest z-value of the # three vertices. logging.info('depth-sorting visible facets') def fkey(t): (a, b, c), _, _ = t return max(a[2], b[2], c[2]) vf.sort(key=fkey) minx, maxx, miny, maxy, _, maxz = bbox.makebb(vertices) logging.info('creating PostScript header') s1 = "% The scale factor used is: {:.2f} PostScript points/STL-unit" s2 = "% This becomes a picture of {:.0f}×{:.0f} PostScript points;"\ " {:.0f}×{:.0f} mm." cs = "% {:.2f} ≤ {} ≤ {:.2f}" lines = [ "%!PS-Adobe-3.0 EPSF-3.0", "%%BoundingBox: 0 0 {:.0f} {:.0f}".format(maxx, maxy), "%%EndComments", "% Generated by stl2ps {}".format(__version__), "% on {}.".format(time.asctime()), "% Bounding box (STL units)", cs.format(origbb[0], 'x', origbb[1]), cs.format(origbb[2], 'y', origbb[3]), cs.format(origbb[4], 'z', origbb[5]), s1.format(sf), s2.format(maxx, maxy, maxx / 72 * 25.4, maxy / 72 * 25.4), "% {} of {} facets are visible.".format(len(vf), len(facets)) ] # PostScript settings and macros. lines += [ "% Settings", ".5 setlinewidth", "1 setlinejoin", "% Defining drawing commands", "/c {setrgbcolor} def", "/f {moveto} def", "/s {lineto} def", "/t {lineto closepath gsave fill grestore stroke} def", "% Start drawing" ] # Draw background. if b_red < 1 or b_green < 1 or b_blue < 1: lines += [ '% Fill background', '{:4.2f} {:4.2f} {:4.2f} c'.format(b_red, b_green, b_blue), '0 0 f', '{:.0f} 0 s'.format(maxx), '{:.0f} {:.0f} s'.format(maxx, maxy), '0 {:.0f} t'.format(maxy) ] # Draw triangles. lines += ['% Rendering triangles'] s3 = "{:4.2f} {:4.2f} {:4.2f} c {:.3f} {:.3f} f {:.3f} {:.3f} s {:.3f} {:.3f} t" logging.info('rendering triangles') lines += [ s3.format(f_red * i, f_green * i, f_blue * i, a[0], a[1], b[0], b[1], c[0], c[1]) for (a, b, c), z, i in vf ] lines += ["showpage", '%%EOF'] outs = '\n'.join(lines) try: with open(args.outfile, "w+") as outf: logging.info('writing output file "{}"'.format(args.outfile)) outf.write(outs) logging.info('done') except Exception: logging.error('cannot write output file "{}"'.format())