Skip to content

Commit f6539aa

Browse files
Code2Collapseclaude
andcommitted
feat(pipes): all skins trace exact pipe shapes + Pipe Shape menu — port to main
Port of feat (see its message). Single self-contained file; syntax OK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 376ee8f commit f6539aa

1 file changed

Lines changed: 42 additions & 13 deletions

File tree

js/c2c_noodle_styles.js

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,20 @@ function _polyAt(t, pts) {
454454
return pts[pts.length - 1].slice();
455455
}
456456

457+
// Complete a path (pen already at `a`) along the CURRENT pipe shape.
458+
// Piecewise shapes (angular/diag45) trace their exact waypoints — hard
459+
// corners — while curved shapes use the bezier. Lets every skin that used
460+
// to stroke a raw bezierCurveTo render the shape EXACTLY.
461+
function _curveTo(ctx, cp1, cp2, a, b) {
462+
const sh = _currentShape();
463+
if (sh === "angular" || sh === "diag45") {
464+
const w = _shapeWaypoints(a, b, sh);
465+
for (let i = 1; i < w.length; i++) ctx.lineTo(w[i][0], w[i][1]);
466+
return;
467+
}
468+
_curveTo(ctx, cp1, cp2, a, b);
469+
}
470+
457471
function _bezierPoints(a, b) {
458472
const shape = _currentShape();
459473
if (shape === "straight" || (shape === "auto" && _alignedStraight(a, b))) {
@@ -573,7 +587,7 @@ function _renderSpiderWeb(ctx, a, b /*, color */) {
573587
// core filament
574588
ctx.globalAlpha = 0.95; ctx.lineWidth = 2;
575589
ctx.beginPath(); ctx.moveTo(a[0], a[1]);
576-
ctx.bezierCurveTo(cp1[0], cp1[1], cp2[0], cp2[1], b[0], b[1]); ctx.stroke();
590+
_curveTo(ctx, cp1, cp2, a, b); ctx.stroke();
577591
// two wavy rails
578592
ctx.lineWidth = 1.3; ctx.globalAlpha = 0.85;
579593
for (const side of [-1, 1]) {
@@ -638,12 +652,12 @@ function _renderLightsaber(ctx, a, b, color, linkType) {
638652
ctx.strokeStyle = glow; ctx.lineWidth = 8; ctx.globalAlpha = 0.45;
639653
ctx.shadowBlur = 16; ctx.shadowColor = glow;
640654
ctx.beginPath(); ctx.moveTo(a[0],a[1]);
641-
ctx.bezierCurveTo(cp1[0],cp1[1], cp2[0],cp2[1], b[0],b[1]); ctx.stroke();
655+
_curveTo(ctx, cp1, cp2, a, b); ctx.stroke();
642656
// White core
643657
ctx.globalAlpha = 1; ctx.shadowBlur = 0;
644658
ctx.strokeStyle = C.white; ctx.lineWidth = 2;
645659
ctx.beginPath(); ctx.moveTo(a[0],a[1]);
646-
ctx.bezierCurveTo(cp1[0],cp1[1], cp2[0],cp2[1], b[0],b[1]); ctx.stroke();
660+
_curveTo(ctx, cp1, cp2, a, b); ctx.stroke();
647661
ctx.restore();
648662
}
649663
function _renderDnaHelix(ctx, a, b, color) {
@@ -681,7 +695,7 @@ function _renderRainbowFlow(ctx, a, b) {
681695
ctx.save();
682696
ctx.strokeStyle = grad; ctx.lineWidth = 3;
683697
ctx.beginPath(); ctx.moveTo(a[0],a[1]);
684-
ctx.bezierCurveTo(cp1[0],cp1[1], cp2[0],cp2[1], b[0],b[1]); ctx.stroke();
698+
_curveTo(ctx, cp1, cp2, a, b); ctx.stroke();
685699
ctx.restore();
686700
}
687701
function _renderDashedMarch(ctx, a, b, color) {
@@ -691,7 +705,7 @@ function _renderDashedMarch(ctx, a, b, color) {
691705
ctx.setLineDash([8,6]);
692706
ctx.lineDashOffset = -((performance.now()/40) % 14);
693707
ctx.beginPath(); ctx.moveTo(a[0],a[1]);
694-
ctx.bezierCurveTo(cp1[0],cp1[1], cp2[0],cp2[1], b[0],b[1]); ctx.stroke();
708+
_curveTo(ctx, cp1, cp2, a, b); ctx.stroke();
695709
ctx.setLineDash([]); ctx.restore();
696710
}
697711
function _renderLightning(ctx, a, b, color) {
@@ -720,7 +734,7 @@ function _renderPulsePacket(ctx, a, b, color) {
720734
ctx.save();
721735
ctx.strokeStyle = color; ctx.lineWidth = 2; ctx.globalAlpha = 0.7;
722736
ctx.beginPath(); ctx.moveTo(a[0],a[1]);
723-
ctx.bezierCurveTo(cp1[0],cp1[1], cp2[0],cp2[1], b[0],b[1]); ctx.stroke();
737+
_curveTo(ctx, cp1, cp2, a, b); ctx.stroke();
724738
ctx.globalAlpha = 1;
725739
const t = ((performance.now()/1500) % 1);
726740
const [px, py] = _bezierAt(t, a, cp1, cp2, b);
@@ -735,12 +749,12 @@ function _renderNeonTube(ctx, a, b, color) {
735749
ctx.strokeStyle = color; ctx.lineWidth = 6; ctx.globalAlpha = 0.25;
736750
ctx.shadowBlur = 18; ctx.shadowColor = color;
737751
ctx.beginPath(); ctx.moveTo(a[0],a[1]);
738-
ctx.bezierCurveTo(cp1[0],cp1[1], cp2[0],cp2[1], b[0],b[1]); ctx.stroke();
752+
_curveTo(ctx, cp1, cp2, a, b); ctx.stroke();
739753
// bright thin core
740754
ctx.globalAlpha = 1; ctx.shadowBlur = 0;
741755
ctx.strokeStyle = C.white; ctx.lineWidth = 1.5;
742756
ctx.beginPath(); ctx.moveTo(a[0],a[1]);
743-
ctx.bezierCurveTo(cp1[0],cp1[1], cp2[0],cp2[1], b[0],b[1]); ctx.stroke();
757+
_curveTo(ctx, cp1, cp2, a, b); ctx.stroke();
744758
ctx.restore();
745759
}
746760

@@ -770,7 +784,7 @@ function _renderGradient(ctx, a, b, color, linkType) {
770784
ctx.save(); ctx.lineCap = "round"; ctx.lineWidth = 3.5; ctx.strokeStyle = grad;
771785
ctx.shadowBlur = 6; ctx.shadowColor = color;
772786
ctx.beginPath(); ctx.moveTo(a[0], a[1]);
773-
ctx.bezierCurveTo(cp1[0], cp1[1], cp2[0], cp2[1], b[0], b[1]); ctx.stroke();
787+
_curveTo(ctx, cp1, cp2, a, b); ctx.stroke();
774788
ctx.restore();
775789
}
776790
// Flame: red→orange→yellow gradient that flickers + tapers, with ember glow.
@@ -795,7 +809,7 @@ function _renderComet(ctx, a, b, color) {
795809
ctx.save(); ctx.lineCap = "round";
796810
ctx.strokeStyle = color; ctx.globalAlpha = 0.22; ctx.lineWidth = 2;
797811
ctx.beginPath(); ctx.moveTo(a[0], a[1]);
798-
ctx.bezierCurveTo(cp1[0], cp1[1], cp2[0], cp2[1], b[0], b[1]); ctx.stroke();
812+
_curveTo(ctx, cp1, cp2, a, b); ctx.stroke();
799813
const head = (performance.now() / 900) % 1, TRAIL = 14;
800814
for (let i = 0; i < TRAIL; i++) {
801815
const t = head - i * 0.025; if (t < 0) continue;
@@ -812,10 +826,10 @@ function _renderCandy(ctx, a, b, color) {
812826
ctx.save(); ctx.lineCap = "butt"; ctx.lineWidth = 4.5;
813827
ctx.strokeStyle = "#ffffff";
814828
ctx.beginPath(); ctx.moveTo(a[0], a[1]);
815-
ctx.bezierCurveTo(cp1[0], cp1[1], cp2[0], cp2[1], b[0], b[1]); ctx.stroke();
829+
_curveTo(ctx, cp1, cp2, a, b); ctx.stroke();
816830
ctx.strokeStyle = color; ctx.lineWidth = 4.5; ctx.setLineDash([8, 8]); ctx.lineDashOffset = -off;
817831
ctx.beginPath(); ctx.moveTo(a[0], a[1]);
818-
ctx.bezierCurveTo(cp1[0], cp1[1], cp2[0], cp2[1], b[0], b[1]); ctx.stroke();
832+
_curveTo(ctx, cp1, cp2, a, b); ctx.stroke();
819833
ctx.setLineDash([]); ctx.restore();
820834
}
821835
// Aurora: soft green↔blue↔purple ribbon that breathes along the wire.
@@ -860,7 +874,7 @@ function _renderHeartbeat(ctx, a, b, color) {
860874
function _trunk(ctx, a, cp1, cp2, b, color, w, glow) {
861875
ctx.beginPath();
862876
ctx.moveTo(a[0], a[1]);
863-
ctx.bezierCurveTo(cp1[0], cp1[1], cp2[0], cp2[1], b[0], b[1]);
877+
_curveTo(ctx, cp1, cp2, a, b);
864878
if (glow) { ctx.shadowBlur = glow; ctx.shadowColor = color; }
865879
ctx.lineWidth = w; ctx.strokeStyle = color; ctx.stroke();
866880
ctx.shadowBlur = 0;
@@ -1771,6 +1785,21 @@ function _installMenuPatch() {
17711785
has_submenu: true,
17721786
submenu: { options: submenu },
17731787
});
1788+
const curShape = _currentShape();
1789+
out.push({
1790+
content: "〰 Pipe Shape",
1791+
has_submenu: true,
1792+
submenu: {
1793+
options: SHAPES.map(sh => ({
1794+
content: `${sh === curShape ? "✓ " : " "}${sh}`,
1795+
callback: () => {
1796+
_shapeCache = sh;
1797+
try { app.ui.settings.setSettingValue(SHAPE_SETTING_ID, sh); } catch (__c2cErr) { __c2cReport("c2c_noodle_styles.shape", __c2cErr); }
1798+
this.setDirty(true, true);
1799+
},
1800+
})),
1801+
},
1802+
});
17741803
out.push({
17751804
content: "— also configurable in Settings → mec.noodle.style",
17761805
disabled: true,

0 commit comments

Comments
 (0)