package module
v1.0.4 Latest Latest

This package is not in the latest version of its module.

Go to latest
Published: Aug 9, 2020 License: MIT Imports: 12 Imported by: 11



A library to read, write, and transform Stereolithography (.stl) files in Go. It is used in the command line STL manipulation tool stltool.


  • Read and write STL files in either binary or ASCII form
  • Check correctness of STL files
  • Measure models
  • Various linear model transformations
    • Scale
    • Rotate
    • Translate (Move)
    • Fit into box
    • Apply generic 4x4 transformation matrix


  • Save 3D models as STL
  • Import STL models
  • Repair and manipulation of STL models
  • General pre-processing before processing STL models in a 3D printing slicer
  • Writing a slicer in Go (I hope someone does this one day)


Using go's builtin installation mechanism:

go get

Usage Example

solid, errRead := stl.ReadFile(inputFilename)
if errRead != nil {
  // handle
solid.Scale(25.4) // Convert from Inches to mm
errWrite := solid.WriteFile(outputFilename)

Stream Processing STL Files

You can implement the stl.Writer interface to directly write into your own data structures. This way you can use the stl.CopyFile and stl.CopyAll functions.

var ownData ownDataStructure // implements stl.Writer
err := stl.CopyFile("somefile.stl", &ownData)

Further Reading

The package godoc documentation should be helpful. Or just start a local godoc server using this command:

godoc -http=:6060

Then open http://localhost:6060/pkg/ in your browser.


The stl package is licensed under the MIT license. See LICENSE file.



Package stl implements functions to read, write, and transform files in the Stereolithography/Surface Tesselation Language (.stl) file format used in 3D modelling.

The format specification was taken from, found at

While STL stores the data in single precision 32 bit floating point numbers, the stl package does all calculations beyond simple addition in double precision 64 bit (float64).

Usage Example

// Read STL file
solid, errRead := stl.ReadFile(inputFilename)
if errRead != nil {
  fmt.Fprintln(os.Stderr, errRead)

// Convert from Inches to mm

// Write STL file
errWrite := solid.WriteFile(outputFilename)
if errWrite != nil {
  fmt.Fprintln(os.Stderr, errWrite)

Everything that operates on a model is defined as a method of Solid.

Note that The STL format has two variants, a human-readable ASCII variant, and a more compact and precise binary variant which is preferrable.

ASCII Format Specialities

The Solid.BinaryHeader field and the Triangle.Attributes fields will be empty, after reading, as these are not part of the ASCII format. The Solid.Name field is read from the first line after "solid ". It is not checked against the name at the end of the file after "endsolid ". The stl package will also not cope with Unicode byte order marks, which some text editors might automatically place at the beginning of a file.

Binary Format Specialities

The Solid.BinaryHeader field is filled with all 80 bytes of header data. Then, ReadFile will try to fill solid.Name with an ASCII string read from the header data from the first byte until a \0 or a non-ASCII character is detected.

Numerical Errors

As always when you do linear transformations on floating point numbers, you get numerical errors. So you should expect a vertex being rotated for 360° not to end up at exactly the original coordinates, but instead just very close to them. As the error is usually far smaller than the available precision of 3D printing applications, this is not an issue in most cases.

Stream Processing

You can implement the Writer interface to directly write into your own data structures. This way you can use the CopyFile and CopyAll functions.

var ownData ownDataStructure // implements stl.Writer
err := stl.CopyFile("somefile.stl", &ownData)



View Source
const HalfPi = math.Pi * 0.5

HalfPi is math.Pi * 0.5

View Source
const Pi = math.Pi

Pi is just math.Pi

View Source
const QuarterPi = math.Pi * 0.25

QuarterPi is math.Pi * 0.25

View Source
const TwoPi = math.Pi * 2

TwoPi is math.Pi * 2


View Source
var ErrIncompleteBinaryHeader = errors.New("incomplete STL binary header, 84 bytes expected")

ErrIncompleteBinaryHeader is used when reading binary STL files with incomplete header.

View Source
var ErrUnexpectedEOF = errors.New("unexpected end of file")

ErrUnexpectedEOF is used by ReadFile and ReadAll to signify an incomplete file.

View Source
var Mat4Identity = Mat4{
	Vec4{1, 0, 0, 0},
	Vec4{0, 1, 0, 0},
	Vec4{0, 0, 1, 0},
	Vec4{0, 0, 0, 1},

Mat4Identity is the identity matrix


func CopyAll added in v1.0.4

func CopyAll(r io.ReadSeeker, sw Writer) (err error)

func CopyFile added in v1.0.4

func CopyFile(filename string, sw Writer) (err error)

func RotationMatrix

func RotationMatrix(pos Vec3, dir Vec3, angle float64, rotationMatrix *Mat4)

RotationMatrix calculates a 4x4 rotation matrix for a rotation of angle in radians around a rotation axis defined by a point on it (pos) and its direction (dir). The result is written into *rotationMatrix.


type EdgeError

type EdgeError struct {
	// SameEdgeTriangles are indexes in Solid.Triangles of triangles that contain exactly the same edge.
	SameEdgeTriangles []int

	// CounterEdgeTriangles are indexes in Solid.Triangles of triangles that contain the edge in the
	// opposite direction. If there is exactly one other triangle, this is no
	// error.
	CounterEdgeTriangles []int

EdgeError describes the errors found for a single edge within a triangle using Solid.Validate().

func (*EdgeError) HasMultipleCounterEdges

func (eer *EdgeError) HasMultipleCounterEdges() bool

HasMultipleCounterEdges is true if there is more than one other triangle with this edge in the opposite direction

func (*EdgeError) HasNoCounterEdge

func (eer *EdgeError) HasNoCounterEdge() bool

HasNoCounterEdge is true if there is no other triangle with this edge in the opposite direction, meaning that there is no neighboring triangle

func (*EdgeError) IsUsedInOtherTriangles

func (eer *EdgeError) IsUsedInOtherTriangles() bool

IsUsedInOtherTriangles is true if this edge is also used in another triangle, meaning that there is probably something wrong with this or the other triangle's orientation.

type Mat4

type Mat4 [4]Vec4

Mat4 represents a 4x4 Matrix of float64 used for 3D transformations. The 4th column can be used for moving the solid on the axes. Accessing matrix elements goes like this:


func (*Mat4) MultMat4

func (m *Mat4) MultMat4(o *Mat4, r *Mat4)

MultMat4 Multiplies m with o and write the result into r.

func (*Mat4) MultVec3

func (m *Mat4) MultVec3(v Vec3) Vec3

MultVec3 multiplies m with v, where v[3] is assumed to be 1, and the 4th result value is not calculated, as is usual in 3D transformations.

type Solid

type Solid struct {
	// only used in binary format
	BinaryHeader []byte

	// Name is the solid's name
	Name string

	// Triangles represent the solid's shape.
	Triangles []Triangle

	// IsAscii is true, if this Solid was read from an ASCII file, and false, if read
	// from a binary file. Also used to determine the format when writing
	// to a file.
	IsAscii bool

Solid is a 3D model made out of triangles, called solid in STL, representing an STL file

func ReadAll

func ReadAll(r io.ReadSeeker) (solid *Solid, err error)

ReadAll reads the contents of a file into a new Solid object. The file can be either in STL ASCII format, beginning with "solid ", or in STL binary format, beginning with a 84 byte header. Because of this, the file pointer has to be at the beginning of the file.

func ReadFile

func ReadFile(filename string) (solid *Solid, err error)

ReadFile reads the contents of a file into a new Solid object. The file can be either in STL ASCII format, beginning with "solid ", or in STL binary format, beginning with a 84 byte header. Shorthand for os.Open and ReadAll

func (*Solid) AppendTriangle added in v1.0.4

func (s *Solid) AppendTriangle(t Triangle)

AppendTriangle appends the given triangle to s.Triangles

func (*Solid) IsInPositive

func (s *Solid) IsInPositive() bool

IsInPositive is true if every vertex in this solid is within the positive octant, i.e. all coordinate values are positive or 0.

func (*Solid) Measure

func (s *Solid) Measure() SolidMeasure

Measure the dimensions of a solid in its own units

func (*Solid) MoveToPositive

func (s *Solid) MoveToPositive()

MoveToPositive moves the solid into the positive octant if necessary, as prescribed by the original STL format spec. Some applications tolerate negative coordinates. This also makes sense, as the origin is a perfect reference point for rotations.

func (*Solid) RecalculateNormals

func (s *Solid) RecalculateNormals()

RecalculateNormals recalculates all triangle normal vectors from the vertices. Can be used after multiple transformations using the TransformNR method that does not recalculate the normal vectors.

func (*Solid) Rotate

func (s *Solid) Rotate(pos, dir Vec3, angle float64)

Rotate the solid by angle radians around a rotation axis defined by a point pos on the axis and a direction vector dir. This example would rotate the solid by 90 degree around the z-axis:

stl.Rotate(stl.Vec3{0,0,0}, stl.Vec3{0,0,1}, stl.HalfPi)

func (*Solid) Scale

func (s *Solid) Scale(factor float64)

Scale all vertex coordinates by scalar factor

func (*Solid) ScaleLinearDowntoSizeBox

func (s *Solid) ScaleLinearDowntoSizeBox(sizeBox Vec3)

ScaleLinearDowntoSizeBox works like this: if the solid does not fit into size box defined by sizeBox, it is scaled down accordingly. It is not scaled up, if it is smaller than sizeBox. All sizes have to be > 0.

func (*Solid) SetASCII added in v1.0.4

func (s *Solid) SetASCII(isASCII bool)

SetASCII sets the IsAscii flag that indicates whether the solid was read from ASCII STL

func (*Solid) SetBinaryHeader added in v1.0.4

func (s *Solid) SetBinaryHeader(header []byte)

SetName sets the binary header optionally used when writing to binary STL

func (*Solid) SetName added in v1.0.4

func (s *Solid) SetName(name string)

SetName sets the solid's name

func (*Solid) SetTriangleCount added in v1.0.4

func (s *Solid) SetTriangleCount(n uint32)

SetTriangleCount ensures that cap(s.Triangles) >= n, and len(s.Triangles) <= n, possibly deleting triangles starting at index n

func (*Solid) Stretch

func (s *Solid) Stretch(vec Vec3)

Stretch scales all vertex coordinates by different factors per axis

func (*Solid) Transform

func (s *Solid) Transform(transformationMatrix *Mat4)

Transform applies a 4x4 transformation matrix to every vertex and recalculates the normal for every triangle

func (*Solid) TransformNR

func (s *Solid) TransformNR(transformationMatrix *Mat4)

TransformNR applies a 4x4 transformation matrix to every vertex and does not recalculate the normal vector for every triangle. This could be used to speed things up when multiple transformations are applied successively to a solid, and the transformation matrix is not calculated beforehand. Before writing this solid to disk then, RecalculateNormals() should be called.

func (*Solid) Translate

func (s *Solid) Translate(vec Vec3)

Translate (i.e. move) the solid by vec

func (*Solid) Validate

func (s *Solid) Validate() map[int]*TriangleErrors

Validate looks for triangles that are really lines or dots, and for edges that violate the vertex-to-vertex rule. Returns a map of errors by triangle index that could be used to print out an error report.

func (*Solid) WriteAll

func (s *Solid) WriteAll(w io.Writer) error

WriteAll writes the contents of this solid to an io.Writer. Depending on solid.IsAscii the STL ASCII format, or the STL binary format is used. If IsAscii is false, and the binary format is used, solid.Name will be used for the header, if solid.BinaryHeader is empty.

func (*Solid) WriteFile

func (s *Solid) WriteFile(filename string) (err error)

WriteFile creates file with name filename and write contents of this Solid. Shorthand for os.Create and Solid.WriteAll

type SolidMeasure

type SolidMeasure struct {
	// Minimum values for axes
	Min Vec3

	// Maximum values for axes
	Max Vec3

	// Max - Min
	Len Vec3

SolidMeasure is used to store the result of Solid.Measure()

type Triangle

type Triangle struct {
	// Normal vector of triangle, should be normalized...
	Normal Vec3

	// Vertices of triangle in right hand order.
	// I.e. from the front the triangle's vertices are ordered counterclockwise
	// and the normal vector is orthogonal to the front pointing outside.
	Vertices [3]Vec3

	// 16 bits of attributes. Not available in ASCII format. Could be used
	// for color selection, texture selection, refraction etc. Some tools ignore
	// this field completely, always writing 0 on export.
	Attributes uint16

Triangle represents single triangles used in Solid.Triangles. The vertices have to be ordered counter-clockwise when looking at their outside surface. The vector Normal is orthogonal to the triangle, pointing outside, and has length 1. This is redundant but included in the STL format in order to avoid recalculation.

type TriangleErrors

type TriangleErrors struct {
	// HasEqualVertices is true if some vertices are identical, meaning we are having
	// a line, or even a point, as opposed to a triangle.
	HasEqualVertices bool

	// NormalDoesNotMatch istrue if the normal vector does not match a normal calculated from the
	// vertices in the right hand order, even allowing for an angular difference
	// of < 90 degree.
	NormalDoesNotMatch bool

	// EdgeErrors by edge. The edge is indexed by it's first vertex, i.e.
	//    0: V0 -> V1
	//    1: V1 -> V2
	//    2: V2 -> V0
	// If the edge has no error its value is nil.
	EdgeErrors [3]*EdgeError

TriangleErrors represent the errors found in a single triangle.

type Vec3

type Vec3 [3]float32

Vec3 represents a 3D vector, used in Triangle for normal vector and vertices.

type Vec4

type Vec4 [4]float64

Vec4 is used to construct Mat4

type Writer added in v1.0.4

type Writer interface {
	// SetName sets the solid's name
	SetName(name string)

	// SetBinaryHeader explicitly sets a binary header used in binary STL
	SetBinaryHeader(header []byte)

	// SetASCII sets the IsAscii flag that indicates whether the solid was read from ASCII STL
	SetASCII(isASCII bool)

	// SetTriangleCount can optionally be used to set the triangle count if it is known, so
	// the underlying implementation can use it to allocate a data structure, or similar.
	SetTriangleCount(n uint32)

	// AppendTriangle adds a triangle to the solid
	AppendTriangle(t Triangle)

Writer processes an STL solid as a stream.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL