Files
claude-robotunits/memory/fire-visual-placement-mode.md
2026-07-09 09:21:49 +02:00

7.1 KiB
Raw Permalink Blame History

name, description, metadata
name description metadata
fire-visual-placement-mode fire-visual Placement-Mode (snap-to-point) — Architektur, Settings, Algorithmus, DropPoints, Commands; Pfade im threed/interaction- und threed/pmi-Modul
node_type type originSessionId
memory project 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.tsgetDropPoint() (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).