Draws the model in the 3D view: section planes and boxes, the turned section frame, picking and walking.
How the precision is kept
The viewer draws with three.js from 32-bit vertex buffers on the GPU. The calculations listed here place clipping planes and frames for the view; none of them changes a model value.
Calculations under this policy
3 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.
Plane positions are 64-bit until three.js hands them to the GPU. The frame is moved inward by a small fixed amount so it is not clipped by its own planes.
viewer3d.js
lines 446 to 456
BimCraft 0.8.1
const e = (hi.x - lo.x + hi.y - lo.y + hi.z - lo.z) * 1e-5 + 1e-4;
for (const cell of cells) {
const pl = cellPlanes(cell), g = [];
for (const q of pl) { g.push(planes.length); planes.push(new THREE.Plane().setFromNormalAndCoplanarPoint(this.turnDir(new THREE.Vector3(-q.n[0], -q.n[2], q.n[1])), P(q.p))); }
groups.push(g);
// the cell's edges, a hair inside (every plane clips the model; the frame is drawn unclipped over it)
const ph = polyhedron(pl);
if (!ph.verts.length) continue;
const c = ph.verts.reduce((s, v) => s.add(P(v)), new THREE.Vector3()).multiplyScalar(1 / ph.verts.length);
const V = ph.verts.map((v) => { const x = P(v), d = c.clone().sub(x); const l = d.length(); return l > e ? x.add(d.multiplyScalar(e / l)) : x; });
for (const [a, z] of ph.edges) frame.push(V[a], V[z]);
Section frame turned with the building
32-bit, display only
Rotates a viewer point about the vertical through the model's center into the section frame.
x′ = c_x + x cos a + z sin a; z′ = c_z − x sin a + z cos a
A plane rotation in 64-bit, used to place section planes and grips in the view.
viewer3d.js
lines 591 to 596
BimCraft 0.8.1
/** viewer → the section frame (turned by −sectionTurn about the vertical through the box's centre), and back. */
toTurned(p) {
if (!this.sectionTurn) return p.clone();
const c = this.box.getCenter(new THREE.Vector3()), a = -this.sectionTurn, x = p.x - c.x, z = p.z - c.z;
return new THREE.Vector3(c.x + x * Math.cos(a) + z * Math.sin(a), p.y, c.z - x * Math.sin(a) + z * Math.cos(a));
}
Model bounds in the section frame
32-bit, display only
The bounds of every drawn vertex in the turned section frame.
box = min and max of turned vertex positions
Reads the meshes' 32-bit position attribute. The box sets the range of the section sliders in the view.
viewer3d.js
lines 575 to 590
BimCraft 0.8.1
/** The model's bounds in the section frame: the box itself when not turned, else the tight bounds of every vertex. */
secBox() {
if (!this.sectionTurn) return this.box;
const key = this.box.min.toArray().concat(this.box.max.toArray()).join() + '|' + this.sectionTurn + '|' + this.meshes.length;
if (this._secBox && this._secBoxKey === key) return this._secBox;
const bb = new THREE.Box3(), v = new THREE.Vector3();
for (const m of [...this.meshes, ...(this.spaceMeshes || [])]) {
if (!m.visible && !m.parent?.visible) continue;
m.updateMatrixWorld();
const pos = m.geometry.getAttribute('position');
for (let i = 0; i < pos.count; i++) bb.expandByPoint(this.toTurned(v.fromBufferAttribute(pos, i).applyMatrix4(m.matrixWorld)));
}
if (bb.isEmpty()) bb.copy(this.box);
this._secBox = bb; this._secBoxKey = key;
return bb;
}
In detail
Method
three.js meshes, clipping planes and raycasting.
Number types
Float32Array vertex buffers for the GPU; 64-bit numbers for plane and frame positions.
Approach: Display only
Computed to draw the screen, often in 32-bit on the graphics processor. No number a person reads, saves or exports comes from it.