Ortho-Foto sichtbar (PictureFrame) + Oberleiste-Polish
Swisstopo Ortho
- AddPictureFrame statt Mesh+Material — Rhinos eigener Picture-Pfad mit
embedBitmap=True + selfIllumination=True macht die Textur in allen
Display-Modi sichtbar (Wireframe / Shaded / Rendered / Raytraced)
- asMesh=False (Brep-Variante) — asMesh=True ist auf Mac Rhino 8 broken
(alle Pictures landen am gleichen Punkt unabhaengig von der Plane)
- Per-Tile Sub-Ebenen unter 80_swisstopo (z.B. 80_swisstopo/2763-1254_Ortho)
via dossier_ebenen JSON registriert → erscheinen im Dossier-Ebenen-Manager
mit eigener Visibility
- target_layer_idx wird vor AddPictureFrame als Active-Layer gesetzt,
Picture landet direkt auf richtigem Sub-Layer (Move-danach broeselte
das Material)
- Regex-Fix in _parse_swisstopo_tile_bbox: Separator zwischen den beiden
Coords MUSS Hyphen sein, sonst matcht es faelschlich auf `_YEAR_EAST_`
Patterns wie `_2025_2763_`
Oberleiste
- DOSSIER. Logo (Krungthep + Petrol-Punkt) + Launcher-Version
(via __LAUNCHER_VERSION__ Vite-Define aus launcher/package.json)
- Overrides + Kombi vertikal gestapelt, gleiche Label-Spalte + Dropdown-
Breite → Dropdowns auf gleicher X-Linie
- View-Icons neu zugeordnet:
Top=view_quilt (Raster), Front=north (Pfeil), Persp=view_in_ar (3D)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+133
-103
@@ -21,10 +21,10 @@ const PRESETS = [
|
||||
]
|
||||
|
||||
const VIEWS = [
|
||||
{ value: 'Top', icon: 'north', label: 'Top' },
|
||||
{ value: 'Front', icon: 'view_in_ar', label: 'Front' },
|
||||
{ value: 'Top', icon: 'view_quilt', label: 'Top' },
|
||||
{ value: 'Front', icon: 'north', label: 'Front' },
|
||||
{ value: 'Right', icon: 'east', label: 'Right' },
|
||||
{ value: 'Perspective', icon: 'view_quilt', label: 'Persp' },
|
||||
{ value: 'Perspective', icon: 'view_in_ar', label: 'Persp' },
|
||||
]
|
||||
|
||||
function fmtScale(s) {
|
||||
@@ -207,21 +207,37 @@ export default function OberleisteApp() {
|
||||
{/* === Toolbar (View, Display, Massstab, Snap, Overrides) === */}
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 8,
|
||||
padding: '6px 12px',
|
||||
padding: '4px 12px 8px',
|
||||
overflowX: 'auto', overflowY: 'hidden',
|
||||
flexShrink: 0,
|
||||
}}>
|
||||
<span
|
||||
{/* Logo: DOSSIER. (Petrol-Punkt) + Launcher-Version */}
|
||||
<div
|
||||
style={{
|
||||
fontSize: 11, fontWeight: 600, letterSpacing: '0.08em',
|
||||
color: 'var(--text-muted)',
|
||||
fontFamily: 'DM Mono, monospace',
|
||||
display: 'flex', alignItems: 'baseline', gap: 8,
|
||||
flexShrink: 0, userSelect: 'none',
|
||||
}}
|
||||
title={`Dossier ${__APP_VERSION__} — Teil von OpenStudio`}
|
||||
title={`Dossier ${__LAUNCHER_VERSION__} (Plugin ${__APP_VERSION__}) — Teil von OpenStudio`}
|
||||
>
|
||||
DOSSIER <span style={{ opacity: 0.55 }}>{__APP_VERSION__}</span>
|
||||
</span>
|
||||
<span style={{
|
||||
fontFamily: "Krungthep, 'Archivo Black', sans-serif",
|
||||
fontSize: 18,
|
||||
letterSpacing: '-0.02em',
|
||||
color: 'var(--text-primary)',
|
||||
lineHeight: 1,
|
||||
}}>
|
||||
DOSSIER<span style={{ color: 'var(--accent)' }}>.</span>
|
||||
</span>
|
||||
<span style={{
|
||||
fontFamily: 'DM Mono, monospace',
|
||||
fontSize: 9,
|
||||
letterSpacing: '0.14em',
|
||||
color: 'var(--text-muted)',
|
||||
textTransform: 'uppercase',
|
||||
}}>
|
||||
v{__LAUNCHER_VERSION__}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => openDossierSettings()}
|
||||
title="Dossier-Einstellungen"
|
||||
@@ -344,98 +360,112 @@ export default function OberleisteApp() {
|
||||
{/* Snap-Toggles (Ortho/Grid/OSnap) sind in Rhinos eigener Footer-Bar
|
||||
schon vorhanden — hier rausgenommen um Doppelung zu vermeiden. */}
|
||||
|
||||
{/* ====== GRUPPE: OVERRIDES ====== */}
|
||||
<span style={groupLabel}>Overrides</span>
|
||||
<ToolButton
|
||||
onClick={() => toggleOverrides(!state.overridesEnabled)}
|
||||
active={state.overridesEnabled}
|
||||
icon="auto_fix_high"
|
||||
label={state.overridesEnabled ? 'AN' : 'AUS'}
|
||||
title={state.overridesEnabled
|
||||
? `Grafische Overrides aktiv — klick zum Ausschalten`
|
||||
: `Grafische Overrides ausgeschaltet`}
|
||||
/>
|
||||
{/* Preset-Dropdown: aktive Kombination waehlen. "—" = keine Kombination
|
||||
(Doc-Rules sind frei editiert oder leer). "Konfigurieren…" oeffnet
|
||||
den grossen Regel-Editor (OVERRIDES-Panel). */}
|
||||
<select
|
||||
value={state.overridesActivePreset || '__none__'}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
if (v === '__configure__') { openOverridesPanel(); return }
|
||||
setOverridesPreset(v === '__none__' ? null : v)
|
||||
}}
|
||||
style={{ ...pillSelect, width: 140 }}
|
||||
title={state.overridesActivePreset
|
||||
? `Aktives Preset: ${state.overridesActivePreset} (${state.overridesCount} Regeln)`
|
||||
: `Kein Preset aktiv (${state.overridesCount} Regeln, frei editiert)`}
|
||||
>
|
||||
<option value="__none__">{state.overridesCount > 0 ? `— (${state.overridesCount} Regeln)` : '—'}</option>
|
||||
{(state.overridesPresets || []).map(name => (
|
||||
<option key={name} value={name}>{name}</option>
|
||||
))}
|
||||
<option disabled>──────────</option>
|
||||
<option value="__configure__">Konfigurieren…</option>
|
||||
</select>
|
||||
<ToolButton
|
||||
onClick={openOverridesPanel}
|
||||
icon="settings"
|
||||
title="Overrides-Regel-Editor öffnen"
|
||||
/>
|
||||
|
||||
<div style={sep} />
|
||||
|
||||
{/* ====== GRUPPE: EBENENKOMBINATION ====== */}
|
||||
<span style={groupLabel}>Kombi</span>
|
||||
<select
|
||||
value={state.layerCombinationActive || '__none__'}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
if (v === '__configure__') { openLayerCombinationsDialog(); return }
|
||||
if (v === '__delete__') {
|
||||
if (state.layerCombinationActive &&
|
||||
window.confirm(`Kombination "${state.layerCombinationActive}" löschen?`))
|
||||
deleteLayerCombination(state.layerCombinationActive)
|
||||
return
|
||||
}
|
||||
pickLayerCombination(v === '__none__' ? null : v)
|
||||
}}
|
||||
style={{ ...pillSelect, width: 140 }}
|
||||
title={state.layerCombinationActive
|
||||
? `Aktive Kombi: ${state.layerCombinationActive}`
|
||||
: 'Keine Kombination — manuelle Sichtbarkeit'}
|
||||
>
|
||||
<option value="__none__">— Eigene —</option>
|
||||
{(state.layerCombinations || []).map(name => (
|
||||
<option key={name} value={name}>{name}</option>
|
||||
))}
|
||||
{state.layerCombinationActive && (
|
||||
<>
|
||||
<option disabled>──────────</option>
|
||||
<option value="__delete__">🗑 Aktuelle löschen</option>
|
||||
</>
|
||||
)}
|
||||
<option disabled>──────────</option>
|
||||
<option value="__configure__">Bearbeiten…</option>
|
||||
</select>
|
||||
<ToolButton
|
||||
onClick={() => {
|
||||
const suggested = state.layerCombinationActive
|
||||
|| `Kombi ${(state.layerCombinations || []).length + 1}`
|
||||
const name = (window.prompt('Name für Ebenenkombination:', suggested) || '').trim()
|
||||
if (!name) return
|
||||
if ((state.layerCombinations || []).includes(name) &&
|
||||
!window.confirm(`"${name}" überschreiben?`)) return
|
||||
saveLayerCombination(name)
|
||||
}}
|
||||
icon="add"
|
||||
title="Aktuelle Sichtbarkeit als neue Kombination speichern"
|
||||
/>
|
||||
<ToolButton
|
||||
onClick={openLayerCombinationsDialog}
|
||||
icon="edit"
|
||||
title="Ebenenkombinationen bearbeiten"
|
||||
/>
|
||||
{/* ====== STACK: Overrides + Kombi uebereinander ======
|
||||
Beide Zeilen haben identisches Spalten-Layout (Label-Spalte fix,
|
||||
Dropdown gleich breit), damit Dropdowns vertikal aligned sind. */}
|
||||
{(() => {
|
||||
const STACK_LABEL_W = 60 // gleich breit fuer beide Zeilen
|
||||
const STACK_DROPDOWN_W = 150
|
||||
const stackLabel = { ...groupLabel, width: STACK_LABEL_W,
|
||||
padding: 0, textAlign: 'left' }
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', gap: 4,
|
||||
flexShrink: 0,
|
||||
}}>
|
||||
{/* Overrides */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={stackLabel}>Overrides</span>
|
||||
<ToolButton
|
||||
onClick={() => toggleOverrides(!state.overridesEnabled)}
|
||||
active={state.overridesEnabled}
|
||||
icon="auto_fix_high"
|
||||
label={state.overridesEnabled ? 'AN' : 'AUS'}
|
||||
title={state.overridesEnabled
|
||||
? `Grafische Overrides aktiv — klick zum Ausschalten`
|
||||
: `Grafische Overrides ausgeschaltet`}
|
||||
/>
|
||||
<select
|
||||
value={state.overridesActivePreset || '__none__'}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
if (v === '__configure__') { openOverridesPanel(); return }
|
||||
setOverridesPreset(v === '__none__' ? null : v)
|
||||
}}
|
||||
style={{ ...pillSelect, width: STACK_DROPDOWN_W }}
|
||||
title={state.overridesActivePreset
|
||||
? `Aktives Preset: ${state.overridesActivePreset} (${state.overridesCount} Regeln)`
|
||||
: `Kein Preset aktiv (${state.overridesCount} Regeln, frei editiert)`}
|
||||
>
|
||||
<option value="__none__">{state.overridesCount > 0 ? `— (${state.overridesCount} Regeln)` : '—'}</option>
|
||||
{(state.overridesPresets || []).map(name => (
|
||||
<option key={name} value={name}>{name}</option>
|
||||
))}
|
||||
<option disabled>──────────</option>
|
||||
<option value="__configure__">Konfigurieren…</option>
|
||||
</select>
|
||||
<ToolButton
|
||||
onClick={openOverridesPanel}
|
||||
icon="settings"
|
||||
title="Overrides-Regel-Editor öffnen"
|
||||
/>
|
||||
</div>
|
||||
{/* Kombi */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={stackLabel}>Kombi</span>
|
||||
<ToolButton
|
||||
onClick={() => {
|
||||
const suggested = state.layerCombinationActive
|
||||
|| `Kombi ${(state.layerCombinations || []).length + 1}`
|
||||
const name = (window.prompt('Name für Ebenenkombination:', suggested) || '').trim()
|
||||
if (!name) return
|
||||
if ((state.layerCombinations || []).includes(name) &&
|
||||
!window.confirm(`"${name}" überschreiben?`)) return
|
||||
saveLayerCombination(name)
|
||||
}}
|
||||
icon="add"
|
||||
title="Aktuelle Sichtbarkeit als neue Kombination speichern"
|
||||
/>
|
||||
<select
|
||||
value={state.layerCombinationActive || '__none__'}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
if (v === '__configure__') { openLayerCombinationsDialog(); return }
|
||||
if (v === '__delete__') {
|
||||
if (state.layerCombinationActive &&
|
||||
window.confirm(`Kombination "${state.layerCombinationActive}" löschen?`))
|
||||
deleteLayerCombination(state.layerCombinationActive)
|
||||
return
|
||||
}
|
||||
pickLayerCombination(v === '__none__' ? null : v)
|
||||
}}
|
||||
style={{ ...pillSelect, width: STACK_DROPDOWN_W }}
|
||||
title={state.layerCombinationActive
|
||||
? `Aktive Kombi: ${state.layerCombinationActive}`
|
||||
: 'Keine Kombination — manuelle Sichtbarkeit'}
|
||||
>
|
||||
<option value="__none__">— Eigene —</option>
|
||||
{(state.layerCombinations || []).map(name => (
|
||||
<option key={name} value={name}>{name}</option>
|
||||
))}
|
||||
{state.layerCombinationActive && (
|
||||
<>
|
||||
<option disabled>──────────</option>
|
||||
<option value="__delete__">🗑 Aktuelle löschen</option>
|
||||
</>
|
||||
)}
|
||||
<option disabled>──────────</option>
|
||||
<option value="__configure__">Bearbeiten…</option>
|
||||
</select>
|
||||
<ToolButton
|
||||
onClick={openLayerCombinationsDialog}
|
||||
icon="edit"
|
||||
title="Ebenenkombinationen bearbeiten"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
|
||||
{/* Spacer am rechten Rand */}
|
||||
<div style={{ flex: 1 }} />
|
||||
|
||||
Reference in New Issue
Block a user