POV-Ray : Newsgroups : povray.general : Impossible stl file conversion! : Re: Impossible stl file conversion! Server Time
15 Sep 2025 08:09:28 EDT (-0400)
  Re: Impossible stl file conversion!  
From: ingo
Date: 12 Sep 2025 14:10:00
Message: <web.68c46188a068c28c17bac71e8ffb8ce3@news.povray.org>
kurtz le pirate <kur### [at] freefr> wrote:
>
>
> I don't know Nim very well, but the code is interesting.
> Even Yoda agrees ;)


Hahaha. Well, for Yoda then, one that reads binary stl, rather untested again,

---%<------%<------%<------%<---
import std/[streams,math, sequtils, strformat]

type
  Vec3 = array[3, float]
  Face = array[3, int]

proc vec3(x,y,z: float): Vec3 = [x,y,z]
proc `+`(a,b: Vec3): Vec3 = [a[0]+b[0], a[1]+b[1], a[2]+b[2]]
proc `/`(a: Vec3, s: float): Vec3 = [a[0]/s, a[1]/s, a[2]/s]
proc dot(a,b: Vec3): float = a[0]*b[0]+a[1]*b[1]+a[2]*b[2]
proc length(v: Vec3): float = sqrt(dot(v,v))
proc normalize(v: Vec3): Vec3 =
  let l = length(v)
  if l > 1e-9: v / l else: vec3(0,1,0)

proc readVec3(s: Stream): Vec3 =
  result[0] = s.readFloat32()
  result[1] = s.readFloat32()
  result[2] = s.readFloat32()

proc loadBinarySTL(path: string): (seq[Vec3], seq[Face], seq[Vec3]) =
  ## Parse binary STL, returning vertices, faces, and per-face normals
  var f = newFileStream(path, fmRead)
  defer: f.close()
  discard f.readStr(80)           # skip header
  let triCount = f.readUint32()

  var verts: seq[Vec3] = @[]
  var faces: seq[Face] = @[]
  var faceNorms: seq[Vec3] = @[]

  for i in 0..<int(triCount):
    let n = readVec3(f)           # face normal
    let v1 = readVec3(f)
    let v2 = readVec3(f)
    let v3 = readVec3(f)
    discard f.readUint16()        # attribute byte count

    let base = verts.len
    verts.add(v1)
    verts.add(v2)
    verts.add(v3)
    faces.add([base, base+1, base+2])
    faceNorms.add(normalize(n))

  return (verts, faces, faceNorms)

# adjecent vertises
proc vertexToFaces(faces: seq[Face], vertexCount: int): seq[seq[int]] =
  result = newSeqWith(vertexCount, newSeq[int]())
  for fi, f in faces:
    for vi in f:
      result[vi].add(fi)

# Vertex normals by averaging face normals
proc computeVertexNormals(vertexCount: int, faceNormals: seq[Vec3], v2f:
seq[seq[int]]): seq[Vec3] =
  result = newSeqWith(vertexCount, vec3(0,0,0))
  for vi in 0..<vertexCount:
    var accum = vec3(0,0,0)
    for fi in v2f[vi]:
      accum = accum + faceNormals[fi]
    result[vi] = normalize(accum)

proc mesh2ToString(verts: seq[Vec3], faces: seq[Face], normals: seq[Vec3]):
string =
  var sb = newStringOfCap(verts.len * 100)
  sb.add("mesh2 {\n")
  sb.add("  vertex_vectors {\n")
  sb.add("    " & $verts.len & ",\n")
  for v in verts: sb.add(&"    <{v[0]}, {v[1]}, {v[2]}>,\n")
  sb.add("  }\n")
  sb.add("  normal_vectors {\n")
  sb.add("    " & $normals.len & ",\n")
  for n in normals: sb.add(&"    <{n[0]}, {n[1]}, {n[2]}>,\n")
  sb.add("  }\n")
  sb.add("  face_indices {\n")
  sb.add("    " & $faces.len & ",\n")
  for f in faces: sb.add(&"    <{f[0]}, {f[1]}, {f[2]}>,\n")
  sb.add("  }\n")
  sb.add("}\n")
  result = sb

proc writeMesh2(path: string, verts: seq[Vec3], faces: seq[Face], normals:
seq[Vec3]) =
  writeFile(path, mesh2ToString(verts, faces, normals))

when isMainModule:
  let (verts, faces, faceNorms) = loadBinarySTL("model.stl")
  let v2f = vertexToFaces(faces, verts.len)
  let vertexNorms = computeVertexNormals(verts.len, faceNorms, v2f)
  writeMesh2("model.inc", verts, faces, vertexNorms)
  echo "Binary STL converted to POV-Ray mesh2."

---%<------%<------%<------%<---

They realy should have/get a decent command line interface...

ingo


Post a reply to this message

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.