feat(fence): add height selection for fence panels and update related logic
continuous-integration/drone/push Build is passing

- Introduced a new section in the UI for selecting fence heights, visible only when the fence option is enabled.
- Updated the fence label to clarify the presence of gates.
- Enhanced the JavaScript logic to manage the visibility of fence heights based on user selection.
- Modified the fence rendering logic to accommodate different heights and ensure proper visibility based on the selected height.
- Updated CSS to style the new fence height controls appropriately.
- Adjusted the model configuration to reflect changes in the fence height options.
This commit is contained in:
martin.fencl
2026-10-26 21:24:38 +01:00
parent 2bc9be73f3
commit c32d1e5a04
7 changed files with 164 additions and 77 deletions
+16 -3
View File
@@ -312,8 +312,10 @@ let backend=null,projectPanel=null;
}
// Independent site layers: initSiteControls enables all three on each fresh open.
const site = { road: false, parcel: false, parking: false, fence: false, savedCamera: null, lastFocus: null };
// fenceHeight: index of the manufactured height (tools/fence.mjs FENCE.heights), default the tallest under 2 m
const site = { road: false, parcel: false, parking: false, fence: false, fenceHeight: 4, savedCamera: null, lastFocus: null };
const siteActive = () => site.road || site.parcel || site.parking;
const fenceHeightVisible = e => e.fenceOnly !== undefined ? e.fenceOnly === site.fenceHeight : e.fenceBand === undefined || e.fenceBand <= site.fenceHeight;
function meshVisible(m) {
if (m.legacySite) return false;
switch (m.siteLayer) {
@@ -323,7 +325,7 @@ let backend=null,projectPanel=null;
case 'road': return site.road;
case 'parking': return site.parking;
// Each fence part stands on one terrain slab and is shown only together with it.
case 'fence': return showTerrain && site.fence && (m.std?.fenceRegion === 'parcel' ? site.parcel : m.std?.fenceRegion === 'north' ? siteActive() : true);
case 'fence': return showTerrain && site.fence && fenceHeightVisible(m.std || {}) && (m.std?.fenceRegion === 'parcel' ? site.parcel : m.std?.fenceRegion === 'north' ? siteActive() : true);
default: return showTerrain || !m.context;
}
}
@@ -374,6 +376,7 @@ let backend=null,projectPanel=null;
const ids = { road: 'siteRoad', parcel: 'siteParcel', parking: 'siteParking', fence: 'siteFence' };
const input = $('#' + ids[key]); input.checked = site[key];
input.closest('label').classList.toggle('selected', site[key]);
if (key === 'fence') $('#fenceHeights').hidden = !site.fence;
document.body.classList.toggle('site-active', siteActive());
$('#siteMeta').hidden = !siteActive();
$('#siteMeta').textContent = 'C.3 + výškopis · mezi body interpolováno';
@@ -387,6 +390,13 @@ let backend=null,projectPanel=null;
}
dirty = true;
}
function setFenceHeight(index) {
const buttons = [...document.querySelectorAll('[data-fence-height]')];
if (!buttons.some(b => +b.dataset.fenceHeight === index)) return;
site.fenceHeight = index;
for (const b of buttons) { const on = +b.dataset.fenceHeight === index; b.classList.toggle('selected', on); b.setAttribute('aria-pressed', on); }
dirty = true;
}
function updateSiteLight() {
if(interior.enabled){
const center=[1,2,0],lightEye=vadd(center,scale(solar.light,45)),view=lookAt(lightEye,center,Math.abs(solar.light[1])>.999?[0,0,1]:[0,1,0]);
@@ -415,14 +425,17 @@ let backend=null,projectPanel=null;
const input = $('#' + id); input.onchange = () => setSiteLayer(key, input.checked);
setSiteLayer(key, key !== 'fence', false);
}
document.querySelectorAll('[data-fence-height]').forEach(b => b.onclick = () => setFenceHeight(+b.dataset.fenceHeight));
setFenceHeight(site.fenceHeight);
$('#siteFit').onclick = () => focusSite();
window.SITE_SIM = {
getState: () => ({
road: site.road, parcel: site.parcel, parking: site.parking, fence: site.fence, terrain: showTerrain,
road: site.road, parcel: site.parcel, parking: site.parking, fence: site.fence, fenceHeight: site.fenceHeight, terrain: showTerrain,
visibleObjects: meshes.filter(visible).length, referenceElevation: 591.840,
projectArea: 1010, digitizedArea: 987.89, houseGeometryUnchanged: true
}),
setLayer: (key, on, focus = true) => setSiteLayer(key, on, focus),
setFenceHeight: index => setFenceHeight(index),
focus: mode => focusSite(mode),
reset: () => { for (const key of ['road', 'parcel', 'parking', 'fence']) setSiteLayer(key, false, false); setPreset('terrace'); },
getCamera: () => ({ theta, phi, distance, target: [...target], aspect }),