81 lines
7.1 KiB
Markdown
81 lines
7.1 KiB
Markdown
---
|
||
name: fire-visual-placement-mode
|
||
description: "fire-visual Placement-Mode (snap-to-point) — Architektur, Settings, Algorithmus, DropPoints, Commands; Pfade im threed/interaction- und threed/pmi-Modul"
|
||
metadata:
|
||
node_type: memory
|
||
type: project
|
||
originSessionId: 733f7054-ac76-49cd-ba04-516ef015d4f3
|
||
---
|
||
|
||
**WICHTIG: `fire-visual` ist KEIN eigener Code.** Es ist eine Kopie fremden Codes (Drittanbieter), die nur zum Reverse Engineering / Verstehen dient — nicht ändern, nicht als unser Produkt behandeln. Daraus gebaut wird das Paket `@ad/fire` (Repo `~/projects/geko-fire`, Remote `git.heron.at/pzm/geko/geko-fire.git`, nur Build: `browser/index.js` + `types/`, u.a. `import-fire-visual-modules.d.ts`). `fire-visual` selbst ist kein Git-Repo. adesso-cpq konsumiert `@ad/fire` als Dependency (`packages/frontend/package.json: "@ad/fire": "^1.0.6"`). Änderungen an der Visualisierung/Placement gehören NICHT hier hinein.
|
||
|
||
Placement-Mode = snap-to-point-Platzierung eines Subjects über Drag-Points/Drop-Points. Teil des Moduls `@fire-visual/interaction`. Quelle: `~/projects/fire-visual`.
|
||
|
||
## Pfade
|
||
- Mode: `threed/interaction/module/src/interactionMode/placementMode.ts`
|
||
- Verdrahtung/Command: `threed/interaction/module/src/feature/interactionFeature.ts` (ab Zeile 259)
|
||
- Options-Typen: `threed/interaction/module/src/options/modeOptions.ts`
|
||
- Modul-Doku: `threed/interaction/MODULE.md`
|
||
- Test: `threed/interaction/module/tests/placementMode.test.ts`
|
||
- DropPoint-Implementierungen (PMI): `threed/pmi/module/src/dropPoint/{pmiDropPoint,planeDropPoint,pointDropPoint,lineDropPoint}.ts`
|
||
- DropPoint-Factory: `threed/pmi/module/src/component/pmiComponent.ts` → `getDropPoint()` (Zeile 422)
|
||
|
||
## Grundbegriffe
|
||
- **Subject**: das zu platzierende Objekt (`Handle`).
|
||
- **Drag-Point**: Punkt am Subject (lokaler `transform: Matrix4_4`), an dem „angefasst" wird. Visualisiert als Kugel (Sphere). Aktiv = gelb, inaktiv = blau.
|
||
- **Drop-Point**: Ziel-Snap-Geometrie in der Szene; liefert über `assessRay` eine Bewertung. Implementiert Interface `DropPoint`.
|
||
- **activeDragPointIndex**: aktiver Drag-Point; `-1` = „alle Drag-Points" (nur erlaubt wenn `allowAllDragPoints !== false`). Start: `-1`, oder `0` falls `allowAllDragPoints === false`.
|
||
|
||
## PlacementModeSettings
|
||
- `camera: CameraComponent`, `subject: Handle`
|
||
- `dragPoints[]`: je `{ transform, dropPoints: Partial<DropPointProvider>[], selector? }`
|
||
- `defaultCursorTransform?` (Fallback = `dragPoints[0].transform`)
|
||
- `allowAllDragPoints?` (Default true) — false: Index -1 verboten, Cycling hält immer einen konkreten DP aktiv
|
||
- `allowDragPointCycling?` (Default true) — false: User kann aktiven DP nicht ändern, „alle DP" bleibt aktiv
|
||
- `maxJumpDistance?` — überschreibt globalen Wert aus `ModeOptionProvider`
|
||
|
||
## Globale Optionen (ModeOptionProvider.options.placement, Typ PlacementModeOptions)
|
||
`maxJumpDistance`, `highlightColor` (Default Color.yellow), `dragPointActiveColor` (Default yellow), `dragPointInactiveColor` (Default blue), `dragPointRadius` (Default 0.1). Plus `gizmoScale` (Default 1) skaliert Kugelradius.
|
||
|
||
## Algorithmus processInput(input) je Frame
|
||
Input: `pointerNormalizedPosition`, `nextDragPoint`, `previousDragPoint`, `interactionClick`.
|
||
1. Ohne Pointer/Camera → false.
|
||
2. **Grab-Gating**: Rising edge von `interactionClick` setzt `_grabActive = true`. Solange nicht grab-aktiv: nur prevClick merken, return true (Subject bewegt sich nicht).
|
||
3. Cycling: bei `nextDragPoint`/`previousDragPoint` Index zyklen (wenn Cycling erlaubt).
|
||
4. Alle Drop-Point-Visualisierungen zurücksetzen (erst `false`+Highlight null), dann die zum aktiven DP gehörenden aktivieren (Index -1 ⇒ alle).
|
||
5. Ray von Kamera durch Pointer; Schnitt mit XZ-Ebene (`Plane` durch Ursprung, Normale up) → `intersectionPoint`.
|
||
6. Für jeden aktiven Drag-Point × dessen Drop-Points: Ray um `dropPoint.predictDragPointRotation(ray) * (dragPoint.transform⁻¹ * cursorTransform) * 0` versetzen, dann `dropPoint.assessRay(offsetRay)` → RayAssessment.
|
||
7. Beste Assessment über `betterRayAssessment`: kleinste `jumpDistance`; bei Gleichstand (Toleranz 0.05) kleinste `rayDistance`.
|
||
8. Wenn `best.jumpDistance < maxJumpDistance`: Drop-Point highlighten und Subject snappen: `subject.matrix = compose(best.position, best.rotation, 1) * dragPointTransform⁻¹`. Sonst: Subject auf `intersectionPoint` auf XZ-Ebene setzen (`* cursorTransform⁻¹`), rotation identity.
|
||
9. `_lastPlacement` (PlacementUpdateData) merken: position, rotation, dragPointSelector, dropPointSelector (jeweils null bei XZ-Fallback).
|
||
10. Drag-Point-Kugeln neu positionieren (`subjectMatrix * dragPoint.transform * 0`), Material gelb/blau je aktiv.
|
||
11. **Falling edge** von `interactionClick`: `_grabActive = false`, `_lastPlacement` per `emitUpdateEvent` (subject als selectorElementReferenceRepresentation) ausgeben.
|
||
|
||
## Cycling-Logik
|
||
- allowAllDragPoints true: Reihenfolge … last → -1 (alle) → 0 → … (wrap über -1).
|
||
- allowAllDragPoints false: -1 wird übersprungen, Wrap last → 0.
|
||
|
||
## Lifecycle
|
||
- `startMode`: Drop-Points je Drag-Point aus Providern extrahieren (`getDropPoint()`), Drag-Point-Visuals (Sphere-Handles, BasicMaterial transparent opacity 0.75) per `addInteractionScene` erzeugen, Handles via `find('.drag-point-i')`.
|
||
- `stopMode`: dropPoints/dragPointHandles leeren.
|
||
|
||
## Command (Environment): start_placement_mode(params)
|
||
params: `subject: string`, `cursorTransform?`, `allowAllDragPoints?`, `allowDragPointCycling?`, `maxJumpDistance?`, `dragPoints: Array<{ selector?, transform?:{position,rotation}, dropPoints: string[] }>`.
|
||
Auflösung in interactionFeature:
|
||
- Subject-Selector → Handle (wirft wenn nicht gefunden).
|
||
- Pro Drag-Point: `dropPoints`-Selektoren via `selectorEngine.findAll` → DropPointProvider.
|
||
- Ohne `selector`: ein Drag-Point mit `transform` (aus position/rotation, sonst identity), selector undefined.
|
||
- Mit `selector`: `findAll(selector, subjectHandle)` → je Sub-Entity ein Drag-Point mit `transform = subject.LTW⁻¹ * dpe.LTW * transform`, selector = `selectorEngine.selector(dpe)`.
|
||
Weitere Commands: `placement_mode_next_drag_point`, `placement_mode_previous_drag_point` (werfen TypeError wenn aktueller Mode kein PlacementMode), `stop_interaction_mode`, `observe_interaction_events`.
|
||
|
||
## DropPoint-Interface
|
||
`assessRay(ray): RayAssessment {jumpDistance, rayDistance, position, rotation}`, `predictDragPointRotation(ray): Matrix4_4`, `setVisualization(state)`, `setVisualizationHighlight(color|null)`, `selector(): string|null`.
|
||
|
||
PMI-Implementierungen (`PmiDropPoint` Basis, hält `LTW`, `dragPointRotation = decompose(LTW)[1]`, predictRotation = konstant):
|
||
- **PointDropPoint** (PMIType point/matrix): position = LTW·0; jumpDistance = Abstand Punkt↔nächster Punkt auf Strahl.
|
||
- **LineDropPoint** (line): Segment LTW·0 → parentLTW·endpoint; closest segment-on-line.
|
||
- **PlaneDropPoint** (plane): Ebene aus LTW; Ray-Schnitt, in Ebenen-Koords auf `size`/2 geclampt, zurück nach Welt; jumpDistance = clampedPoint↔rayPoint.
|
||
`PMIComponent.getDropPoint()` mappt PMIType → Klasse (point/matrix→Point, line→Line, plane→Plane, sonst null).
|
||
|
||
Verwandt: [[cpq-verbau-architektur]] (CPQ-seitige Verbau/Platzierung).
|