Пример #1
0
def test_normals():
    vertices, name = stl.readstl('test/data/cube-txt.stl')
    facets, pnts = stl.toindexed(vertices)
    ni, nv = stl.normals(facets, pnts)
    assert len(ni) == 12  # 6 faces, 2 facets/face.
    p = np.arange(6)
    assert np.all(ni == np.vstack((p, p)).T.reshape((1, -1)))
    assert nv.shape == (6, 3)
Пример #2
0
def test_normals():
    vertices, name = stl.readstl('data/cube-txt.stl')
    facets, pnts = stl.toindexed(vertices)
    ni, nv = stl.normals(facets, pnts)
    assert len(ni) == 12  # 6 faces, 2 facets/face.
    p = np.arange(6)
    assert np.all(ni == np.vstack((p, p)).T.reshape((1, -1)))
    assert nv.shape == (6, 3)
Пример #3
0
def test_text():
    vertices, name = stl.readstl('data/cube-txt.stl')
    facets, pnts = stl.toindexed(vertices)
    ni, nv = stl.normals(facets, pnts)
    res = stl.text('cube_txt', facets, pnts, ni, nv)
    res = [ln.strip() for ln in res.splitlines()]
    with open('data/cube-txt.stl') as inp:
        orig = [ln.strip() for ln in inp.readlines()]
    for a, b in zip(orig, res):
        assert a == b
Пример #4
0
def test_read_txt():
    vertices, name = stl.readstl('test/data/cube-txt.stl')
    assert name == 'cube_txt'
    assert vertices.shape == (36, 3)
    _, pnts = stl.toindexed(vertices)
    assert pnts.shape == (8, 3)
    assert np.all(pnts[0] == np.array([0, 0, 1]))
    assert np.all(pnts[1] == np.array([1, 0, 1]))
    assert np.all(pnts[2] == np.array([0, 1, 1]))
    assert np.all(pnts[3] == np.array([1, 1, 1]))
    assert np.all(pnts[4] == np.array([1, 0, 0]))
    assert np.all(pnts[5] == np.array([0, 0, 0]))
    assert np.all(pnts[6] == np.array([1, 1, 0]))
    assert np.all(pnts[7] == np.array([0, 1, 0]))
Пример #5
0
def test_read_txt():
    vertices, name = stl.readstl('data/cube-txt.stl')
    assert name == 'cube_txt'
    assert vertices.shape == (36, 3)
    _, pnts = stl.toindexed(vertices)
    assert pnts.shape == (8, 3)
    assert np.all(pnts[0] == np.array([0, 0, 1]))
    assert np.all(pnts[1] == np.array([1, 0, 1]))
    assert np.all(pnts[2] == np.array([0, 1, 1]))
    assert np.all(pnts[3] == np.array([1, 1, 1]))
    assert np.all(pnts[4] == np.array([1, 0, 0]))
    assert np.all(pnts[5] == np.array([0, 0, 0]))
    assert np.all(pnts[6] == np.array([1, 1, 0]))
    assert np.all(pnts[7] == np.array([0, 1, 0]))
Пример #6
0
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))
Пример #7
0
def main(argv):
    """Main program.

    :argv: command line arguments (without program name!)
    """
    msg = utils.Msg()
    parser = argparse.ArgumentParser(description=__doc__)
    argtxt = 'generate a mesh2 object (slow on big files)'
    parser.add_argument('-2,' '--mesh2', action='store_true',
                        help=argtxt, dest='mesh2')
    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:
        msg.say('Starting file "{}"'.format(fn))
        if not fn.lower().endswith('.stl'):
            w = 'The file "{}" is probably not an STL file, skipping.'
            msg.say(w.format(fn))
            continue
        try:
            msg.say('Reading facets')
            vertices, name = stl.readstl(fn)
            outfn = utils.outname(fn, '.inc')
        except Exception as e:  # pylint: disable=W0703
            msg.say('Error;', e)
            continue
        outs = "// Generated by stl2pov {}\n".format(__version__)
        outs = "// on {}.\n".format(time.asctime())
        outs += "// Source file name: '{}'\n".format(fn)
        if args.mesh2:
            msg.say('Generating mesh2 data')
            outs += mesh2(name, vertices)
        else:
            msg.say('Generating mesh data')
            outs += mesh1(name, vertices)
        try:
            with open(outfn, 'w+') as of:
                msg.say('Writing output file "{}"'.format(outfn))
                of.write(outs)
        except:
            msg.say('Cannot write output file "{}"'.format(outfn))
            continue
        msg.say('Done with file "{}"'.format(fn))
