def test_xform_roty(): m = ma.roty(120) pnts = np.arange(15).reshape((-1, 3)) r = vo.xform(m, pnts) s = vo.xform(m, r) t = vo.xform(m, s) assert np.all(t - pnts < 0.001)
def test_vo_xform_scale(): m = np.identity(4) * 0.5 m[-1, -1] = 1.0 pnts = np.arange(15).reshape((-1, 3)) r = vo.xform(m, pnts) assert np.all(r * 2 == pnts) p = ma.scale(0.5, 0.5, 0.5) r = vo.xform(p, pnts) assert np.all(r * 2 == pnts)
def test_vo_xform_trans(): m = np.identity(4) m.T[-1] = np.array([2, 3, 4, 1.0]) pnts = np.arange(15).reshape((-1, 3)) r = vo.xform(m, pnts) q = np.array([2, 3, 4] * 5).reshape((-1, 3)) assert np.all(r - q == pnts) m = ma.trans(np.array([2, 3, 4])) r = vo.xform(m, pnts) assert np.all(r - q == pnts)
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')
) logging.info('calculating normals') ni, normals = stl.normals(facets, vertices) logging.info('vertices:\n{}'.format(vertices)) logging.info('facets:\n{}'.format(facets)) logging.info('normals:\n{}'.format(normals)) logging.info('normal indices:\n{}'.format(ni)) logging.info('creating text STL data') txtdata = stl.text('cube-txt', facets, vertices, ni, normals) try: with open('cube-txt.stl', 'w') as stlf: stlf.write(txtdata) logging.info('wrote text STL file cube-txt.stl') except IOError as e: logging.error('unable to write cube-txt.stl; {}'.format(e)) logging.info('creating binary STL data') bindata = stl.binary('cube-bin', facets, vertices, ni, normals) try: with open('cube-bin.stl', 'wb') as stlf: stlf.write(bindata) logging.info('wrote binary STL file cube-bin.stl') except IOError as e: logging.error('unable to write cube-txt.bin; {}'.format(e)) tr = mx.concat(mx.rotx(30), mx.roty(20)) nv = vo.xform(tr, vertices) nn = vo.xform(tr, normals)
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')
np.uint16) logging.info('calculating normals') ni, normals = stl.normals(facets, vertices) logging.info('vertices:\n{}'.format(vertices)) logging.info('facets:\n{}'.format(facets)) logging.info('normals:\n{}'.format(normals)) logging.info('normal indices:\n{}'.format(ni)) logging.info('creating text STL data') txtdata = stl.text('cube-txt', facets, vertices, ni, normals) try: with open('cube-txt.stl', 'w') as stlf: stlf.write(txtdata) logging.info('wrote text STL file cube-txt.stl') except IOError as e: logging.error('unable to write cube-txt.stl; {}'.format(e)) logging.info('creating binary STL data') bindata = stl.binary('cube-bin', facets, vertices, ni, normals) try: with open('cube-bin.stl', 'wb') as stlf: stlf.write(bindata) logging.info('wrote binary STL file cube-bin.stl') except IOError as e: logging.error('unable to write cube-txt.bin; {}'.format(e)) tr = mx.concat(mx.rotx(30), mx.roty(20)) nv = vo.xform(tr, vertices) nn = vo.xform(tr, normals)
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())