Commit 602aff35 authored by Matthias Betz's avatar Matthias Betz
Browse files

add LOD panel



Co-Authored-By: default avatarClaude Opus 5.5 (1M context) <noreply@anthropic.com>
parent 74d15e77
// Checkboxes to show and hide LODs. lodItems() is the pure part (tested in
// Node), mountLodPanel() renders it and writes changes to the store.
import { compareLods, isVisible, setHidden } from '../app/state.js';
export function lodLabel(lod) {
return lod === null ? 'No LOD' : `LOD ${lod}`;
}
// One item per LOD that occurs in any layer, in LOD order.
export function lodItems(state) {
const lods = new Set();
for (const layer of state.layers) {
for (const lod of layer.lods) {
lods.add(lod);
}
}
return [...lods].sort(compareLods).map(lod => ({
lod,
label: lodLabel(lod),
visible: isVisible(state, 'lod', lod),
}));
}
export function mountLodPanel(container, store) {
const heading = document.createElement('h2');
heading.textContent = 'LOD';
const list = document.createElement('div');
container.append(heading, list);
let rendered = null;
function render(state) {
const items = lodItems(state);
const key = JSON.stringify([state.layers.length, items]);
if (key === rendered) {
return; // progress updates do not rebuild the checkboxes
}
rendered = key;
list.replaceChildren();
if (items.length === 0) {
const message = document.createElement('p');
message.textContent = state.layers.length === 0 ? 'No file loaded' : 'No geometry yet';
list.append(message);
return;
}
for (const item of items) {
const label = document.createElement('label');
const box = document.createElement('input');
box.type = 'checkbox';
box.checked = item.visible;
box.dataset.lod = String(item.lod);
box.addEventListener('change', () => store.update(setHidden, 'lod', item.lod, !box.checked));
label.append(box, ' ', item.label);
list.append(label);
}
}
render(store.get());
return store.subscribe(render);
}
import test from 'node:test';
import assert from 'node:assert/strict';
import { lodItems, lodLabel } from '../public/src/ui/lodPanel.js';
import { initialState, addLayer, reportLod, setHidden } from '../public/src/app/state.js';
function stateWithLods(...lods) {
let state = addLayer(initialState(), { id: 1, name: 'a.gml' });
for (const lod of lods) state = reportLod(state, 1, lod);
return state;
}
test('labels name the LOD, null is "No LOD"', () => {
assert.deepEqual([0, 4, null].map(lodLabel), ['LOD 0', 'LOD 4', 'No LOD']);
});
test('without layers there are no items', () => {
assert.deepEqual(lodItems(initialState()), []);
});
test('items follow the LOD order with "No LOD" last, all visible by default', () => {
assert.deepEqual(lodItems(stateWithLods(null, 2, 1)), [
{ lod: 1, label: 'LOD 1', visible: true },
{ lod: 2, label: 'LOD 2', visible: true },
{ lod: null, label: 'No LOD', visible: true },
]);
});
test('hidden LODs are listed as not visible', () => {
const state = setHidden(stateWithLods(1, 2), 'lod', 2, true);
assert.deepEqual(lodItems(state).map(i => [i.lod, i.visible]), [[1, true], [2, false]]);
});
test('a file with geometry only outside lodN elements offers "No LOD"', () => {
assert.deepEqual(lodItems(stateWithLods(null)), [{ lod: null, label: 'No LOD', visible: true }]);
});
test('LODs of several layers are combined without duplicates', () => {
const state = {
...initialState(),
layers: [{ id: 1, lods: [1, 2] }, { id: 2, lods: [2, 3] }],
};
assert.deepEqual(lodItems(state).map(i => i.lod), [1, 2, 3]);
});
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment