Fix design spec measurements and add visual X/Y axis guides

- Fixed conversion formula for accurate CM measurements
- Add red dashed border around print area
- Add blue X-axis line (horizontal, 0cm at left)
- Add green Y-axis line (vertical, 0cm at top)
- Add orange circles marking design element positions
- Add reference legend explaining all visual guides
- Element labels changed from E1, E2 for clarity
This commit is contained in:
Andymick
2026-07-18 10:36:51 +01:00
parent 4d9cfa4023
commit a1c6e022b8
+91 -100
View File
@@ -42,10 +42,95 @@ export async function GET(req: Request, { params }: { params: { id: string } })
const printAreaWidthPx = DISPLAY * (printArea.wPct / 100); const printAreaWidthPx = DISPLAY * (printArea.wPct / 100);
const printAreaHeightPx = DISPLAY * (printArea.hPct / 100); const printAreaHeightPx = DISPLAY * (printArea.hPct / 100);
// Scale: how many pixels per millimeter
const pxPerMm = printAreaWidthPx / printAreaWidthMm; const pxPerMm = printAreaWidthPx / printAreaWidthMm;
// Convert pixel positions to CM // Convert pixel measurements to CM (divide by pxPerMm to get mm, then divide by 10 for cm)
const convertToCm = (px: number) => (px / pxPerMm / 10).toFixed(1); const convertToCm = (px: number) => {
const mm = px / pxPerMm;
return (mm / 10).toFixed(1);
};
const printAreaStartX = DISPLAY * (printArea.xPct / 100);
const printAreaStartY = DISPLAY * (printArea.yPct / 100);
const generateDesignSection = (designElements: any[], side: 'Front' | 'Back', placement: string | null, designPreview: string | null) => {
if (!designElements || designElements.length === 0) return '';
return `
<div class="spec-section">
<h2>${side} Design</h2>
<div class="print-area-info">
<strong>Printable Area:</strong> ${printAreaWidthMm}mm × ${printAreaHeightMm}mm (${convertToCm(printAreaWidthPx)} cm × ${convertToCm(printAreaHeightPx)} cm)
</div>
${placement ? `
<div class="mockup-container">
<p><em>Design on product (reference only) — with measurement guides</em></p>
<div style="position: relative; display: inline-block; max-width: 100%; max-height: 400px;">
<img src="${placement}" alt="${side} design placement" class="mockup-image" style="max-height: 400px; width: auto; display: block;">
<svg style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;" viewBox="0 0 ${DISPLAY} ${DISPLAY}">
<!-- Print area border -->
<rect x="${printAreaStartX}" y="${printAreaStartY}" width="${printAreaWidthPx}" height="${printAreaHeightPx}" fill="none" stroke="#ff0000" stroke-width="2" stroke-dasharray="5,5" opacity="0.7"/>
<!-- X-axis line -->
<line x1="${printAreaStartX}" y1="${printAreaStartY}" x2="${printAreaStartX + printAreaWidthPx}" y2="${printAreaStartY}" stroke="#0066ff" stroke-width="1" opacity="0.5"/>
<!-- Y-axis line -->
<line x1="${printAreaStartX}" y1="${printAreaStartY}" x2="${printAreaStartX}" y2="${printAreaStartY + printAreaHeightPx}" stroke="#00cc00" stroke-width="1" opacity="0.5"/>
<!-- Design elements with coordinate markers -->
${designElements.map((el, idx) => `
<circle cx="${el.x}" cy="${el.y}" r="4" fill="#ff6600" opacity="0.9"/>
<text x="${el.x + 8}" y="${el.y - 8}" font-size="11" fill="#ff6600" font-weight="bold" font-family="monospace">E${idx + 1}</text>
`).join('')}
</svg>
</div>
<div style="margin-top: 15px; font-size: 12px; color: #666; text-align: left;">
<p style="margin: 5px 0;"><strong style="color: #ff0000;">━━ Red dashed:</strong> Print area boundary</p>
<p style="margin: 5px 0;"><strong style="color: #0066ff;">━ Blue:</strong> X-axis (0cm at left)</p>
<p style="margin: 5px 0;"><strong style="color: #00cc00;">━ Green:</strong> Y-axis (0cm at top)</p>
<p style="margin: 5px 0;"><strong style="color: #ff6600;">● Orange circles:</strong> Element positions with labels (E1, E2, etc.)</p>
</div>
</div>
` : ''}
<table class="measurements-table">
<thead>
<tr>
<th>Element</th>
<th>Type</th>
<th>Position (CM)</th>
<th>Size (CM)</th>
<th>Details</th>
</tr>
</thead>
<tbody>
${designElements.map((el, idx) => `
<tr>
<td>E${idx + 1}</td>
<td class="element-type">${el.type === 'text' ? '📝 Text' : '🖼️ Image'}</td>
<td>X: ${convertToCm(el.x)} | Y: ${convertToCm(el.y)}</td>
<td>${el.type === 'image' ? `${convertToCm(el.width)} × ${convertToCm(el.height)}` : 'N/A'}</td>
<td>
${el.type === 'text' ? `
Font: ${el.fontFamily}, ${Math.round(el.fontSize / pxPerMm * 10 / 10)}pt<br>
Color: ${el.fill}<br>
Text: "${el.text}"<br>
Rotation: ${Math.round(el.rotation)}°
` : `
Rotation: ${Math.round(el.rotation)}°
`}
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
`;
};
const html = ` const html = `
<!DOCTYPE html> <!DOCTYPE html>
@@ -134,109 +219,15 @@ export async function GET(req: Request, { params }: { params: { id: string } })
<p><strong>Order Date:</strong> ${new Date(order.createdAt).toLocaleDateString()}</p> <p><strong>Order Date:</strong> ${new Date(order.createdAt).toLocaleDateString()}</p>
</div> </div>
${design.front && design.front.length > 0 ? ` ${generateDesignSection(design.front, 'Front', item.placementPreviewUrl, item.designPreviewUrl)}
<div class="spec-section"> ${generateDesignSection(design.back, 'Back', item.placementPreviewUrlBack, item.designPreviewUrlBack)}
<h2>Front Design</h2>
<div class="print-area-info">
<strong>Printable Area:</strong> ${printAreaWidthMm}mm × ${printAreaHeightMm}mm (${convertToCm(printAreaWidthPx)} cm × ${convertToCm(printAreaHeightPx)} cm)
</div>
${item.placementPreviewUrl ? `
<div class="mockup-container">
<p><em>Design on product (reference only)</em></p>
<img src="${item.placementPreviewUrl}" alt="Front design placement" class="mockup-image" style="max-height: 400px;">
</div>
` : ''}
<table class="measurements-table">
<thead>
<tr>
<th>Element</th>
<th>Type</th>
<th>Position (CM)</th>
<th>Size (CM)</th>
<th>Details</th>
</tr>
</thead>
<tbody>
${(design.front as any[]).map((el, idx) => `
<tr>
<td>${idx + 1}</td>
<td class="element-type">${el.type === 'text' ? '📝 Text' : '🖼️ Image'}</td>
<td>X: ${convertToCm(el.x)} | Y: ${convertToCm(el.y)}</td>
<td>${el.type === 'image' ? `${convertToCm(el.width)} × ${convertToCm(el.height)}` : 'N/A'}</td>
<td>
${el.type === 'text' ? `
Font: ${el.fontFamily}, ${Math.round(el.fontSize / pxPerMm * 10 / 10)}pt<br>
Color: ${el.fill}<br>
Text: "${el.text}"<br>
Rotation: ${Math.round(el.rotation)}°
` : `
Rotation: ${Math.round(el.rotation)}°
`}
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
` : ''}
${design.back && design.back.length > 0 ? `
<div class="spec-section">
<h2>Back Design</h2>
<div class="print-area-info">
<strong>Printable Area:</strong> ${printAreaWidthMm}mm × ${printAreaHeightMm}mm (${convertToCm(printAreaWidthPx)} cm × ${convertToCm(printAreaHeightPx)} cm)
</div>
${item.placementPreviewUrlBack ? `
<div class="mockup-container">
<p><em>Design on product (reference only)</em></p>
<img src="${item.placementPreviewUrlBack}" alt="Back design placement" class="mockup-image" style="max-height: 400px;">
</div>
` : ''}
<table class="measurements-table">
<thead>
<tr>
<th>Element</th>
<th>Type</th>
<th>Position (CM)</th>
<th>Size (CM)</th>
<th>Details</th>
</tr>
</thead>
<tbody>
${(design.back as any[]).map((el, idx) => `
<tr>
<td>${idx + 1}</td>
<td class="element-type">${el.type === 'text' ? '📝 Text' : '🖼️ Image'}</td>
<td>X: ${convertToCm(el.x)} | Y: ${convertToCm(el.y)}</td>
<td>${el.type === 'image' ? `${convertToCm(el.width)} × ${convertToCm(el.height)}` : 'N/A'}</td>
<td>
${el.type === 'text' ? `
Font: ${el.fontFamily}, ${Math.round(el.fontSize / pxPerMm * 10 / 10)}pt<br>
Color: ${el.fill}<br>
Text: "${el.text}"<br>
Rotation: ${Math.round(el.rotation)}°
` : `
Rotation: ${Math.round(el.rotation)}°
`}
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
` : ''}
<div class="spec-section" style="margin-top: 40px; padding-top: 20px; border-top: 1px solid #ccc; font-size: 12px; color: #666;"> <div class="spec-section" style="margin-top: 40px; padding-top: 20px; border-top: 1px solid #ccc; font-size: 12px; color: #666;">
<p><strong>Notes:</strong></p> <p><strong>Notes:</strong></p>
<ul> <ul>
<li>All measurements are in centimeters (CM)</li> <li>All measurements are in centimeters (CM)</li>
<li>Coordinates are measured from top-left of the printable area</li> <li>Coordinates are measured from top-left of the printable area (0,0)</li>
<li>X-axis runs left-to-right | Y-axis runs top-to-bottom</li>
<li>Print the design files separately for production</li> <li>Print the design files separately for production</li>
<li>This specification sheet is for reference and planning only</li> <li>This specification sheet is for reference and planning only</li>
</ul> </ul>