Exemplo n.º 1
0
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)
Exemplo n.º 2
0
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)
Exemplo n.º 3
0
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)
Exemplo n.º 4
0
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')
Exemplo n.º 5
0
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')
Exemplo n.º 6
0
)
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)
Exemplo n.º 7
0
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())
Exemplo n.º 8
0
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')
Exemplo n.º 9
0
    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)
Exemplo n.º 10
0
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())
Exemplo n.º 11
0
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())