// setFootprints.js // LEGO Hidden Side set footprint presets for the Newbury Exhibit layout editor. // Units: centimetres (1 Three.js unit = 1 cm). width = X extent, depth = Z extent. // Fan/tribute project; not affiliated with or endorsed by the LEGO Group. // Source: brickset.com/sets/theme-Hidden-Side (published model dimensions W x D x H; // footprint = the two horizontal axes). export const SET_FOOTPRINTS = [ { setNumber: "70418", name: "J.B.'s Ghost Lab", width: 13, depth: 18, height: 9, year: 2019 }, { setNumber: "70420", name: "Graveyard Mystery", width: 14, depth: 32, height: 10, year: 2019 }, { setNumber: "70421", name: "El Fuego's Stunt Truck", width: 10, depth: 10, height: 17, year: 2019 }, { setNumber: "70422", name: "Shrimp Shack Attack", width: 19, depth: 32, height: 10, year: 2019 }, { setNumber: "70424", name: "Ghost Train Express", width: 14, depth: 61, height: 15, year: 2019 }, { setNumber: "70425", name: "Newbury Haunted High School", width: 30, depth: 43, height: 26, year: 2019 }, { setNumber: "70427", name: "Welcome to the Hidden Side", width: 17, depth: 22, height: 13, year: 2020 }, { setNumber: "70428", name: "Jack's Beach Buggy", width: 10, depth: 11, height: 8, year: 2020 }, { setNumber: "70429", name: "El Fuego's Stunt Plane", width: 22, depth: 22, height: 8, year: 2020 }, { setNumber: "70430", name: "Newbury Subway", width: 15, depth: 26, height: 14, year: 2020 }, { setNumber: "70431", name: "The Lighthouse of Darkness", width: 29, depth: 22, height: 18, year: 2020 }, { setNumber: "70432", name: "Haunted Fairground", width: 28, depth: 35, height: 27, year: 2020 }, { setNumber: "70433", name: "J.B.'s Submarine", width: 21, depth: 19, height: 10, year: 2020 }, { setNumber: "70434", name: "Supernatural Race Car", width: 19, depth: 11, height: 7, year: 2020 }, { setNumber: "70435", name: "Newbury Abandoned Prison", width: 19, depth: 30, height: 15, year: 2020 }, { setNumber: "70437", name: "Mystery Castle (closed)", width: 34, depth: 31, height: 27, year: 2020 }, { setNumber: "70437o", name: "Mystery Castle (open)", width: 45.5, depth: 26, height: 27, year: 2020 } ]; // Lookup by set number. export function getFootprint(setNumber) { return SET_FOOTPRINTS.find(s => s.setNumber === setNumber) || null; } // Dropdown label helper: "70425 — Newbury Haunted High School (30 × 43 cm)". export function footprintLabel(fp) { return `${fp.setNumber} — ${fp.name} (${fp.width} × ${fp.depth} cm)`; } // Build a THREE.LineLoop outlining the footprint on the table plane (Y = 0), // centred on origin. Pass in your THREE namespace so this stays import-map friendly. // Returns an object3D you can add to the scene and reposition via .position. export function makeFootprintOutline(THREE, fp, { color = 0x51eaf1, y = 0.05 } = {}) { const hw = fp.width / 2; const hd = fp.depth / 2; const pts = [ new THREE.Vector3(-hw, y, -hd), new THREE.Vector3( hw, y, -hd), new THREE.Vector3( hw, y, hd), new THREE.Vector3(-hw, y, hd), new THREE.Vector3(-hw, y, -hd) ]; const geom = new THREE.BufferGeometry().setFromPoints(pts); const mat = new THREE.LineBasicMaterial({ color }); const loop = new THREE.Line(geom, mat); loop.userData.setNumber = fp.setNumber; loop.userData.footprint = { width: fp.width, depth: fp.depth }; return loop; } // Filled semi-transparent plane version (useful as a placement guide beneath the build). export function makeFootprintPlane(THREE, fp, { color = 0x51eaf1, opacity = 0.12, y = 0.02 } = {}) { const geom = new THREE.PlaneGeometry(fp.width, fp.depth); const mat = new THREE.MeshBasicMaterial({ color, opacity, transparent: true, side: THREE.DoubleSide, depthWrite: false }); const mesh = new THREE.Mesh(geom, mat); mesh.rotation.x = -Math.PI / 2; // lay flat on the table mesh.position.y = y; mesh.userData.setNumber = fp.setNumber; mesh.userData.footprint = { width: fp.width, depth: fp.depth }; return mesh; }