mirror of
git://projects.qi-hardware.com/cae-tools.git
synced 2024-12-22 23:26:27 +02:00
sfc/slicer.py: FreeCAD-based slicer, first commit
This commit is contained in:
parent
ae8d393859
commit
79a821052f
161
sfc/slicer.py
Executable file
161
sfc/slicer.py
Executable file
@ -0,0 +1,161 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# slicer.py - FreeCAD-based STL to Gnuplot slicer
|
||||
#
|
||||
# Written 2015 by Werner Almesberger
|
||||
# Copyright 2015 by Werner Almesberger
|
||||
#
|
||||
# This program//library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.append("/usr/lib/freecad/lib")
|
||||
|
||||
|
||||
import FreeCAD, Part, Mesh
|
||||
import os, getopt
|
||||
from FreeCAD import Base
|
||||
from math import hypot
|
||||
|
||||
|
||||
epsilon = 0.0001 # acceptable math rounding error and slicing offset
|
||||
mech_eps = 0.01 # acceptable mechanical deviation
|
||||
margin = None # draw a workpiece at the specified xy distance around
|
||||
# the model (default: none)
|
||||
|
||||
|
||||
def dist(a, b):
|
||||
pa = a.Point
|
||||
pb = b.Point
|
||||
return hypot(pa[0] - pb[0], pa[1] - pb[1])
|
||||
|
||||
|
||||
def print_vec(v):
|
||||
p = v.Point
|
||||
print p[0], " ", p[1], " ", p[2] - epsilon
|
||||
|
||||
|
||||
def usage():
|
||||
print >>sys.stderr, "usage:", sys.argv[0], "file.stl"
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
#
|
||||
# FreeCAD prints progress information to stdout instead of stderr.
|
||||
# We don't want that ...
|
||||
#
|
||||
|
||||
stdout = os.dup(1)
|
||||
os.dup2(2, 1)
|
||||
sys.stdout = os.fdopen(stdout, "w")
|
||||
|
||||
opts, args = getopt.getopt(sys.argv[1:], "b:")
|
||||
for opt, arg in opts:
|
||||
if opt == "-b":
|
||||
margin = float(arg)
|
||||
else:
|
||||
assert False
|
||||
|
||||
if len(args) != 1:
|
||||
usage()
|
||||
|
||||
#
|
||||
# Read the STL mesh
|
||||
#
|
||||
|
||||
mesh = Mesh.Mesh(args[0])
|
||||
|
||||
#
|
||||
# The 2.5D model consists of "plateaus" (facets parallel to the xy plane) and
|
||||
# "walls" (facets parallel to the z axis). Anything else is an error and will
|
||||
# produce incorrect results.
|
||||
#
|
||||
# We use plateau facets only for their z position, as indication where to mill
|
||||
# a plateau. Wall facets are kept for later use.
|
||||
#
|
||||
|
||||
vert = Mesh.Mesh()
|
||||
z_raw = {}
|
||||
max_nz = 0
|
||||
inclined = 0
|
||||
for facet in mesh.Facets:
|
||||
if abs(facet.Normal.z) >= 1 - epsilon:
|
||||
z_raw[facet.Points[0][2]] = 1
|
||||
else:
|
||||
nz = abs(facet.Normal.z)
|
||||
if nz > epsilon:
|
||||
inclined += 1
|
||||
max_nz = max(max_nz, nz)
|
||||
v1 = Base.Vector(facet.Points[0])
|
||||
v2 = Base.Vector(facet.Points[1])
|
||||
v3 = Base.Vector(facet.Points[1])
|
||||
vert.addFacet(v1, v2, v3)
|
||||
|
||||
if inclined:
|
||||
print >>sys.stderr # FreeCAD progress reporting messes up newlines
|
||||
print >>sys.stderr, inclined, "inclined facets, maximum normal", max_nz
|
||||
|
||||
#
|
||||
# @@@ This is perhaps a bit too paranoid
|
||||
#
|
||||
# I wrote the Z noise filtering because I had mis-read perfectly good
|
||||
# distinct coordinates as being essentially the same value but with
|
||||
# rounding errors.
|
||||
#
|
||||
|
||||
z_levels = []
|
||||
last = None
|
||||
for z in sorted(z_raw.keys(), reverse = True):
|
||||
if last is None or last - z > epsilon:
|
||||
z_levels.append(z)
|
||||
last = z
|
||||
|
||||
#
|
||||
# Convert the walls to a FreeCAD shape
|
||||
#
|
||||
|
||||
shape = Part.Shape()
|
||||
shape.makeShapeFromMesh(mesh.Topology, mech_eps)
|
||||
bb = shape.BoundBox
|
||||
|
||||
#
|
||||
# Iterate over all plateaus and determine how they intersect with the walls.
|
||||
# For this, we add a small offset to the z position so that we intersect above
|
||||
# the plateau.
|
||||
#
|
||||
|
||||
for z in z_levels:
|
||||
print "# level z = ", z
|
||||
|
||||
if margin is not None:
|
||||
print bb.XMin - margin, " ", bb.YMin - margin, " ", z
|
||||
print bb.XMax + margin, " ", bb.YMin - margin, " ", z
|
||||
print bb.XMax + margin, " ", bb.YMax + margin, " ", z
|
||||
print bb.XMin - margin, " ", bb.YMax + margin, " ", z
|
||||
print bb.XMin - margin, " ", bb.YMin - margin, " ", z
|
||||
print
|
||||
|
||||
for wire in shape.slice(Base.Vector(0, 0, 1), z + epsilon):
|
||||
print "# wire = ", wire
|
||||
first = None
|
||||
last = None
|
||||
for e in wire.Edges:
|
||||
v = e.Vertexes[0]
|
||||
if first is None:
|
||||
first = v
|
||||
if last is None or dist(v, last) >= mech_eps:
|
||||
print_vec(v)
|
||||
last = v
|
||||
if first is not None:
|
||||
print_vec(first)
|
||||
print
|
||||
print
|
||||
|
||||
#
|
||||
# That's all, folks !
|
||||
#
|
Loading…
Reference in New Issue
Block a user