Add InteractionManager with mode system, selection outline, keyboard shortcuts
- Mode system: view | select | move | rotate | place - Click furniture to select with cyan wireframe outline - Keyboard: R/Shift+R rotate, Delete remove, Escape deselect, Ctrl+Z undo, Ctrl+Shift+Z/Ctrl+Y redo - Listens to DesignState changes to sync scene on undo/redo - Wired into index.html with DesignState integration - Added furnitureIndex to mesh userData for state lookups
This commit is contained in:
@@ -76,7 +76,7 @@
|
||||
<div id="room-list"></div>
|
||||
</div>
|
||||
|
||||
<div id="info">Click a room to select it. Scroll to zoom, drag to orbit.</div>
|
||||
<div id="info">Click a room to select it. Click furniture to edit. Scroll to zoom, drag to orbit.</div>
|
||||
|
||||
<script type="importmap">
|
||||
{
|
||||
@@ -88,16 +88,35 @@
|
||||
</script>
|
||||
<script type="module">
|
||||
import { HouseRenderer } from './renderer.js';
|
||||
import { DesignState } from './state.js';
|
||||
import { InteractionManager } from './interaction.js';
|
||||
|
||||
const viewer = document.getElementById('viewer');
|
||||
const renderer = new HouseRenderer(viewer);
|
||||
const houseRenderer = new HouseRenderer(viewer);
|
||||
|
||||
let selectedRoom = null;
|
||||
let designState = null;
|
||||
let interaction = null;
|
||||
|
||||
renderer.loadHouse('../data/sample-house.json').then(async (house) => {
|
||||
houseRenderer.loadHouse('../data/sample-house.json').then(async (house) => {
|
||||
document.getElementById('house-name').textContent = house.name;
|
||||
await renderer.loadCatalog('../data/furniture-catalog.json');
|
||||
await renderer.loadDesign('../designs/sample-house-design.json');
|
||||
await houseRenderer.loadCatalog('../data/furniture-catalog.json');
|
||||
const design = await houseRenderer.loadDesign('../designs/sample-house-design.json');
|
||||
|
||||
// Initialize state and interaction manager
|
||||
designState = new DesignState(design);
|
||||
interaction = new InteractionManager(houseRenderer, designState);
|
||||
|
||||
interaction.onChange((type, detail) => {
|
||||
if (type === 'select') {
|
||||
document.getElementById('info').textContent =
|
||||
`Selected: ${detail.itemName} — R to rotate, Delete to remove, Escape to deselect`;
|
||||
} else if (type === 'deselect') {
|
||||
document.getElementById('info').textContent =
|
||||
'Click a room to select it. Click furniture to edit. Scroll to zoom, drag to orbit.';
|
||||
}
|
||||
});
|
||||
|
||||
buildFloorButtons();
|
||||
buildRoomList();
|
||||
}).catch(err => {
|
||||
@@ -108,12 +127,12 @@
|
||||
function buildFloorButtons() {
|
||||
const container = document.getElementById('floor-buttons');
|
||||
container.innerHTML = '';
|
||||
for (const floor of renderer.getFloors()) {
|
||||
for (const floor of houseRenderer.getFloors()) {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'floor-btn' + (floor.index === renderer.currentFloor ? ' active' : '');
|
||||
btn.className = 'floor-btn' + (floor.index === houseRenderer.currentFloor ? ' active' : '');
|
||||
btn.textContent = floor.name;
|
||||
btn.addEventListener('click', () => {
|
||||
renderer.showFloor(floor.index);
|
||||
houseRenderer.showFloor(floor.index);
|
||||
document.querySelectorAll('.floor-btn').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
buildRoomList();
|
||||
@@ -126,7 +145,7 @@
|
||||
function buildRoomList() {
|
||||
const container = document.getElementById('room-list');
|
||||
container.innerHTML = '';
|
||||
for (const room of renderer.getRooms()) {
|
||||
for (const room of houseRenderer.getRooms()) {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'room-item';
|
||||
item.dataset.roomId = room.id;
|
||||
@@ -138,11 +157,11 @@
|
||||
|
||||
function selectRoom(roomId) {
|
||||
selectedRoom = roomId;
|
||||
renderer.focusRoom(roomId);
|
||||
houseRenderer.focusRoom(roomId);
|
||||
document.querySelectorAll('.room-item').forEach(el => {
|
||||
el.classList.toggle('active', el.dataset.roomId === roomId);
|
||||
});
|
||||
const room = renderer.getRooms().find(r => r.id === roomId);
|
||||
const room = houseRenderer.getRooms().find(r => r.id === roomId);
|
||||
if (room) {
|
||||
document.getElementById('info').textContent = `${room.name} (${room.nameEN}) — ${room.area} m²`;
|
||||
}
|
||||
@@ -151,11 +170,6 @@
|
||||
viewer.addEventListener('roomclick', (e) => {
|
||||
selectRoom(e.detail.roomId);
|
||||
});
|
||||
|
||||
viewer.addEventListener('furnitureclick', (e) => {
|
||||
const d = e.detail;
|
||||
document.getElementById('info').textContent = `${d.itemName} — in ${renderer.getRooms().find(r => r.id === d.roomId)?.name || d.roomId}`;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user