Date:2015-01-19 01:03:49 (9 years 2 months ago)
Author:Werner Almesberger
Commit:79a821052f85523ada5bc622e70cfef5027af857
Message:sfc/slicer.py: FreeCAD-based slicer, first commit

Files: sfc/slicer.py (1 diff)

Change Details

sfc/slicer.py
1#!/usr/bin/python
2#
3# slicer.py - FreeCAD-based STL to Gnuplot slicer
4#
5# Written 2015 by Werner Almesberger
6# Copyright 2015 by Werner Almesberger
7#
8# This program//library is free software; you can redistribute it and/or
9# modify it under the terms of the GNU Lesser General Public
10# License as published by the Free Software Foundation; either
11# version 2.1 of the License, or (at your option) any later version.
12#
13
14
15import sys
16
17sys.path.append("/usr/lib/freecad/lib")
18
19
20import FreeCAD, Part, Mesh
21import os, getopt
22from FreeCAD import Base
23from math import hypot
24
25
26epsilon = 0.0001 # acceptable math rounding error and slicing offset
27mech_eps = 0.01 # acceptable mechanical deviation
28margin = None # draw a workpiece at the specified xy distance around
29            # the model (default: none)
30
31
32def dist(a, b):
33    pa = a.Point
34    pb = b.Point
35    return hypot(pa[0] - pb[0], pa[1] - pb[1])
36
37
38def print_vec(v):
39    p = v.Point
40    print p[0], " ", p[1], " ", p[2] - epsilon
41
42
43def usage():
44    print >>sys.stderr, "usage:", sys.argv[0], "file.stl"
45    sys.exit(1)
46
47
48#
49# FreeCAD prints progress information to stdout instead of stderr.
50# We don't want that ...
51#
52
53stdout = os.dup(1)
54os.dup2(2, 1)
55sys.stdout = os.fdopen(stdout, "w")
56
57opts, args = getopt.getopt(sys.argv[1:], "b:")
58for opt, arg in opts:
59    if opt == "-b":
60        margin = float(arg)
61    else:
62        assert False
63
64if len(args) != 1:
65    usage()
66
67#
68# Read the STL mesh
69#
70
71mesh = Mesh.Mesh(args[0])
72
73#
74# The 2.5D model consists of "plateaus" (facets parallel to the xy plane) and
75# "walls" (facets parallel to the z axis). Anything else is an error and will
76# produce incorrect results.
77#
78# We use plateau facets only for their z position, as indication where to mill
79# a plateau. Wall facets are kept for later use.
80#
81
82vert = Mesh.Mesh()
83z_raw = {}
84max_nz = 0
85inclined = 0
86for facet in mesh.Facets:
87    if abs(facet.Normal.z) >= 1 - epsilon:
88        z_raw[facet.Points[0][2]] = 1
89    else:
90        nz = abs(facet.Normal.z)
91        if nz > epsilon:
92            inclined += 1
93            max_nz = max(max_nz, nz)
94        v1 = Base.Vector(facet.Points[0])
95        v2 = Base.Vector(facet.Points[1])
96        v3 = Base.Vector(facet.Points[1])
97        vert.addFacet(v1, v2, v3)
98
99if inclined:
100    print >>sys.stderr # FreeCAD progress reporting messes up newlines
101    print >>sys.stderr, inclined, "inclined facets, maximum normal", max_nz
102
103#
104# @@@ This is perhaps a bit too paranoid
105#
106# I wrote the Z noise filtering because I had mis-read perfectly good
107# distinct coordinates as being essentially the same value but with
108# rounding errors.
109#
110
111z_levels = []
112last = None
113for z in sorted(z_raw.keys(), reverse = True):
114    if last is None or last - z > epsilon:
115        z_levels.append(z)
116        last = z
117
118#
119# Convert the walls to a FreeCAD shape
120#
121
122shape = Part.Shape()
123shape.makeShapeFromMesh(mesh.Topology, mech_eps)
124bb = shape.BoundBox
125
126#
127# Iterate over all plateaus and determine how they intersect with the walls.
128# For this, we add a small offset to the z position so that we intersect above
129# the plateau.
130#
131
132for z in z_levels:
133    print "# level z = ", z
134
135    if margin is not None:
136        print bb.XMin - margin, " ", bb.YMin - margin, " ", z
137        print bb.XMax + margin, " ", bb.YMin - margin, " ", z
138        print bb.XMax + margin, " ", bb.YMax + margin, " ", z
139        print bb.XMin - margin, " ", bb.YMax + margin, " ", z
140        print bb.XMin - margin, " ", bb.YMin - margin, " ", z
141        print
142
143    for wire in shape.slice(Base.Vector(0, 0, 1), z + epsilon):
144        print "# wire = ", wire
145        first = None
146        last = None
147        for e in wire.Edges:
148            v = e.Vertexes[0]
149            if first is None:
150                first = v
151            if last is None or dist(v, last) >= mech_eps:
152                print_vec(v)
153            last = v
154        if first is not None:
155            print_vec(first)
156            print
157    print
158
159#
160# That's all, folks !
161#

Archive Download the corresponding diff file

Branches:
master



interactive