From 353a2cb2ef68384df51f6917374d20ff8c18067e Mon Sep 17 00:00:00 2001 From: Kyle Date: Fri, 24 Apr 2026 15:04:38 -0400 Subject: [PATCH] Render stripe as perpendicular tick marks along wire path Replaces the helix-based stripe (which looked like a squiggling twisted pair) with short perpendicular tick marks centered on the wire body, spaced every 8px. This gives the characteristic slash/stripe appearance of real striped wire, contained within the wire width. Co-Authored-By: Claude Sonnet 4.6 --- frontend/js/canvas.js | 49 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/frontend/js/canvas.js b/frontend/js/canvas.js index a727228..d33941d 100644 --- a/frontend/js/canvas.js +++ b/frontend/js/canvas.js @@ -970,7 +970,7 @@ class DiagramCanvas { if (n.shield) n.shield.data(svgPath); if (!wire.twisted_pair) { n.main.data(svgPath); - if (n.stripe) n.stripe.data(this._computeHelixPath(pts, 20, false) || svgPath); + if (n.stripe) n.stripe.data(this._computeStripeTickPath(pts, 8, 2) || svgPath); } if (n.helix) n.helix.data(this._computeHelixPath(pts, wire.twist_pitch || 16, false)); if (n.helix2) n.helix2.data(this._computeHelixPath(pts, wire.twist_pitch || 16, true)); @@ -1478,6 +1478,43 @@ class DiagramCanvas { // ── Wire rendering ──────────────────────────────────────────────────────────── + // Returns SVG path of short perpendicular tick marks along the polyline — used for stripe rendering. + _computeStripeTickPath(pts, spacing = 8, halfLen = 2) { + if (!pts || pts.length < 4) return ''; + const points = []; + let total = 0; + for (let i = 0; i < pts.length; i += 2) { + if (i === 0) { points.push({ x: pts[0], y: pts[1], d: 0 }); continue; } + const dx = pts[i] - pts[i-2], dy = pts[i+1] - pts[i-1]; + total += Math.sqrt(dx*dx + dy*dy); + points.push({ x: pts[i], y: pts[i+1], d: total }); + } + if (total < spacing) return ''; + const interp = (d) => { + d = Math.max(0, Math.min(total, d)); + for (let i = 1; i < points.length; i++) { + if (points[i].d >= d) { + const p0 = points[i-1], p1 = points[i]; + const segLen = p1.d - p0.d; + if (segLen < 1e-6) return { x: p0.x, y: p0.y, nx: 0, ny: 1 }; + const t = (d - p0.d) / segLen; + const len = Math.sqrt((p1.x-p0.x)**2 + (p1.y-p0.y)**2); + const ux = (p1.x-p0.x)/len, uy = (p1.y-p0.y)/len; + return { x: p0.x + t*(p1.x-p0.x), y: p0.y + t*(p1.y-p0.y), nx: -uy, ny: ux }; + } + } + const last = points[points.length-1]; + return { x: last.x, y: last.y, nx: 0, ny: 1 }; + }; + let d = ''; + for (let dist = spacing / 2; dist <= total - spacing / 2; dist += spacing) { + const p = interp(dist); + d += `M ${(p.x - p.nx * halfLen).toFixed(1)} ${(p.y - p.ny * halfLen).toFixed(1)} `; + d += `L ${(p.x + p.nx * halfLen).toFixed(1)} ${(p.y + p.ny * halfLen).toFixed(1)} `; + } + return d.trim(); + } + // Returns SVG path string of alternating bezier arcs along the polyline. // flip=true starts arcs on the opposite side — used for the second wire of a twisted pair. _computeHelixPath(pts, pitch = 16, flip = false) { @@ -1547,11 +1584,11 @@ class DiagramCanvas { let stripe = null; if (wire.color_stripe && !wire.twisted_pair) { - const stripePath = this._computeHelixPath(pts, 20, false); - if (stripePath) { + const tickPath = this._computeStripeTickPath(pts, 8, 2); + if (tickPath) { stripe = new Konva.Path({ - data: stripePath, stroke: wire.color_stripe, - strokeWidth: 2, lineCap: "round", lineJoin: "round", fill: null, listening: false, + data: tickPath, stroke: wire.color_stripe, + strokeWidth: 2, lineCap: "round", fill: null, listening: false, }); } } @@ -1654,7 +1691,7 @@ class DiagramCanvas { if (n.shield) n.shield.data(svgPath); if (!wire.twisted_pair) { n.main.data(svgPath); - if (n.stripe) n.stripe.data(this._computeHelixPath(pts, 20, false) || svgPath); + if (n.stripe) n.stripe.data(this._computeStripeTickPath(pts, 8, 2) || svgPath); } if (n.helix) n.helix.data(this._computeHelixPath(pts, wire.twist_pitch || 16, false)); if (n.helix2) n.helix2.data(this._computeHelixPath(pts, wire.twist_pitch || 16, true));