/** * Utility functions and constants for StepStructureDetection. * Pure logic — no React, no 'use client' needed. */ export const KLAUSUR_API = '/klausur-api' export const COLOR_HEX: Record = { red: '#dc2626', orange: '#ea580c', yellow: '#ca8a04', green: '#16a34a', blue: '#2563eb', purple: '#9333ea', } export type DetectionMethod = 'auto' | 'opencv' | 'ppdoclayout' /** Color map for PP-DocLayout region classes */ export const DOCLAYOUT_CLASS_COLORS: Record = { table: '#2563eb', figure: '#16a34a', title: '#ea580c', text: '#6b7280', list: '#9333ea', header: '#0ea5e9', footer: '#64748b', equation: '#dc2626', } const DOCLAYOUT_DEFAULT_COLOR = '#a3a3a3' export function getDocLayoutColor(className: string): string { return DOCLAYOUT_CLASS_COLORS[className.toLowerCase()] || DOCLAYOUT_DEFAULT_COLOR } /** * Convert a mouse event on the image container to image-pixel coordinates. * The image uses object-contain inside an A4-ratio container, so we need * to account for letterboxing. */ export function mouseToImageCoords( e: React.MouseEvent, containerEl: HTMLElement, imgWidth: number, imgHeight: number, ): { x: number; y: number } | null { const rect = containerEl.getBoundingClientRect() const containerW = rect.width const containerH = rect.height // object-contain: image is scaled to fit, centered const scaleX = containerW / imgWidth const scaleY = containerH / imgHeight const scale = Math.min(scaleX, scaleY) const renderedW = imgWidth * scale const renderedH = imgHeight * scale const offsetX = (containerW - renderedW) / 2 const offsetY = (containerH - renderedH) / 2 const relX = e.clientX - rect.left - offsetX const relY = e.clientY - rect.top - offsetY if (relX < 0 || relY < 0 || relX > renderedW || relY > renderedH) { return null } return { x: Math.round(relX / scale), y: Math.round(relY / scale), } } /** * Convert image-pixel coordinates to container-relative percentages * for overlay positioning. */ export function imageToOverlayPct( region: { x: number; y: number; w: number; h: number }, containerW: number, containerH: number, imgWidth: number, imgHeight: number, ): { left: string; top: string; width: string; height: string } { const scaleX = containerW / imgWidth const scaleY = containerH / imgHeight const scale = Math.min(scaleX, scaleY) const renderedW = imgWidth * scale const renderedH = imgHeight * scale const offsetX = (containerW - renderedW) / 2 const offsetY = (containerH - renderedH) / 2 const left = offsetX + region.x * scale const top = offsetY + region.y * scale const width = region.w * scale const height = region.h * scale return { left: `${(left / containerW) * 100}%`, top: `${(top / containerH) * 100}%`, width: `${(width / containerW) * 100}%`, height: `${(height / containerH) * 100}%`, } }