Пример #8
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())
Пример #9
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')
Пример #10
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')
Пример #11
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')
Пример #12
0
def main(argv):
    """
    Entry point for stl2pov.

    Arguments:
        argv: List of command line arguments (without program name!)
    """
    parser = argparse.ArgumentParser(description=__doc__)
    argtxt = 'generate a mesh2 object (slow on big files)'
    parser.add_argument('-2',
                        '--mesh2',
                        action='store_true',
                        help=argtxt,
                        dest='mesh2')
    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:
        logging.info('Starting file "{}"'.format(fn))
        if not fn.lower().endswith('.stl'):
            w = 'the file "{}" is probably not an STL file, skipping'
            logging.warning(w.format(fn))
            continue
        try:
            logging.info('Reading facets')
            vertices, name = stl.readstl(fn, args.encoding)
            outfn = utils.outname(fn, '.inc')
        except Exception as e:  # pylint: disable=W0703
            logging.error('{}: {}'.format(fn, e))
            continue
        if not valid_name(name):
            ws = 'the object name "{}" is not a valid POV-ray identifier.'
            logging.warning(ws.format(name))
            name = generate_name(name, fn)
            logging.warning('using "m_{}" instead.'.format(name))
        outs = "// Generated by stl2pov {}\n".format(__version__)
        outs += "// on {}.\n".format(time.asctime())
        outs += "// Source file name: '{}'\n".format(fn)
        if args.mesh2:
            logging.info('generating mesh2 data.')
            outs += mesh2(name, vertices)
        else:
            logging.info('generating mesh data.')
            outs += mesh1(name, vertices)
        try:
            with open(outfn, 'w+') as of:
                logging.info('writing output file "{}".'.format(outfn))
                of.write(outs)
        except Exception:
            logging.warning('cannot write output file "{}".'.format(outfn))
            continue
        logging.info('done with file "{}".'.format(fn))
Пример #13
0
def test_read_bin():
    vertices, name = stl.readstl('test/data/cube-bin.stl')
    assert name == 'cube_bin'
    assert vertices.shape == (36, 3)
    _, pnts = stl.toindexed(vertices)
    assert pnts.shape == (8, 3)
Пример #14
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())
Пример #15
0
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))
Пример #16
0
def main(argv):
    """
    Entry point for stl2pov.

    Arguments:
        argv: List of command line arguments (without program name!)
    """
    parser = argparse.ArgumentParser(description=__doc__)
    argtxt = 'generate a mesh2 object (slow on big files)'
    parser.add_argument('-2,' '--mesh2', action='store_true', help=argtxt, dest='mesh2')
    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:
        logging.info('Starting file "{}"'.format(fn))
        if not fn.lower().endswith('.stl'):
            w = 'the file "{}" is probably not an STL file, skipping'
            logging.warning(w.format(fn))
            continue
        try:
            logging.info('Reading facets')
            vertices, name = stl.readstl(fn, args.encoding)
            outfn = utils.outname(fn, '.inc')
        except Exception as e:  # pylint: disable=W0703
            logging.error('{}: {}'.format(fn, e))
            continue
        if not valid_name(name):
            ws = 'the object name "{}" is not a valid POV-ray identifier.'
            logging.warning(ws.format(name))
            name = generate_name(name, fn)
            logging.warning('using "m_{}" instead.'.format(name))
        outs = "// Generated by stl2pov {}\n".format(__version__)
        outs += "// on {}.\n".format(time.asctime())
        outs += "// Source file name: '{}'\n".format(fn)
        if args.mesh2:
            logging.info('generating mesh2 data.')
            outs += mesh2(name, vertices)
        else:
            logging.info('generating mesh data.')
            outs += mesh1(name, vertices)
        try:
            with open(outfn, 'w+') as of:
                logging.info('writing output file "{}".'.format(outfn))
                of.write(outs)
        except Exception:
            logging.warning('cannot write output file "{}".'.format(outfn))
            continue
        logging.info('done with file "{}".'.format(fn))
Пример #17
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())
Пример #18
0
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))
Пример #19
0
def test_read_bin():
    vertices, name = stl.readstl('data/cube-bin.stl')
    assert name == 'cube_bin'
    assert vertices.shape == (36, 3)
    _, pnts = stl.toindexed(vertices)
    assert pnts.shape == (8, 3)