Пример #1
0
func scale(cmd *cobra.Command, args []string) {
	_, r, err := openIn(args)
	if err != nil {
		fail(err)
	}
	defer r.Close()
	w, err := openOut(outPath)
	if err != nil {
		fail(err)
	}
	defer w.Close()

	t, err := stl.Read(r)
	if err != nil {
		fail("Failed to read STL file:", err)
	}
	for i := range t {
		tr := &t[i]
		for j := 0; j < 3; j++ {
			for k := 0; k < 3; k++ {
				tr.V[j][k] = float32(scaleX * float64(tr.V[j][k]))
			}
		}
	}
	if err := stl.WriteBinary(w, t); err != nil {
		fail("Failed to write STL file:", err)
	}
}
Пример #2
0
func main() {
	timing.StartTiming("total")
	timing.StartTiming("Read STL from Stdin")
	triangles, err := stl.Read(os.Stdin)
	if err != nil {
		log.Fatalf("stl.Read: %v", err)
	}
	timing.StopTiming("Read STL from Stdin")

	timing.StartTiming("STLToMesh")
	mesh := raster.STLToMesh(VoxelSide*MeshMultiplier, triangles)
	timing.StopTiming("STLToMesh")

	timing.StartTiming("MeshVolume")
	volume := triangle.MeshVolume(mesh.Triangle, 1)
	if volume < 0 {
		volume = -volume
	}
	fmt.Fprintf(os.Stderr, "Mesh volume (in mesh units): %d\n", volume)
	timing.StopTiming("MeshVolume")

	timing.StartTiming("Rasterize")
	vol := raster.Rasterize(mesh, VoxelSide)
	timing.StopTiming("Rasterize")

	timing.StartTiming("Optimize")
	Optimize(vol, 22)
	timing.StopTiming("Optimize")

	/*	timing.StartTiming("Write nptl")
		if err = nptl.Write(os.Stdout, vol, mesh.Grid); err != nil {
			log.Fatalf("nptl.Write: %v", err)
		}
		v := vol.Volume()
		fmt.Fprintf(os.Stderr, "Volume is filled by %v%%\n", float64(v)*float64(100)/(float64(vol.N())*float64(vol.N())*float64(vol.N())))
		timing.StopTiming("Write nptl")
	*/

	side := mesh.Grid.Side()
	vsize := surface.Vector{side, side, side}
	t := surface.MarchingCubes(NewVolumeField2(vol), 128, 0.8, vsize)
	var f *os.File
	if f, err = os.OpenFile("output.stl", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
		log.Fatal(err)
	}
	if err = stl.WriteBinary(f, t); err != nil {
		log.Fatalf("stl.Write: %v", err)
	}
	f.Close()

	timing.StopTiming("total")
	timing.PrintTimings(os.Stderr)
}
Пример #3
0
func info(cmd *cobra.Command, args []string) {
	name, r, err := openIn(args)
	if err != nil {
		fail(err)
	}
	defer r.Close()
	t, err := stl.Read(r)
	if err != nil {
		fail(fmt.Sprintf("Failed to read STL file %q: %v", name, err))
	}
	fmt.Printf("File: %s\n", name)
	fmt.Printf("Triangles: %d\n", len(t))
	min, max := stl.BoundingBox(t)
	fmt.Printf("Bounding box: %v - %v\n", min, max)
}
Пример #4
0
func slice(cmd *cobra.Command, args []string) {
	_, r, err := openIn(args)
	if err != nil {
		fail(err)
	}
	defer r.Close()
	w, err := openOut(outPath)
	if err != nil {
		fail(err)
	}
	defer w.Close()
	t, err := stl.Read(r)
	if err != nil {
		fail("Failed to read STL file:", err)
	}

	// by default, slice with XY plane at z = 0
	si := 2
	sx := 0
	sy := 0
	var sv float32
	cnt := 0

	for i, v := range []float64{coordX, coordY, coordZ} {
		if v != 0 {
			si, sx, sy = i, (i+1)%3, (i+2)%3
			sv = float32(v)
			cnt++
		}
	}
	if cnt > 1 {
		fail("More than one coord is specified: x: %f, y: %f, z: %f", coordX, coordY, coordZ)
	}

	min, max := stl.BoundingBox(t)
	eps := (max[si] - min[si]) * sliceThreshold

	less := func(p stl.Point) bool { return p[si] < sv-eps }
	more := func(p stl.Point) bool { return p[si] > sv+eps }
	eq := func(p stl.Point) bool { return !less(p) && !more(p) }
	intersect := func(p0, p1 stl.Point) (res stl.Point) {
		alpha := (sv - p0[si]) / (p1[si] - p0[si])
		for i := 0; i < 3; i++ {
			res[i] = p0[i] + alpha*(p1[i]-p0[i])
		}
		return
	}

	// SVG file will have units of 0.01 mm, and the input STL file is treated as mm.
	pmm := func(v float32) int { return int(v * 100) }
	width := pmm(max[sx] - min[sx])
	height := pmm(max[sy] - min[sy])

	// Write SVG header
	fmt.Fprintln(w, `<?xml version="1.0" encoding="UTF-8" standalone="no"?>`)
	fmt.Fprintln(w, `<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">`)
	fmt.Fprintf(w, `<svg width="%fmm" height="%fmm" version="1.1" viewBox="0 0 %d %d" xmlns="http://www.w3.org/2000/svg">`,
		float64(width)/100, float64(height)/100, width, height)
	fmt.Fprintln(w)
	fmt.Fprintln(w, `<g fill="gray" stroke="black" stroke-width="10">`)

	xx := func(v float32) int { return pmm(v - min[sx]) }
	yy := func(v float32) int { return pmm(v - min[sy]) }
	pxy := func(p stl.Point) string { return fmt.Sprintf("%d,%d", xx(p[sx]), yy(p[sy])) }

	for _, tr := range t {
		// For each triangle, we have the options: skip, draw a line, draw the triangle.

		// First, let's detect the triangles to skip.
		if less(tr.V[0]) && less(tr.V[1]) && less(tr.V[2]) {
			if verbose {
				fmt.Fprintf(w, "<!-- skip a triangle; it's below: %v -->\n", tr.V)
			}
			continue
		}
		if more(tr.V[0]) && more(tr.V[1]) && more(tr.V[2]) {
			if verbose {
				fmt.Fprintf(w, "<!-- skip a triangle; it's above: %v -->\n", tr.V)
			}
			continue
		}

		// Now, check if we need to draw the whole triangle
		if eq(tr.V[0]) && eq(tr.V[1]) && eq(tr.V[2]) {
			fmt.Fprintf(w, "<path d=\"M%s L%s L%s L%s\"/>\n", pxy(tr.V[0]), pxy(tr.V[1]), pxy(tr.V[2]), pxy(tr.V[0]))
			continue
		}

		// OK, it's the line. Two cases: line is a triangle side, or it's not.
		was := false
		for i := 0; i < 3; i++ {
			j := (i + 1) % 3
			k := (i + 2) % 3
			// First, let's check if it's a triangle side.
			if eq(tr.V[i]) && eq(tr.V[j]) {
				fmt.Fprintf(w, "<path d='M%s L%s' />\n", pxy(tr.V[i]), pxy(tr.V[j]))
				was = true
				break
			}
			if less(tr.V[i]) && less(tr.V[j]) || more(tr.V[i]) && more(tr.V[j]) {
				// Since this triangle is known to intersect the slice plane, the k'th vertex is on the other side.
				// and we need to find intersection points on i-k and j-k triangle sides.
				p0 := intersect(tr.V[i], tr.V[k])
				p1 := intersect(tr.V[j], tr.V[k])
				fmt.Fprintf(w, "<path d='M%s L%s' />\n", pxy(p0), pxy(p1))
				was = true
			}
		}
		if was {
			continue
		}

		// Just a dot
		if verbose {
			fmt.Fprintf(w, "<!-- it's just a dot: %v -->\n", tr)
		}
	}

	// Write SVG footer
	fmt.Fprintln(w, "</g>")
	fmt.Fprintln(w, "</svg>")
}
Пример #5
0
func cut(cmd *cobra.Command, args []string) {
	_, r, err := openIn(args)
	if err != nil {
		fail(err)
	}
	defer r.Close()

	if outPath == "" {
		fail(errors.New("--output is not specified"))
	}

	// Find output file base. For example: /home/user/lala.stl -> /home/user/lala, and
	// then it will become /home/user/{lala001.stl,lala002.stl}.
	outExt := filepath.Ext(outPath)
	outBase := outPath[:len(outPath)-len(outExt)]

	// Read input STL
	t, err := stl.Read(r)
	if err != nil {
		fail("Failed to read STL file:", err)
	}

	// by default, cut with XY plane at z = 0
	si := 2
	var sv float32
	cnt := 0

	for i, v := range []float64{coordX, coordY, coordZ} {
		if v != 0 {
			si = i
			sv = float32(v)
			cnt++
		}
	}
	if cnt > 1 {
		fail("More than one coord is specified: x: %f, y: %f, z: %f", coordX, coordY, coordZ)
	}

	min, max := stl.BoundingBox(t)
	eps := (max[si] - min[si]) * cutThreshold

	below := func(p stl.Point) bool { return p[si] < sv-eps }
	above := func(p stl.Point) bool { return p[si] > sv+eps }

	intersect := func(p0, p1 stl.Point) (res stl.Point) {
		alpha := (sv - p0[si]) / (p1[si] - p0[si])
		for i := 0; i < 3; i++ {
			res[i] = p0[i] + alpha*(p1[i]-p0[i])
		}
		return
	}

	// We'll have two output parts: below and above.
	parts := make([][]stl.Triangle, 2)

	for _, tr := range t {
		// For each triangle, we have the options:
		// 1. put into bottom part (all vertices are not above)
		// 2. put into upper part (all vertices are not below)
		// 3. put into both parts (all vertices are equal) -- special case for the two rules above
		// 4. split triangle into two parts, if some vertices are above, and some are below

		simple := false

		if !above(tr.V[0]) && !above(tr.V[1]) && !above(tr.V[2]) {
			parts[0] = append(parts[0], tr)
			simple = true
		}
		if !below(tr.V[0]) && !below(tr.V[1]) && !below(tr.V[2]) {
			parts[1] = append(parts[1], tr)
			simple = true
		}
		if simple {
			continue
		}

		// We'll need to split the triangle into bottom and upper parts.
		tmp := trimTriangleBelow(tr, below, above, intersect)
		parts[0] = append(parts[0], tmp...)

		tmp = trimTriangleBelow(tr, above, below, intersect)
		parts[1] = append(parts[1], tmp...)
	}

	// Now, we need to save both parts.
	for i := 0; i < 2; i++ {
		w, err := openOut(fmt.Sprintf("%s%03d%s", outBase, i, outExt))
		if err != nil {
			fail("Failed to open output file: ", err)
		}

		if err := stl.WriteASCII(w, parts[i]); err != nil {
			w.Close()
			fail("Failed to save an output STL: ", err)
		}
		if err := w.Close(); err != nil {
			fail("Failed to close the output file: ", err)
		}
	}
}