muse/book-reading-clip-lamp

A Wi‑Fi/USB‑C ESP32‑S3 reading lamp with OV5640 camera, microphone, speaker amplifier, rechargeable Li‑Po power management, six-LED boost light, USB protection/charging detection, and a clip/gooseneck enclosure with external FlexPIFA antenna.

Version
0.4.3
License
unset
Stars
0

extract-board-bounds.mjs

import { readFileSync, writeFileSync } from "node:fs"
import { Matrix4, Quaternion, Vector3, Box3 } from "three"
const bytes = readFileSync("mechanical/pcb.glb")
const size = bytes.readUInt32LE(12)
const gltf = JSON.parse(bytes.subarray(20, 20 + size).toString())
const inverse = new Matrix4().set(-1,0,0,0, 0,0,1,0, 0,1,0,0, 0,0,0,1)
const result = []
function visit(index, parent) {
  const node = gltf.nodes[index]
  const local = node.matrix ? new Matrix4().fromArray(node.matrix) : new Matrix4().compose(
    new Vector3().fromArray(node.translation ?? [0,0,0]),
    new Quaternion().fromArray(node.rotation ?? [0,0,0,1]),
    new Vector3().fromArray(node.scale ?? [1,1,1]))
  const world = new Matrix4().multiplyMatrices(parent, local)
  if (node.mesh !== undefined && node.name !== "Box0") {
    const bounds = new Box3()
    for (const p of gltf.meshes[node.mesh].primitives) {
      const a = gltf.accessors[p.attributes.POSITION]
      bounds.union(new Box3(new Vector3().fromArray(a.min), new Vector3().fromArray(a.max)).applyMatrix4(world))
    }
    result.push({ name: node.name, min: bounds.min.toArray(), max: bounds.max.toArray() })
  }
  for (const child of node.children ?? []) visit(child, world)
}
for (const index of gltf.scenes[gltf.scene ?? 0].nodes) visit(index, inverse)
writeFileSync("mechanical/pcb-component-bounds.json", JSON.stringify(result, null, 2))
console.log(`Measured ${result.length} component CAD bounding boxes in PCB coordinates`)