diff --git a/svelte-app/src/lib/canvas/render-theme.ts b/svelte-app/src/lib/canvas/render-theme.ts index 4451668..c56788b 100644 --- a/svelte-app/src/lib/canvas/render-theme.ts +++ b/svelte-app/src/lib/canvas/render-theme.ts @@ -58,7 +58,9 @@ export const THEME = { rightBoxStrokeWidth: 1.5, }, induction: { - fillColor: '#000000', + fillColor: '#ffffff', + strokeColor: '#000000', + lineWidth: 0.5, }, canvas: { maxRenderScale: 4, diff --git a/svelte-app/src/lib/canvas/renderer.ts b/svelte-app/src/lib/canvas/renderer.ts index d5ddfdd..83c9a03 100644 --- a/svelte-app/src/lib/canvas/renderer.ts +++ b/svelte-app/src/lib/canvas/renderer.ts @@ -186,7 +186,6 @@ function drawEpcSymbol(ctx: CanvasRenderingContext2D, sym: PlacedSymbol) { ctx.fillStyle = THEME.epcBody.rightBoxFill; ctx.strokeStyle = THEME.epcBody.rightBoxStroke; ctx.lineWidth = THEME.epcBody.rightBoxStrokeWidth; - ctx.rotate(-Math.PI / 2); ctx.fillRect(-rb.w, -rb.h / 2, rb.w, rb.h); ctx.strokeRect(-rb.w, -rb.h / 2, rb.w, rb.h); ctx.restore(); @@ -269,7 +268,7 @@ function traceEpcOutlinePath(ctx: CanvasRenderingContext2D, sym: PlacedSymbol, p ctx.save(); ctx.translate(plx, ply); - ctx.rotate(rAngle - Math.PI / 2); + ctx.rotate(rAngle); ctx.beginPath(); ctx.rect(-rb.w - pad, -rb.h / 2 - pad, rb.w + pad * 2, rb.h + pad * 2); ctx.stroke(); @@ -376,26 +375,47 @@ function drawInductionSymbol(ctx: CanvasRenderingContext2D, sym: PlacedSymbol) { ctx.closePath(); ctx.fillStyle = THEME.induction.fillColor; + ctx.strokeStyle = THEME.induction.strokeColor; + ctx.lineWidth = THEME.induction.lineWidth; ctx.fill(); + ctx.stroke(); } -/** Draw photoeye with 3-slice: fixed left cap, stretched middle beam, fixed right cap */ -function drawPhotoeye3Slice(ctx: CanvasRenderingContext2D, sym: PlacedSymbol, img: HTMLImageElement) { - const { leftCap, rightCap, defaultWidth } = PHOTOEYE_CONFIG; - const srcW = img.naturalWidth; - const srcH = img.naturalHeight; - const scale = srcW / defaultWidth; - const srcLeftW = leftCap * scale; - const srcRightW = rightCap * scale; - const srcMiddleW = srcW - srcLeftW - srcRightW; - const dstMiddleW = sym.w - leftCap - rightCap; +/** Draw photoeye programmatically for consistent stroke at any size */ +function drawPhotoeyeSymbol(ctx: CanvasRenderingContext2D, sym: PlacedSymbol) { + const { leftCap, rightCap } = PHOTOEYE_CONFIG; + const x = sym.x, y = sym.y, w = sym.w, h = sym.h; - // Left cap (fixed) - ctx.drawImage(img, 0, 0, srcLeftW, srcH, sym.x, sym.y, leftCap, sym.h); - // Middle beam (stretched) - ctx.drawImage(img, srcLeftW, 0, srcMiddleW, srcH, sym.x + leftCap, sym.y, dstMiddleW, sym.h); - // Right cap (fixed) - ctx.drawImage(img, srcW - srcRightW, 0, srcRightW, srcH, sym.x + sym.w - rightCap, sym.y, rightCap, sym.h); + // Y positions as fractions of height (derived from original SVG path) + const beamTop = y + h * 0.42; + const beamBottom = y + h * 0.585; + const arrowInnerTop = y + h * 0.248; + const arrowInnerBottom = y + h * 0.744; + const recvTop = y + h * 0.181; + const recvBottom = y + h * 0.826; + const arrowTipTop = y + h * 0.05; + const arrowTipBottom = y + h * 0.948; + + ctx.beginPath(); + ctx.moveTo(x + leftCap, beamTop); + ctx.lineTo(x + leftCap, arrowInnerTop); + ctx.lineTo(x, arrowTipTop); + ctx.lineTo(x, arrowTipBottom); + ctx.lineTo(x + leftCap, arrowInnerBottom); + ctx.lineTo(x + leftCap, beamBottom); + ctx.lineTo(x + w - rightCap, beamBottom); + ctx.lineTo(x + w - rightCap, recvBottom); + ctx.lineTo(x + w, recvBottom); + ctx.lineTo(x + w, recvTop); + ctx.lineTo(x + w - rightCap, recvTop); + ctx.lineTo(x + w - rightCap, beamTop); + ctx.closePath(); + + ctx.fillStyle = '#ffffff'; + ctx.strokeStyle = '#000000'; + ctx.lineWidth = 0.5; + ctx.fill(); + ctx.stroke(); } /** Draw curved conveyor/chute programmatically with fixed band width */ @@ -409,7 +429,7 @@ function drawCurvedSymbol(ctx: CanvasRenderingContext2D, sym: PlacedSymbol) { ctx.arc(arcCx, arcCy, innerR, -sweepRad, 0, false); ctx.closePath(); - ctx.fillStyle = '#000000'; + ctx.fillStyle = '#ffffff'; ctx.strokeStyle = '#000000'; ctx.lineWidth = 0.5; ctx.fill(); @@ -432,19 +452,17 @@ function drawSymbolBody(ctx: CanvasRenderingContext2D, sym: PlacedSymbol): boole ctx.lineTo(sym.x + sym.w, sym.y + sym.h); ctx.lineTo(sym.x, sym.y + sym.h); ctx.closePath(); - ctx.fillStyle = '#000000'; + ctx.fillStyle = '#ffffff'; ctx.strokeStyle = '#000000'; ctx.lineWidth = 0.5; ctx.fill(); ctx.stroke(); + } else if (isPhotoeyeType(sym.symbolId)) { + drawPhotoeyeSymbol(ctx, sym); } else { const img = getSymbolImage(sym.file); if (!img) return false; - if (isPhotoeyeType(sym.symbolId)) { - drawPhotoeye3Slice(ctx, sym, img); - } else { - ctx.drawImage(img, sym.x, sym.y, sym.w, sym.h); - } + ctx.drawImage(img, sym.x, sym.y, sym.w, sym.h); } return true; } diff --git a/svelte-app/src/lib/export.ts b/svelte-app/src/lib/export.ts index b632879..bda235e 100644 --- a/svelte-app/src/lib/export.ts +++ b/svelte-app/src/lib/export.ts @@ -52,7 +52,7 @@ export async function exportSVG() { const stripBottomY = sym.y + sym.h * INDUCTION_CONFIG.stripBottomFrac; const pts = INDUCTION_CONFIG.arrowPoints.map(([xf, yf]) => [sym.x + xf * hw, sym.y + yf * sym.h] as const); const d = `M ${sym.x + sym.w},${stripTopY} L ${pts[0][0]},${stripTopY} ${pts.map(([px, py]) => `L ${px},${py}`).join(' ')} L ${pts[5][0]},${stripBottomY} L ${sym.x + sym.w},${stripBottomY} Z`; - lines.push(` `); + lines.push(` `); } else if (isCurvedType(sym.symbolId)) { const angle = sym.curveAngle || 90; const { arcCx, arcCy, outerR, innerR } = getCurveGeometry(sym.symbolId, sym.x, sym.y, sym.w, sym.h); @@ -69,11 +69,11 @@ export async function exportSVG() { `A ${innerR},${innerR} 0 ${largeArc},1 ${arcCx + innerR},${arcCy}`, 'Z', ].join(' '); - lines.push(` `); + lines.push(` `); } else if (isSpurType(sym.symbolId)) { const w2 = sym.w2 ?? sym.w; const d = `M ${sym.x},${sym.y} L ${sym.x + w2},${sym.y} L ${sym.x + sym.w},${sym.y + sym.h} L ${sym.x},${sym.y + sym.h} Z`; - lines.push(` `); + lines.push(` `); } else { // Regular SVG symbol try { diff --git a/svelte-app/src/lib/symbols.ts b/svelte-app/src/lib/symbols.ts index fc7bda6..9580494 100644 --- a/svelte-app/src/lib/symbols.ts +++ b/svelte-app/src/lib/symbols.ts @@ -36,8 +36,8 @@ export const SYMBOLS: SymbolDef[] = [ { id: 'fio_sio_fioh_v', name: 'FIO/SIO/FIOH (V)', file: '/symbols/fio_sio_fioh.svg', w: 14, h: 20, defaultRotation: 90, group: 'I/O Modules' }, // --- Sensors --- - { id: 'photoeye', name: 'Photoeye', file: '/symbols/photoeye.svg', w: 56, h: 20, group: 'Sensors' }, - { id: 'photoeye_v', name: 'Photoeye (V)', file: '/symbols/photoeye.svg', w: 56, h: 20, defaultRotation: 90, group: 'Sensors' }, + { id: 'photoeye', name: 'Photoeye', file: '/symbols/photoeye.svg', w: 30, h: 14, group: 'Sensors' }, + { id: 'photoeye_v', name: 'Photoeye (V)', file: '/symbols/photoeye.svg', w: 30, h: 14, defaultRotation: 90, group: 'Sensors' }, { id: 'pressure_sensor', name: 'Pressure Sensor', file: '/symbols/pressure_sensor.svg', w: 20, h: 20, group: 'Sensors' }, { id: 'pressure_sensor_v', name: 'Pressure Sensor (V)', file: '/symbols/pressure_sensor.svg', w: 20, h: 20, defaultRotation: 90, group: 'Sensors' }, diff --git a/svelte-app/static/symbols/chute.svg b/svelte-app/static/symbols/chute.svg index 96117c4..a621872 100644 Binary files a/svelte-app/static/symbols/chute.svg and b/svelte-app/static/symbols/chute.svg differ diff --git a/svelte-app/static/symbols/conveyor.svg b/svelte-app/static/symbols/conveyor.svg index 9c8e36b..0f87887 100644 Binary files a/svelte-app/static/symbols/conveyor.svg and b/svelte-app/static/symbols/conveyor.svg differ diff --git a/svelte-app/static/symbols/extendo.svg b/svelte-app/static/symbols/extendo.svg index a2b79ff..f6ce139 100644 Binary files a/svelte-app/static/symbols/extendo.svg and b/svelte-app/static/symbols/extendo.svg differ diff --git a/svelte-app/static/symbols/induction.svg b/svelte-app/static/symbols/induction.svg index ffbb691..8660372 100644 Binary files a/svelte-app/static/symbols/induction.svg and b/svelte-app/static/symbols/induction.svg differ diff --git a/svelte-app/static/symbols/spur.svg b/svelte-app/static/symbols/spur.svg index 48c40b6..4818f09 100644 Binary files a/svelte-app/static/symbols/spur.svg and b/svelte-app/static/symbols/spur.svg differ diff --git a/svelte-app/static/symbols/tipper.svg b/svelte-app/static/symbols/tipper.svg index 416b1db..5aee896 100644 Binary files a/svelte-app/static/symbols/tipper.svg and b/svelte-app/static/symbols/tipper.svg differ