(canvas)
| 159 | const x = box.max.x + pad; |
| 160 | addLine([new THREE.Vector3(x, box.min.y, groundZ), new THREE.Vector3(x, box.max.y, groundZ)], hex); |
| 161 | addTick(new THREE.Vector3(x, box.min.y, groundZ), new THREE.Vector3(1, 0, 0), hex); |
| 162 | addTick(new THREE.Vector3(x, box.max.y, groundZ), new THREE.Vector3(1, 0, 0), hex); |
| 163 | const lbl = buildDimensionLabel(`Y: ${fmt(box.max.y - box.min.y)}`, hex, lblW, lblH); |
| 164 | lbl.position.set(x + lblH * 0.7, (box.min.y + box.max.y) / 2, groundZ + zOff); |
| 165 | lbl.rotation.z = Math.PI / 2; |
| 166 | group.add(lbl); |
| 167 | } |
| 168 | |
| 169 | return group; |
| 170 | } |
| 171 | |
| 172 | export function initViewer(canvas) { |
| 173 | // Renderer |
| 174 | renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: false }); |
| 175 | renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); |
| 176 | renderer.outputColorSpace = THREE.SRGBColorSpace; |
| 177 | renderer.toneMapping = THREE.ACESFilmicToneMapping; |
| 178 | renderer.toneMappingExposure = 1.1; |
| 179 | renderer.shadowMap.enabled = false; |
| 180 | |
| 181 | // Scene |
| 182 | scene = new THREE.Scene(); |
| 183 | scene.background = new THREE.Color(0x111114); |
| 184 | |
| 185 | // Grid helper — in XY plane (Z-up) |
| 186 | grid = new THREE.GridHelper(200, 40, 0x333340, 0x2a2a34); |
| 187 | grid.rotation.x = Math.PI / 2; // rotate to XY plane for Z-up |
| 188 | grid.position.z = 0; |
| 189 | scene.add(grid); |
| 190 | |
| 191 | // Camera — orthographic (parallel projection), Z-up (default) |
| 192 | orthoCamera = new THREE.OrthographicCamera(-150, 150, 150, -150, -10000, 10000); |
| 193 | orthoCamera.up.set(0, 0, 1); |
| 194 | orthoCamera.position.set(120, -200, 100); |
| 195 | orthoCamera.lookAt(0, 0, 0); |
| 196 | |
| 197 | // Camera — perspective, Z-up |
| 198 | perspCamera = new THREE.PerspectiveCamera(50, 1, 0.1, 20000); |
| 199 | perspCamera.up.set(0, 0, 1); |
| 200 | perspCamera.position.copy(orthoCamera.position); |
| 201 | perspCamera.lookAt(0, 0, 0); |
| 202 | |
| 203 | camera = orthoCamera; |
| 204 | |
| 205 | // Lights |
| 206 | ambientLight = new THREE.AmbientLight(0xffffff, 0.4); |
| 207 | scene.add(ambientLight); |
| 208 | |
| 209 | dirLight1 = new THREE.DirectionalLight(0xffffff, 1.2); |
| 210 | dirLight1.position.set(80, 120, 60); |
| 211 | dirLight1.castShadow = false; |
| 212 | scene.add(dirLight1); |
| 213 | |
| 214 | dirLight2 = new THREE.DirectionalLight(0x8899ff, 0.4); |
| 215 | dirLight2.position.set(-60, -20, -80); |
| 216 | scene.add(dirLight2); |
| 217 | |
| 218 | // Group to hold the mesh |
no test coverage detected