diff --git a/frontend/src/components/CourtDrawingRender.vue b/frontend/src/components/CourtDrawingRender.vue index c8a6b67..0f392b5 100644 --- a/frontend/src/components/CourtDrawingRender.vue +++ b/frontend/src/components/CourtDrawingRender.vue @@ -133,6 +133,14 @@ export default { this.drawTable(); this.drawStartCircles(); this.drawArrowsAndLabels(); + // Zuletzt: Overlay mit dem gerenderten Kürzel-String + const code = this.buildCodeString(); + if (code) { + this.drawOverlayText(code); + } + // Debug: Rohwerte anzeigen + const raw = `raw: start=${this.drawingData?.selectedStartPosition || '-'} circle=${this.drawingData?.selectedCirclePosition || '-'}`; + this.drawOverlayText(raw, 2); }, drawTable() { const ctx = this.ctx; @@ -221,9 +229,7 @@ export default { const botY = tableY + tableHeight - cfg.bottomYOffset; // Mapping und Fallback: bei "AS" auf 'middle' - const map = { AS1: 'top', AS2: 'middle', AS3: 'bottom', AS: 'middle' }; - const selKey = this.drawingData?.selectedStartPosition || 'AS'; - const selectedPos = map[selKey] || 'middle'; + const selectedPos = this.resolveStartPos(); const y = selectedPos === 'top' ? topY : selectedPos === 'bottom' ? botY : midY; ctx.fillStyle = cfg.selectedColor; @@ -269,8 +275,7 @@ export default { const tableX = (this.config.canvas.width - tblW) / 2; const tableY = (this.config.canvas.height - tblH) / 2; const circleX = tableX - sc.x; // Kreis links vor dem Tisch - const map = { AS1: 'top', AS2: 'middle', AS3: 'bottom', AS: 'middle' }; - const pos = map[this.drawingData?.selectedStartPosition] || 'middle'; + const pos = this.resolveStartPos(); const y = pos === 'top' ? tableY + sc.topYOffset : pos === 'bottom' ? tableY + tblH - sc.bottomYOffset : tableY + tblH / 2; const isVH = (this.drawingData?.strokeType || 'VH') === 'VH'; const startX = isVH @@ -373,6 +378,97 @@ export default { } } }, + applyRenderCode(code) { + try { + const startMatch = code.match(/ASv([LMR])/); + if (startMatch) { + const map = { L: 'AS1', M: 'AS2', R: 'AS3' }; + this.drawingData.selectedStartPosition = map[startMatch[1]] || 'AS2'; + this.drawingData.selectedCirclePosition = this.drawingData.selectedStartPosition === 'AS1' ? 'top' : this.drawingData.selectedStartPosition === 'AS3' ? 'bottom' : 'middle'; + } + // Hauptziel extrahieren (nach dem ersten → ) + const arrowParts = code.split('→').map(s => s.trim()); + if (arrowParts.length >= 2) { + const mainLabel = arrowParts[1].split('/')[0].trim(); + const mainNum = this.labelToNumber(mainLabel); + if (mainNum) { + this.drawingData.targetPosition = String(mainNum); + } + } + // Zusatzschläge: Segmente nach '/' + const extras = []; + if (code.includes('/')) { + const extraSegments = code.split('/').slice(1); + extraSegments.forEach(seg => { + const m = seg.split('→')[1]; + if (m) { + const lbl = m.trim(); + const num = this.labelToNumber(lbl); + if (num) { + extras.push({ targetPosition: String(num) }); + } + } + }); + } + this.drawingData.additionalStrokes = extras; + } catch (e) { + // still draw with whatever is present + } + }, + labelToNumber(label) { + const map = { + 'VH L': 1, 'M L': 2, 'RH L': 3, + 'VH H': 4, 'M H': 5, 'RH H': 6, + 'VH K': 7, 'M K': 8, 'RH K': 9 + }; + return map[label] || null; + }, + buildCodeString() { + if (!this.drawingData) return ''; + // Wenn ein expliziter renderCode mitgeliefert wird, verwende den + if (this.drawingData && (this.drawingData.code || this.drawingData.renderCode)) { + return this.drawingData.code || this.drawingData.renderCode; + } + // sonst: Nutze die gleiche Logik wie beim Zeichnen (resolveStartPos) + const pos = this.resolveStartPos(); // 'top' | 'middle' | 'bottom' + const start = pos === 'top' ? 'vL' : pos === 'bottom' ? 'vR' : 'vM'; + const strokeSide = this.drawingData.strokeType || ''; + const spin = this.abbrevSpin(this.drawingData.spinType); + const mainTargetMap = { + '1': 'VH L', '2': 'M L', '3': 'RH L', + '4': 'VH H', '5': 'M H', '6': 'RH H', + '7': 'VH K', '8': 'M K', '9': 'RH K' + }; + let code = `AS${start} ${strokeSide} ${spin}`.trim(); + if (this.drawingData.targetPosition) { + const main = mainTargetMap[String(this.drawingData.targetPosition)] || this.drawingData.targetPosition; + code += ` → ${main}`; + } + const extras = Array.isArray(this.drawingData.additionalStrokes) ? this.drawingData.additionalStrokes : []; + if (extras.length) { + extras.forEach(stroke => { + const tgt = mainTargetMap[String(stroke.targetPosition)] || stroke.targetPosition; + code += ` / ${stroke.side} ${stroke.type} → ${tgt}`; + }); + } + return code; + }, + drawOverlayText(text, line = 1) { + const ctx = this.ctx; + const padding = 6; + const x = 10; + const y = this.config.canvas.height - 16 - (line - 1) * 22; + ctx.save(); + ctx.font = '12px Arial'; + const width = ctx.measureText(text).width; + const boxW = width + padding * 2; + const boxH = 16 + padding * 2; + ctx.fillStyle = 'rgba(0,0,0,0.6)'; + ctx.fillRect(x - padding, y - 12 - padding, boxW, boxH); + ctx.fillStyle = '#ffffff'; + ctx.fillText(text, x, y); + ctx.restore(); + }, getStartCircleCenter() { const cfg = this.config.startCircles; const tableWidth = this.config.table.width; @@ -380,15 +476,25 @@ export default { const tableX = (this.config.canvas.width - tableWidth) / 2; const tableY = (this.config.canvas.height - tableHeight) / 2; const circleX = tableX - cfg.x; - const map = { AS1: 'top', AS2: 'middle', AS3: 'bottom', AS: 'middle' }; - const selKey = this.drawingData?.selectedStartPosition || 'AS'; - const selectedPos = map[selKey] || 'middle'; + const selectedPos = this.resolveStartPos(); const topY = tableY + cfg.topYOffset; const midY = tableY + tableHeight / 2; const botY = tableY + tableHeight - cfg.bottomYOffset; const y = selectedPos === 'top' ? topY : selectedPos === 'bottom' ? botY : midY; return { x: circleX, y }; }, + resolveStartPos() { + // 1) bevorzugt AS1/AS2/AS3 aus drawingData.selectedStartPosition + const key = this.drawingData?.selectedStartPosition; + if (key === 'AS1') return 'top'; + if (key === 'AS2') return 'middle'; + if (key === 'AS3') return 'bottom'; + // 2) Fallback über selectedCirclePosition (top/middle/bottom) + const circlePos = this.drawingData?.selectedCirclePosition; + if (circlePos === 'top' || circlePos === 'middle' || circlePos === 'bottom') return circlePos; + // 3) Default Mitte + return 'middle'; + }, drawHitMarker(ctx, pos) { if (!pos) return; const mk = this.config.hitMarker; diff --git a/frontend/src/components/CourtDrawingTool.vue b/frontend/src/components/CourtDrawingTool.vue index 62ad2d1..0c5d417 100644 --- a/frontend/src/components/CourtDrawingTool.vue +++ b/frontend/src/components/CourtDrawingTool.vue @@ -22,7 +22,8 @@