From e18d9a5796671e9130c1dec4687dd9613d8918d2 Mon Sep 17 00:00:00 2001 From: admin Date: Sat, 25 Apr 2026 21:36:22 +0200 Subject: [PATCH] bbox/polygon durch selections ersetzen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - bbox und polygon Felder in Directus versteckt (Daten bleiben) - Alle Auswahlen laufen nur noch über das selections-Feld - CanvasObject, DirectusObject, API und Zeichenlogik umgestellt - Objekte mit mehreren Auswahlen werden korrekt auf Canvas gezeichnet Co-Authored-By: Claude Sonnet 4.6 --- app.py | 8 +--- frontend/src/api.ts | 6 +-- frontend/src/components/DrawCanvas.tsx | 51 ++++++++++++-------------- frontend/src/pages/DrawIt.tsx | 8 +--- frontend/src/types.ts | 5 +-- 5 files changed, 31 insertions(+), 47 deletions(-) diff --git a/app.py b/app.py index 3a450db..004d9f5 100644 --- a/app.py +++ b/app.py @@ -79,7 +79,7 @@ def directus_objects(): token = request.headers.get("Authorization", "") if request.method == "GET": picture_id = request.args.get("picture_id", "") - fields = "id,bbox,polygon,selections,user_notes,parent,status,picture" + fields = "id,selections,user_notes,parent,status,picture" path = f"/items/objects?filter[picture][_eq]={picture_id}&fields={fields}&sort=date_created" data, status = _directus("GET", path, token) return jsonify(data), status @@ -771,8 +771,6 @@ def crop_image(): "id": obj_id, "created_at": timestamp, "source_filename": data["filename"], - "mode": processed_selections[0]["mode"], # Legacy-Feld - "bbox": processed_selections[0]["bbox"], # Legacy-Feld "image_file": out_image_name, "hierarchy": 1, "parent_id": None, @@ -780,10 +778,8 @@ def crop_image(): "position_de": data.get("position_de", ""), "action_de": data.get("action_de", ""), "condition_de": data.get("condition_de", ""), - "selections": processed_selections, # Neue Feld: alle Auswahlen nummeriert + "selections": processed_selections, } - if processed_selections[0]["mode"] == "polygon": - meta["polygon"] = processed_selections[0]["polygon"] # Legacy-Feld else: # Legacy-Format: einzelne Auswahl (für Rückwärtskompatibilität) diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 45c10bb..c02c140 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -32,7 +32,7 @@ export function directusAssetUrl(mediaId: string, token: string): string { return `${DIRECTUS_URL}/assets/${mediaId}?access_token=${token}` } -import type { DirectusObject, BBox, Point } from './types' +import type { DirectusObject, Selection } from './types' export async function getDirectusObjects(pictureId: string, token: string): Promise { const res = await fetch(`/api/directus/objects?picture_id=${pictureId}`, { @@ -45,9 +45,7 @@ export async function getDirectusObjects(pictureId: string, token: string): Prom export async function createDirectusObject(payload: { picture: string - bbox: BBox | null - polygon: Point[] | null - selections: import('./types').Selection[] | null + selections: Selection[] | null user_notes: string | null parent: string | null }, token: string): Promise { diff --git a/frontend/src/components/DrawCanvas.tsx b/frontend/src/components/DrawCanvas.tsx index 7f6d8a5..9a01f1a 100644 --- a/frontend/src/components/DrawCanvas.tsx +++ b/frontend/src/components/DrawCanvas.tsx @@ -81,45 +81,42 @@ export default forwardRef(function DrawCanvas( ctx.lineWidth = isSelected ? 3 : 2 ctx.setLineDash(isSelected ? [2, 2] : [4, 3]) - const { polygon, bbox } = obj - if (polygon && polygon.length >= 3) { - ctx.beginPath() - ctx.moveTo(polygon[0].x * scale, polygon[0].y * scale) - for (let i = 1; i < polygon.length; i++) ctx.lineTo(polygon[i].x * scale, polygon[i].y * scale) - ctx.closePath() - ctx.fill() - ctx.stroke() - } else if (bbox) { - ctx.fillRect(bbox.x * scale, bbox.y * scale, bbox.width * scale, bbox.height * scale) - ctx.strokeRect(bbox.x * scale, bbox.y * scale, bbox.width * scale, bbox.height * scale) + const drawShape = (sel: { mode: string; bbox?: { x: number; y: number; width: number; height: number } | null; polygon?: { x: number; y: number }[] | null }) => { + const { polygon, bbox } = sel as { polygon?: { x: number; y: number }[] | null; bbox?: { x: number; y: number; width: number; height: number } | null } + if (polygon && polygon.length >= 3) { + ctx.beginPath() + ctx.moveTo(polygon[0].x * scale, polygon[0].y * scale) + for (let i = 1; i < polygon.length; i++) ctx.lineTo(polygon[i].x * scale, polygon[i].y * scale) + ctx.closePath() + ctx.fill() + ctx.stroke() + } else if (bbox) { + ctx.fillRect(bbox.x * scale, bbox.y * scale, bbox.width * scale, bbox.height * scale) + ctx.strokeRect(bbox.x * scale, bbox.y * scale, bbox.width * scale, bbox.height * scale) + } } + for (const sel of obj.selections ?? []) drawShape(sel) + // White highlight ring for selected object if (isSelected) { ctx.strokeStyle = '#ffffff' ctx.lineWidth = 2 ctx.setLineDash([]) - if (polygon && polygon.length >= 3) { - ctx.beginPath() - ctx.moveTo(polygon[0].x * scale, polygon[0].y * scale) - for (let i = 1; i < polygon.length; i++) ctx.lineTo(polygon[i].x * scale, polygon[i].y * scale) - ctx.closePath() - ctx.stroke() - } else if (bbox) { - ctx.strokeRect(bbox.x * scale, bbox.y * scale, bbox.width * scale, bbox.height * scale) - } + for (const sel of obj.selections ?? []) drawShape(sel) } - // Index number in object center + // Index number: center of first selection const indexLabel = typeof obj.index === 'number' ? String(obj.index) : '' if (indexLabel) { + const first = obj.selections?.[0] as { polygon?: { x: number; y: number }[] | null; bbox?: { x: number; y: number; width: number; height: number } | null } | undefined let cx = 0, cy = 0 - if (bbox) { - cx = (bbox.x + bbox.width / 2) * scale - cy = (bbox.y + bbox.height / 2) * scale - } else if (polygon && polygon.length > 0) { - const xs = polygon.map(p => p.x) - const ys = polygon.map(p => p.y) + if (first?.bbox) { + cx = (first.bbox.x + first.bbox.width / 2) * scale + cy = (first.bbox.y + first.bbox.height / 2) * scale + } else if (first?.polygon && first.polygon.length > 0) { + const xs = first.polygon.map((p: { x: number; y: number }) => p.x) + const ys = first.polygon.map((p: { x: number; y: number }) => p.y) cx = ((Math.min(...xs) + Math.max(...xs)) / 2) * scale cy = ((Math.min(...ys) + Math.max(...ys)) / 2) * scale } diff --git a/frontend/src/pages/DrawIt.tsx b/frontend/src/pages/DrawIt.tsx index ef86229..9221538 100644 --- a/frontend/src/pages/DrawIt.tsx +++ b/frontend/src/pages/DrawIt.tsx @@ -50,8 +50,7 @@ export default function DrawIt() { const canvasObjects: CanvasObject[] = objects.map((obj, i) => ({ id: obj.id, visible: obj.visible !== false, - bbox: obj.bbox, - polygon: obj.polygon, + selections: obj.selections, index: i + 1, hierarchy: 1, })) @@ -88,11 +87,8 @@ export default function DrawIt() { if (!currentPicture || !token || currentSelections.length === 0) return setSaving(true) try { - const first = currentSelections[0] const obj = await createDirectusObject({ picture: currentPicture.id, - bbox: first.mode === 'rect' ? (first.bbox ?? null) : null, - polygon: first.mode === 'polygon' ? (first.polygon ?? null) : null, selections: currentSelections, user_notes: userNotes.trim() || null, parent: parentId, @@ -182,7 +178,7 @@ export default function DrawIt() { />
Objekt {i + 1} - {obj.bbox ? `Rect ${Math.round(obj.bbox.width)}×${Math.round(obj.bbox.height)}` : obj.polygon ? `Poly ${obj.polygon.length} Pkt.` : '–'} + {obj.selections?.length ? `${obj.selections.length} Auswahl(en)` : '–'} {obj.parent && ↳ Kind von #{objects.findIndex(o => o.id === obj.parent) + 1}}