task(C3fin): selection handles and snap marks in pixi renderer

This commit is contained in:
Agent Zero
2026-08-27 18:37:09 +02:00
parent 0abfc3e0be
commit 129ddcc61f
2 changed files with 107 additions and 4 deletions
+105 -4
View File
@@ -217,12 +217,113 @@ export class PixiRenderer implements RenderAdapter {
} }
} }
setSelection(_sel: SelectionState): void { setSelection(sel: SelectionState): void {
// Selektionshandles in C3 if (!this.overlayLayer) return;
// Bestehende Selektions-Grafik entfernen:
for (const child of [...this.overlayLayer.children]) {
if ((child as Container).label === '__selection__') {
try {
child.destroy({ children: true });
} catch {
// teardown-safe
}
}
}
if (!sel.selectedIds.length || !sel.elements || !this.pixiModule) return;
const GraphicsCtor = this.pixiModule.Graphics;
const g = new GraphicsCtor();
const HANDLE = 4; // halbe Kantenlänge der Eck-Handles (in Weltkoordinaten)
for (const el of sel.elements) {
if (!sel.selectedIds.includes(el.id)) continue;
// Legacy-Konvention getElementBBox: x/y = Zentrum für rects, sonst BBox
let minX: number, minY: number, maxX: number, maxY: number;
if (el.type === 'rect' || el.type === 'stage') {
minX = el.x - el.width / 2;
minY = el.y - el.height / 2;
maxX = el.x + el.width / 2;
maxY = el.y + el.height / 2;
} else if (
el.properties &&
el.properties.x1 !== undefined && el.properties.y1 !== undefined &&
el.properties.x2 !== undefined && el.properties.y2 !== undefined
) {
minX = Math.min(el.properties.x1 as number, el.properties.x2 as number);
minY = Math.min(el.properties.y1 as number, el.properties.y2 as number);
maxX = Math.max(el.properties.x1 as number, el.properties.x2 as number);
maxY = Math.max(el.properties.y1 as number, el.properties.y2 as number);
} else {
minX = el.x - el.width / 2;
minY = el.y - el.height / 2;
maxX = el.x + el.width / 2;
maxY = el.y + el.height / 2;
}
// Umrandung (gestrichelt wird via Linien-Segmenten simuliert):
const dashLen = 6;
const drawDashed = (ax: number, ay: number, bx: number, by: number) => {
const len = Math.hypot(bx - ax, by - ay);
const n = Math.max(1, Math.floor(len / dashLen));
for (let i = 0; i <= n; i += 2) {
const t0 = i / n;
const t1 = Math.min(1, (i + 1) / n);
g.moveTo(ax + (bx - ax) * t0, ay + (by - ay) * t0)
.lineTo(ax + (bx - ax) * t1, ay + (by - ay) * t1)
.stroke({ color: '#50fa7b', width: 1 / this.viewport.scale });
}
};
drawDashed(minX, minY, maxX, minY);
drawDashed(maxX, minY, maxX, maxY);
drawDashed(maxX, maxY, minX, maxY);
drawDashed(minX, maxY, minX, minY);
// 4 Eck-Handles (gefüllte Quadrate):
const corners = [
{ x: minX, y: minY }, { x: maxX, y: minY },
{ x: maxX, y: maxY }, { x: minX, y: maxY },
];
for (const c of corners) {
g.rect(c.x - HANDLE, c.y - HANDLE, HANDLE * 2, HANDLE * 2)
.fill({ color: '#50fa7b' })
.stroke({ color: '#1e1e2e', width: 1 / this.viewport.scale });
}
}
g.label = '__selection__';
this.overlayLayer.addChild(g);
} }
setSnapPoints(_pts: Array<{ x: number; y: number; type: string }>): void { setSnapPoints(pts: Array<{ x: number; y: number; type: string }>): void {
// Snap-Markierungen in C3 if (!this.overlayLayer || !this.pixiModule || pts.length === 0) return;
const GraphicsCtor = this.pixiModule.Graphics;
const g = new GraphicsCtor();
g.label = '__snaps__';
// Bestehende Snap-Marks entfernen:
for (const child of [...this.overlayLayer.children]) {
if ((child as Container).label === '__snaps__') {
try {
child.destroy({ children: true });
} catch {
// teardown-safe
}
}
}
// Snap-Punkt farbig nach Typ (Legacy-Ähnlich):
const colorByType: Record<string, string> = {
endpoint: '#ff79c6',
midpoint: '#8be9fd',
center: '#f1fa8c',
intersection: '#ffb86c',
grid: '#6272a4',
};
const scaleComp = 1 / this.viewport.scale; // konstante Bildschirmgröße
for (const pt of pts) {
const color = colorByType[pt.type] ?? '#bd93f9';
g.circle(pt.x, pt.y, 4 * scaleComp)
.fill({ color })
.stroke({ color: '#1e1e2e', width: 1 * scaleComp });
}
this.overlayLayer.addChild(g);
} }
overlay(draw: (g: OverlayPainter) => void): void { overlay(draw: (g: OverlayPainter) => void): void {
+2
View File
@@ -64,6 +64,8 @@ export interface OverlayPainter {
/** Selektionszustand für die Anzeige von Handles. */ /** Selektionszustand für die Anzeige von Handles. */
export interface SelectionState { export interface SelectionState {
selectedIds: string[]; selectedIds: string[];
/** Task C3-Finish: Elemente für Handle-Zeichnung (Renderer braucht BBox). */
elements?: CADElement[];
} }
/** /**