Ribbon 3D-Tab: Kamera-Presets + Darstellungsart; truck-Integration geplant 2026-07-07
This commit is contained in:
@@ -18,6 +18,10 @@
|
||||
|
||||
---
|
||||
|
||||
## 📅 Geplant
|
||||
|
||||
- [ ] **truck-Integration (Profil-Extrusion / B-Rep)** — **Start: 2026-07-07 (Dienstag, nach Reset).** Nutzer zeichnet 2D-Querschnitt (L-Profil, T-Träger, Freiform) → truck-Extrusion → 3D-Körper + Boolean gegen Wand/Decke. Voraussetzung: truck-Booleans stabil + WASM-Integration. Umfang: ~4–6 Wochen. Mit Nutzer Scope/MVP klären vor Start.
|
||||
|
||||
## ⛔ Blocker (zuerst klären)
|
||||
|
||||
- [x] ~~**Toolchain prüfen**~~ — **ERLEDIGT 2026-07-04 (macOS-Gerät):** `node`/`npm`/`cargo` (1.96.0)/`wasm-pack` (0.15.0) alle vorhanden. `npm install` + `npm approve-scripts esbuild wasm-pack …` (npm gated Install-Scripts → `allowScripts`-Block in `package.json`, ungetrackt gelassen), `rustup target add wasm32-unknown-unknown`, beide WASM-Engines gebaut, Frontend gebaut, **`npm run tauri:build` → Mac-App (`cad.app` + `cad_0.1.0_aarch64.dmg`, arm64) läuft.** Verifikations-Baseline grün: `tsc --noEmit` sauber, `vitest run` 230/230, `cargo test render3d` 56/56. **Kein Blocker mehr für Engine-Slices auf diesem Gerät.**
|
||||
|
||||
+15
-1
@@ -69,7 +69,7 @@ import type { Pick3dHit } from "./viewport/Wasm3DViewport";
|
||||
import { ResourceManager } from "./ui/ResourceManager";
|
||||
import type { ResourceManagerHandlers } from "./ui/ResourceManager";
|
||||
import { HatchSwatch } from "./ui/hatchPreview";
|
||||
import { TopBar, ViewRibbonTab } from "./ui/TopBar";
|
||||
import { TopBar, ViewRibbonTab, Ribbon3dTab } from "./ui/TopBar";
|
||||
import { RibbonBar } from "./ui/ribbon/RibbonBar";
|
||||
import type { RibbonTabId } from "./ui/ribbon/ribbonItems";
|
||||
import { Dropdown } from "./ui/Dropdown";
|
||||
@@ -3320,6 +3320,20 @@ export default function App() {
|
||||
textTarget={textTarget}
|
||||
/>
|
||||
),
|
||||
"3d": (
|
||||
<Ribbon3dTab
|
||||
viewToggleEnabled={activeLevel.kind === "floor"}
|
||||
viewType={viewType}
|
||||
onViewType={setViewType}
|
||||
view3d={view3d}
|
||||
onView3d={setView3d}
|
||||
fov={fov}
|
||||
onFov={setFov}
|
||||
renderMode={renderMode}
|
||||
onRenderMode={setRenderMode}
|
||||
renderModeEnabled={viewType === "perspektive"}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -1262,6 +1262,99 @@ export function ViewRibbonTab({
|
||||
);
|
||||
}
|
||||
|
||||
export interface Ribbon3dTabProps {
|
||||
viewToggleEnabled: boolean;
|
||||
viewType: ViewType;
|
||||
onViewType: (v: ViewType) => void;
|
||||
view3d: View3d;
|
||||
onView3d: (v: View3d) => void;
|
||||
fov: number;
|
||||
onFov: (n: number) => void;
|
||||
renderMode: RenderMode;
|
||||
onRenderMode: (m: RenderMode) => void;
|
||||
renderModeEnabled: boolean;
|
||||
}
|
||||
|
||||
/** 3D-Tab des Ribbons — Kamera-Presets + Darstellungsart. Kein Duplikat des
|
||||
* Ansichten-Tabs: hier nur 3D-relevante Steuerung (Blickwinkel, Rendermodus). */
|
||||
export function Ribbon3dTab({
|
||||
viewToggleEnabled,
|
||||
viewType,
|
||||
onViewType,
|
||||
view3d,
|
||||
onView3d,
|
||||
fov,
|
||||
onFov,
|
||||
renderMode,
|
||||
onRenderMode,
|
||||
renderModeEnabled,
|
||||
}: Ribbon3dTabProps) {
|
||||
return (
|
||||
<div className="ribbon-views">
|
||||
<div className="tb-group" role="group" aria-label={t("topbar.viewGroup")}>
|
||||
<div className="view-grid view-grid-4">
|
||||
<button
|
||||
className={`view-icon${viewToggleEnabled && viewType === "grundriss" ? " active" : ""}`}
|
||||
onClick={() => onViewType("grundriss")}
|
||||
disabled={!viewToggleEnabled}
|
||||
title={viewToggleEnabled ? t("topbar.grundriss") : t("topbar.viewGroup.disabled")}
|
||||
aria-label={t("topbar.grundriss")}
|
||||
>
|
||||
<ViewIcon name="grundriss" />
|
||||
</button>
|
||||
{(
|
||||
[
|
||||
["top", t("view3d.top")],
|
||||
["iso", t("view3d.iso")],
|
||||
["perspective", t("view3d.perspective")],
|
||||
["front", t("view3d.front")],
|
||||
["side", t("view3d.side")],
|
||||
] as [View3d, string][]
|
||||
).map(([key, label]) => (
|
||||
<button
|
||||
key={key}
|
||||
className={`view-icon${viewToggleEnabled && viewType === "perspektive" && view3d === key ? " active" : ""}`}
|
||||
onClick={() => {
|
||||
onView3d(key);
|
||||
if (viewType !== "perspektive") onViewType("perspektive");
|
||||
}}
|
||||
disabled={!viewToggleEnabled}
|
||||
title={viewToggleEnabled ? `${label} — ${t(`view3d.${key}.hint`)}` : t("topbar.viewGroup.disabled")}
|
||||
aria-label={label}
|
||||
>
|
||||
<ViewIcon name={key} />
|
||||
</button>
|
||||
))}
|
||||
<CameraMenu
|
||||
fov={fov}
|
||||
onFov={onFov}
|
||||
disabled={!viewToggleEnabled || viewType !== "perspektive"}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Sep />
|
||||
<div className="tb-group" role="group" aria-label={t("display.style.group")}>
|
||||
<label className="tb-field">
|
||||
<span className="tb-label">{t("display.style")}</span>
|
||||
<Dropdown
|
||||
value={renderMode}
|
||||
disabled={!renderModeEnabled}
|
||||
onChange={(v) => onRenderMode(v as RenderMode)}
|
||||
title={t("display.style.hint")}
|
||||
options={[
|
||||
{ value: "shaded", label: t("display.style.shaded") },
|
||||
{ value: "white", label: t("display.style.white") },
|
||||
{ value: "textured", label: t("display.style.textured") },
|
||||
{ value: "wireframe", label: t("display.style.wireframe") },
|
||||
{ value: "hidden", label: t("display.style.hidden") },
|
||||
]}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function TopBar({
|
||||
project,
|
||||
ribbonTab,
|
||||
|
||||
Reference in New Issue
Block a user