Schnitt/Ansicht-Feature + Terrain-Volumen + Geschoss-Add-Dialog
Schnitt-Feature V1+V2: - Neues rhino/schnitte.py mit Pick-Workflow, Activation (Clipping-Planes + Parallel-View), 2D-Plan-Symbol auf 18_Schnittlinien-Sublayer - Doppelklick auf Symbol aktiviert den Schnitt - Schnitt-Settings (cutAtLine/Tiefe/Höhen/Blickrichtung) im GeschossSettingsDialog - View-Snapshot + Restore beim Wechsel Schnitt → Geschoss - Symbol-Cleanup bei Delete via normalem Ebenen-Menü Terrain als Volumen: - swisstopo.volumize_terrain_object: Skirt + Bottom-Cap auf Mesh/Brep damit Clipping-Planes gefuellte Querschnitte erzeugen - UI im SwisstopoApp mit Nachbearbeitung-Section + Tiefen-Eingabe Geschoss-Add mit Dialog: - + im GeschossManager oeffnet 3-Optionen-Picker (Geschoss/Schnitt/Zeichnung) - Geschoss-Dialog mit Anker-Dropdown, Position über/unter, Auto-Name, Höhen-Prefill aus Anker Fix: _send_state fallback — Element gilt als selektiert wenn Source ODER Volume in der Selection ist (robust gegen Layer-Visibility wenn Referenz- linien-Layer im aktuellen Mode versteckt ist) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -39,12 +39,19 @@ export default function GeschossSettingsDialog({ geschoss, onSave, onClose, embe
|
||||
const [draft, setDraft] = useState({ ...geschoss })
|
||||
const set = (patch) => setDraft({ ...draft, ...patch })
|
||||
|
||||
const isG = !!draft.isGeschoss
|
||||
const hoehe = draft.hoehe ?? 3.0
|
||||
const schnitt = draft.schnitthoehe ?? 1.0
|
||||
const hasClip = !!draft.hasClipping
|
||||
const okff = draft.okff ?? 0
|
||||
const clipZ = (okff + schnitt).toFixed(2)
|
||||
const isG = !!draft.isGeschoss
|
||||
const isSchnitt = draft.type === 'schnitt'
|
||||
const hoehe = draft.hoehe ?? 3.0
|
||||
const schnitt = draft.schnitthoehe ?? 1.0
|
||||
const hasClip = !!draft.hasClipping
|
||||
const okff = draft.okff ?? 0
|
||||
const clipZ = (okff + schnitt).toFixed(2)
|
||||
// Schnitt-Felder
|
||||
const cutAtLine = draft.cutAtLine !== false // default true = Schnitt
|
||||
const depthBack = draft.depthBack ?? 8.0
|
||||
const heightMin = draft.heightMin ?? -1.0
|
||||
const heightMax = draft.heightMax ?? 12.0
|
||||
const dirSign = draft.dirSign ?? 1
|
||||
|
||||
// embedded=true: in einem Satelliten-Fenster gerendert — kein Backdrop,
|
||||
// keine Width-Constraint, fuellt das ganze WebView.
|
||||
@@ -103,14 +110,72 @@ export default function GeschossSettingsDialog({ geschoss, onSave, onClose, embe
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Toggle
|
||||
label="Ist Geschoss"
|
||||
checked={isG}
|
||||
onChange={(v) => set({ isGeschoss: v })}
|
||||
hint={isG ? 'Höhe & Clipping verfügbar' : 'reines Zeichenblatt'}
|
||||
/>
|
||||
{/* Geschoss-Toggle nur fuer non-schnitt Eintraege — Schnitt-Type
|
||||
ist exklusiv (kein Geschoss zugleich). */}
|
||||
{!isSchnitt && (
|
||||
<Toggle
|
||||
label="Ist Geschoss"
|
||||
checked={isG}
|
||||
onChange={(v) => set({ isGeschoss: v })}
|
||||
hint={isG ? 'Höhe & Clipping verfügbar' : 'reines Zeichenblatt'}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isG && (
|
||||
{isSchnitt && (
|
||||
<>
|
||||
<div style={{ height: 1, background: 'var(--border-light)', margin: '6px 0' }} />
|
||||
|
||||
<Toggle
|
||||
label="Front-Cut (Schnitt durchschneiden)"
|
||||
checked={cutAtLine}
|
||||
onChange={(v) => set({ cutAtLine: v })}
|
||||
hint={cutAtLine
|
||||
? 'Schnitt: alles vor der Schnittlinie wird weggeschnitten'
|
||||
: 'Ansicht: nur Tiefenbegrenzung hinten, kein Front-Cut'}
|
||||
/>
|
||||
|
||||
<Field label="TIEFE HINTEN (m)"
|
||||
hint="Wie weit hinter der Schnittlinie noch sichtbar ist">
|
||||
<input
|
||||
type="number" step="0.5" min="0.5"
|
||||
value={depthBack}
|
||||
onChange={(ev) => set({ depthBack: parseFloat(ev.target.value) || 8.0 })}
|
||||
style={{ flex: 1, fontSize: 11, textAlign: 'right', minWidth: 0 }}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<div style={{ display: 'flex', gap: 6 }}>
|
||||
<Field label="HÖHE UNTEN (m)">
|
||||
<input
|
||||
type="number" step="0.1"
|
||||
value={heightMin}
|
||||
onChange={(ev) => set({ heightMin: parseFloat(ev.target.value) })}
|
||||
style={{ flex: 1, fontSize: 11, textAlign: 'right', minWidth: 0 }}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="HÖHE OBEN (m)">
|
||||
<input
|
||||
type="number" step="0.1"
|
||||
value={heightMax}
|
||||
onChange={(ev) => set({ heightMax: parseFloat(ev.target.value) })}
|
||||
style={{ flex: 1, fontSize: 11, textAlign: 'right', minWidth: 0 }}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<Field label="BLICKRICHTUNG"
|
||||
hint="Wechselt zwischen den beiden Seiten der Schnittlinie">
|
||||
<button className={dirSign >= 0 ? 'btn-contained' : 'btn-outlined'}
|
||||
onClick={() => set({ dirSign: 1 })}
|
||||
style={{ flex: 1, fontSize: 11 }}>← Seite A</button>
|
||||
<button className={dirSign < 0 ? 'btn-contained' : 'btn-outlined'}
|
||||
onClick={() => set({ dirSign: -1 })}
|
||||
style={{ flex: 1, fontSize: 11 }}>Seite B →</button>
|
||||
</Field>
|
||||
</>
|
||||
)}
|
||||
|
||||
{isG && !isSchnitt && (
|
||||
<>
|
||||
<div style={{ height: 1, background: 'var(--border-light)', margin: '6px 0' }} />
|
||||
|
||||
@@ -180,6 +245,13 @@ export default function GeschossSettingsDialog({ geschoss, onSave, onClose, embe
|
||||
if (out.hoehe == null) out.hoehe = 3.0
|
||||
if (out.schnitthoehe == null) out.schnitthoehe = 1.0
|
||||
}
|
||||
if (out.type === 'schnitt') {
|
||||
if (out.depthBack == null) out.depthBack = 8.0
|
||||
if (out.heightMin == null) out.heightMin = -1.0
|
||||
if (out.heightMax == null) out.heightMax = 12.0
|
||||
if (out.dirSign == null) out.dirSign = 1
|
||||
if (out.cutAtLine == null) out.cutAtLine = true
|
||||
}
|
||||
onSave(out)
|
||||
}}>Übernehmen</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user