Skip to content

Commit 141f1d5

Browse files
ddyson1claude
andauthored
Live plot rewrite, consistent plot sizing, and copy fixes (#4)
* Rewrite README to reflect the PlotFlow brand Lead with what PlotFlow is (pen-plotter art house, one-continuous-line mobile suit editions) and the process, while keeping the local-run, deploy, and structure notes. https://claude.ai/code/session_01VhWRYXbuqapZ9YkvksroaS * Crop plots to bounding box for consistent sizing Compute each suit's bounding box at runtime (getBBox) and crop the viewBox to a padded version, so every edition fills its card and the live-plot stage at a consistent scale instead of being letterboxed in the original canvas. Fix the paper bed to a single 4:3 shape so the canvas is consistent across designs, and reset the animation clock on load so the live plot always starts from a blank sheet. https://claude.ai/code/session_01VhWRYXbuqapZ9YkvksroaS * Fix live plot blank-start flash and rename to PLOTFLOW Hide the path before swapping suit data so the old dash values don't flash the full path for a frame. Rename "PLOTFLOW ART HOUSE" to "PLOTFLOW" in all footer copyright lines. https://claude.ai/code/session_01VhWRYXbuqapZ9YkvksroaS * Fix live plot revealing whole path at once The animated hero path used vector-effect:non-scaling-stroke, which measures stroke-dasharray in screen pixels while getTotalLength returns user units. The mismatch made the full path render as one solid dash (visible immediately) with the trace drawing over it. Drop non-scaling-stroke from the animated path and set its width in user units per-load so it stays ~1.2px. Static shop-card paths are unchanged. https://claude.ai/code/session_01VhWRYXbuqapZ9YkvksroaS * Remove "art house" from brand copy Replace "art house" with "studio" in the hero sub, contact page, README, and design system doc; drop "ART HOUSE" from the README copyright line. https://claude.ai/code/session_01VhWRYXbuqapZ9YkvksroaS * Plot live drawing on a canvas instead of dash-offset The suit paths contain thousands of M-command subpaths, which the CSS stroke-dashoffset reveal renders all at once — so the whole design appeared instantly instead of being traced. Replace the dash technique with progressive canvas stroking: walk the geometry with getPointAtLength, lifting the pen at subpath jumps, so ink only appears where the pen has passed. The SVG path is kept for geometry only (stroke:none) and the pen reticle stays on top. https://claude.ai/code/session_01VhWRYXbuqapZ9YkvksroaS --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent ff181ac commit 141f1d5

13 files changed

Lines changed: 131 additions & 33 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# PLOTFLOW
22

3-
**Avant-garde pen-plotter art house.** Universal-Century mobile suits, rebuilt as a single unbroken line and drawn in ink by machine.
3+
**Avant-garde pen-plotter studio.** Universal-Century mobile suits, rebuilt as a single unbroken line and drawn in ink by machine.
44

55
一筆書き — *hitofudegaki*, "one continuous line."
66

@@ -63,4 +63,4 @@ git add -A && git commit -m "update site" && git push
6363

6464
---
6565

66-
<sub>© 2026 PLOTFLOW ART HOUSE* — plotted in the U.S.A. · 一筆書き · one continuous line</sub>
66+
<sub>© 2026 PLOTFLOW* — plotted in the U.S.A. · 一筆書き · one continuous line</sub>

commissions.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ <h3>Get Started</h3>
6666
<div class="fc"><h5>Info*</h5><a href="shipping.html">Shipping</a><a href="returns.html">Returns</a><a href="faq.html">FAQ</a></div>
6767
<div class="fc"><h5>一筆書き</h5><a href="#">@plotflow</a><a href="#">Newsletter</a><a href="#">est. 2026</a></div>
6868
</div>
69-
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW ART HOUSE* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
69+
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
7070
</div>
7171
</footer>
7272

contact.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ <h3>Commissions</h3>
4848
<p>Interested in a custom piece? Visit our <a href="commissions.html" style="color:var(--red)">Commissions</a> page for details on custom work, or email us directly with your idea.</p>
4949

5050
<h3>Studio</h3>
51-
<p>PlotFlow Art House<br>Plotted in the U.S.A.</p>
51+
<p>PlotFlow Studio<br>Plotted in the U.S.A.</p>
5252
</div>
5353
</div>
5454
</section>
@@ -63,7 +63,7 @@ <h3>Studio</h3>
6363
<div class="fc"><h5>Info*</h5><a href="shipping.html">Shipping</a><a href="returns.html">Returns</a><a href="faq.html">FAQ</a></div>
6464
<div class="fc"><h5>一筆書き</h5><a href="#">@plotflow</a><a href="#">Newsletter</a><a href="#">est. 2026</a></div>
6565
</div>
66-
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW ART HOUSE* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
66+
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
6767
</div>
6868
</footer>
6969

docs/DESIGN_SYSTEM.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# PLOTFLOW — Design System
22

3-
A small art house selling **pen-plotter (AxiDraw) line drawings of Universal-Century mobile suits** — each rebuilt as a single continuous line and drawn in red ink on archival paper, signed + numbered. *Plotted, not printed.* est. 2026.
3+
A small studio selling **pen-plotter (AxiDraw) line drawings of Universal-Century mobile suits** — each rebuilt as a single continuous line and drawn in red ink on archival paper, signed + numbered. *Plotted, not printed.* est. 2026.
44

55
Aesthetic: **washed/muted ground · bold white grotesque · Japanese · halftone + asterisk**, with an interactive **Live Plot** hero that draws the suit in front of you. This document is the source of truth for tokens, components, and rules. (`tokens.json` is the machine-readable mirror; `tokens.css` is what the site consumes.)
66

faq.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ <h3>Can I return a piece?</h3>
9090
<div class="fc"><h5>Info*</h5><a href="shipping.html">Shipping</a><a href="returns.html">Returns</a><a href="faq.html">FAQ</a></div>
9191
<div class="fc"><h5>一筆書き</h5><a href="#">@plotflow</a><a href="#">Newsletter</a><a href="#">est. 2026</a></div>
9292
</div>
93-
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW ART HOUSE* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
93+
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
9494
</div>
9595
</footer>
9696

index.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
<div class="hero-head">
2929
<h1>PLOTFLOW<span class="s">*</span></h1>
30-
<div class="sub">avant-garde plotter art house <b>//</b> mobile suit editions, drawn by machine <b>//</b> プロットフロー</div>
30+
<div class="sub">avant-garde pen-plotter studio <b>//</b> mobile suit editions, drawn by machine <b>//</b> プロットフロー</div>
3131
</div>
3232

3333
<div class="hud num">
@@ -41,8 +41,9 @@ <h1>PLOTFLOW<span class="s">*</span></h1>
4141
<div class="bed" id="bed">
4242
<span class="corner c-tl"></span><span class="corner c-tr"></span><span class="corner c-bl"></span><span class="corner c-br"></span>
4343
<span class="bl" id="bedlabel">BED 01 · ARCHIVAL</span>
44+
<canvas id="pcanvas"></canvas>
4445
<svg id="stage" viewBox="0 0 1493 801" preserveAspectRatio="xMidYMid meet">
45-
<path id="ppath" vector-effect="non-scaling-stroke" d=""></path>
46+
<path id="ppath" d=""></path>
4647
<g id="pen" opacity="0">
4748
<circle id="pen-c" r="14" fill="none" stroke="#15140f" stroke-width="1.6" vector-effect="non-scaling-stroke"></circle>
4849
<path id="pen-x" d="" stroke="#15140f" stroke-width="1.6" vector-effect="non-scaling-stroke"></path>
@@ -111,7 +112,7 @@ <h2>The Work<span class="star">*</span></h2><span class="jp">作品</span>
111112
<div class="fc"><h5>Info*</h5><a href="shipping.html">Shipping</a><a href="returns.html">Returns</a><a href="faq.html">FAQ</a></div>
112113
<div class="fc"><h5>一筆書き</h5><a href="#">@plotflow</a><a href="#">Newsletter</a><a href="#">est. 2026</a></div>
113114
</div>
114-
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW ART HOUSE* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
115+
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
115116
</div>
116117
</footer>
117118

manifesto.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ <h1>Manifesto<span class="star">*</span></h1>
6060
<div class="fc"><h5>Info*</h5><a href="shipping.html">Shipping</a><a href="returns.html">Returns</a><a href="faq.html">FAQ</a></div>
6161
<div class="fc"><h5>一筆書き</h5><a href="#">@plotflow</a><a href="#">Newsletter</a><a href="#">est. 2026</a></div>
6262
</div>
63-
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW ART HOUSE* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
63+
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
6464
</div>
6565
</footer>
6666

preview.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@
178178

179179
<div class="hero-head">
180180
<h1>PLOTFLOW<span class="s">*</span></h1>
181-
<div class="sub">avant-garde plotter art house <b>//</b> mobile suit editions, drawn by machine <b>//</b> プロットフロー</div>
181+
<div class="sub">avant-garde pen-plotter studio <b>//</b> mobile suit editions, drawn by machine <b>//</b> プロットフロー</div>
182182
</div>
183183

184184
<div class="hud num">
@@ -262,7 +262,7 @@ <h2>The Work<span class="star">*</span></h2><span class="jp">作品</span>
262262
<div class="fc"><h5>Info*</h5><a href="#">Shipping</a><a href="#">Returns</a><a href="#">FAQ</a></div>
263263
<div class="fc"><h5>一筆書き</h5><a href="#">@plotflow</a><a href="#">Newsletter</a><a href="#">est. 2026</a></div>
264264
</div>
265-
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW ART HOUSE* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
265+
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
266266
</div>
267267
</footer>
268268

returns.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ <h3>Cancellations</h3>
5656
<div class="fc"><h5>Info*</h5><a href="shipping.html">Shipping</a><a href="returns.html">Returns</a><a href="faq.html">FAQ</a></div>
5757
<div class="fc"><h5>一筆書き</h5><a href="#">@plotflow</a><a href="#">Newsletter</a><a href="#">est. 2026</a></div>
5858
</div>
59-
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW ART HOUSE* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
59+
<div class="foot-bot tiny"><span>© 2026 PLOTFLOW* — PLOTTED IN THE U.S.A.</span><span>一筆書き · ONE CONTINUOUS LINE</span></div>
6060
</div>
6161
</footer>
6262

scripts/plotter.js

Lines changed: 97 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
/* ============================================================
22
PLOTFLOW · Live Plot component
3-
Animates a suit's single continuous path being drawn by a
4-
virtual AxiDraw, with a moving pen head + live readouts.
3+
Progressively strokes a suit's path onto a <canvas> the way an
4+
AxiDraw would — walking the geometry with getPointAtLength and
5+
lifting the pen at subpath breaks — with a moving pen head + HUD.
6+
(A CSS stroke-dashoffset reveal is unreliable here: each suit's
7+
path has thousands of M-command subpaths, which the dash trick
8+
renders all at once.)
59
Depends on: window.PLOTFLOW (data/editions.js)
610
Exposes: window.PlotflowPlotter.load(key)
711
============================================================ */
@@ -11,15 +15,21 @@
1115

1216
var $ = function (id) { return document.getElementById(id); };
1317
var stage = $('stage'), ppath = $('ppath'), pen = $('pen'), penC = $('pen-c'), penX = $('pen-x');
14-
if (!stage || !ppath) return; // plotter markup not on this page
18+
var canvas = $('pcanvas');
19+
if (!stage || !ppath || !canvas) return; // plotter markup not on this page
20+
var ctx = canvas.getContext('2d');
1521

1622
var pbar = $('pbar'), pct = $('pct'), cx = $('cx'), cy = $('cy');
1723
var ink = $('ink'), inktot = $('inktot'), elapsed = $('elapsed'), total = $('total');
1824
var bed = $('bed'), bedlabel = $('bedlabel'), selSuit = $('suit'), playBtn = $('play'), replay = $('replay');
1925

2026
var FEED = 1100; // mm/min, shown in HUD + used for plot-time
2127
var BASE = 22; // seconds for a full plot at 1x speed
22-
var cur, len = 1, drawn = 0, playing = true, speed = 1, mmPerUnit = 1, totalMin = 1;
28+
var INK = '#e8351f';
29+
var cur, len = 1, drawn = 0, painted = 0, playing = true, speed = 1, mmPerUnit = 1, totalMin = 1;
30+
var vb = { x: 0, y: 0, w: 1, h: 1 };
31+
var tf = { s: 1, ox: 0, oy: 0, dpr: 1, ready: false };
32+
var prevPt = null;
2333

2434
if (selSuit) {
2535
ORDER.forEach(function (k) {
@@ -34,33 +44,95 @@
3444
return String(Math.floor(s / 60)).padStart(2, '0') + ':' + String(s % 60).padStart(2, '0');
3545
}
3646

47+
// Map a point in suit/user coordinates to canvas device pixels, matching the
48+
// SVG stage's preserveAspectRatio="xMidYMid meet" so the pen lines up.
49+
function mapX(ux) { return (tf.ox + (ux - vb.x) * tf.s) * tf.dpr; }
50+
function mapY(uy) { return (tf.oy + (uy - vb.y) * tf.s) * tf.dpr; }
51+
52+
// Size the canvas backing store to the bed and (re)derive the meet transform.
53+
// Resizing the backing store clears it, so this also resets the ink.
54+
function resetCanvas() {
55+
var w = canvas.clientWidth, h = canvas.clientHeight;
56+
tf.ready = w > 0 && h > 0;
57+
if (!tf.ready) return;
58+
tf.dpr = window.devicePixelRatio || 1;
59+
canvas.width = Math.round(w * tf.dpr);
60+
canvas.height = Math.round(h * tf.dpr);
61+
tf.s = Math.min(w / vb.w, h / vb.h);
62+
tf.ox = (w - vb.w * tf.s) / 2;
63+
tf.oy = (h - vb.h * tf.s) / 2;
64+
ctx.setTransform(1, 0, 0, 1, 0, 0);
65+
ctx.clearRect(0, 0, canvas.width, canvas.height);
66+
ctx.strokeStyle = INK;
67+
ctx.lineWidth = Math.max(1, 1.2 * tf.dpr);
68+
ctx.lineJoin = 'round';
69+
ctx.lineCap = 'round';
70+
painted = 0;
71+
prevPt = null;
72+
}
73+
74+
// Stroke ink from `painted` up to `target` length, sampling the path and
75+
// lifting the pen (moveTo) wherever the geometry jumps between subpaths.
76+
function paintTo(target) {
77+
if (!tf.ready || target <= painted) return;
78+
var step = Math.max(0.5, 2 / tf.s); // ~2 CSS px between samples
79+
var liftSq = (step * 3) * (step * 3); // a jump this big ⇒ pen lift
80+
ctx.beginPath();
81+
if (prevPt) ctx.moveTo(mapX(prevPt.x), mapY(prevPt.y));
82+
var L = painted;
83+
while (L < target) {
84+
L = Math.min(target, L + step);
85+
var p = ppath.getPointAtLength(L);
86+
if (!prevPt) { ctx.moveTo(mapX(p.x), mapY(p.y)); }
87+
else {
88+
var dx = p.x - prevPt.x, dy = p.y - prevPt.y;
89+
if (dx * dx + dy * dy > liftSq) ctx.moveTo(mapX(p.x), mapY(p.y));
90+
else ctx.lineTo(mapX(p.x), mapY(p.y));
91+
}
92+
prevPt = p;
93+
}
94+
ctx.stroke();
95+
painted = target;
96+
}
97+
3798
function load(key) {
3899
cur = DATA[key]; if (!cur) return;
39100
if (selSuit) selSuit.value = key;
40-
stage.setAttribute('viewBox', '0 0 ' + cur.w + ' ' + cur.h);
41-
bed.style.aspectRatio = cur.w + '/' + cur.h;
42101
ppath.setAttribute('d', cur.d);
102+
103+
// Crop the stage to the suit's bounding box (+ padding) so every suit is
104+
// drawn large and centred on the same sheet, instead of being letterboxed
105+
// inside the mostly-empty original canvas. The bed keeps its fixed CSS
106+
// shape so the paper is consistent across all editions.
107+
vb = { x: 0, y: 0, w: cur.w, h: cur.h };
108+
try {
109+
var bb = ppath.getBBox();
110+
var pad = Math.max(bb.width, bb.height) * 0.06;
111+
vb = { x: bb.x - pad, y: bb.y - pad, w: bb.width + pad * 2, h: bb.height + pad * 2 };
112+
} catch (e) { /* getBBox unavailable — keep full-canvas viewBox */ }
113+
stage.setAttribute('viewBox', vb.x + ' ' + vb.y + ' ' + vb.w + ' ' + vb.h);
114+
43115
len = ppath.getTotalLength(); if (!len || !isFinite(len)) len = 1;
44-
var md = Math.max(cur.w, cur.h);
116+
var md = Math.max(vb.w, vb.h);
45117
mmPerUnit = 420 / md; // longest side ≈ 420mm
46118
totalMin = (len * mmPerUnit) / FEED;
47119
penC.setAttribute('r', md * 0.014);
48120
penX.setAttribute('d', 'M ' + (-md * 0.032) + ' 0 H ' + (md * 0.032) + ' M 0 ' + (-md * 0.032) + ' V ' + (md * 0.032));
49-
ppath.style.strokeDasharray = len;
50-
ppath.style.strokeDashoffset = len; // hidden until plotting begins
51121
pen.setAttribute('opacity', '0');
52122
if (inktot) inktot.textContent = (len * mmPerUnit / 1000).toFixed(2);
53123
if (total) total.textContent = fmt(totalMin);
54124
if (bedlabel) bedlabel.textContent = 'BED 01 · ' + cur.code;
55-
drawn = 0; playing = true;
125+
drawn = 0; playing = true; last = null; // reset the clock → always start from a blank sheet
126+
resetCanvas();
56127
if (replay) replay.classList.remove('show');
57128
if (playBtn) playBtn.textContent = 'Pause';
58129
render();
59130
}
60131

61132
function render() {
133+
if (!tf.ready) resetCanvas(); // self-heal if the bed wasn't laid out yet
62134
var p = drawn / len;
63-
ppath.style.strokeDashoffset = (len - drawn);
135+
paintTo(drawn);
64136
if (pbar) pbar.style.width = (p * 100) + '%';
65137
if (pct) pct.textContent = Math.round(p * 100);
66138
if (ink) ink.textContent = (drawn * mmPerUnit / 1000).toFixed(2);
@@ -76,6 +148,17 @@
76148
}
77149
}
78150

151+
// Re-fit + repaint the current progress when the bed changes size.
152+
function refit() { resetCanvas(); paintTo(drawn); }
153+
window.addEventListener('resize', refit);
154+
155+
function restart() {
156+
drawn = 0; playing = true; last = null;
157+
resetCanvas(); render();
158+
if (replay) replay.classList.remove('show');
159+
if (playBtn) playBtn.textContent = 'Pause';
160+
}
161+
79162
var last = null;
80163
function frame(t) {
81164
if (last == null) last = t;
@@ -93,21 +176,21 @@
93176
}
94177

95178
function toggle() {
96-
if (drawn >= len) drawn = 0;
179+
if (drawn >= len) { restart(); return; }
97180
playing = !playing;
98181
if (playBtn) playBtn.textContent = playing ? 'Pause' : 'Resume';
99182
if (replay) replay.classList.remove('show');
100183
}
101184
if (playBtn) playBtn.onclick = toggle;
102-
if ($('restart')) $('restart').onclick = function () { drawn = 0; playing = true; if (replay) replay.classList.remove('show'); if (playBtn) playBtn.textContent = 'Pause'; };
185+
if ($('restart')) $('restart').onclick = restart;
103186
if ($('skip')) $('skip').onclick = function () { drawn = len; playing = false; render(); if (replay) replay.classList.add('show'); if (playBtn) playBtn.textContent = 'Replay'; };
104187
if ($('speed')) $('speed').addEventListener('click', function (e) {
105188
var b = e.target.closest('button'); if (!b) return;
106189
[].forEach.call(e.currentTarget.children, function (x) { x.classList.remove('on'); });
107190
b.classList.add('on'); speed = parseFloat(b.dataset.s);
108191
});
109192
if (selSuit) selSuit.addEventListener('change', function (e) { load(e.target.value); });
110-
if (replay) replay.addEventListener('click', function () { drawn = 0; playing = true; replay.classList.remove('show'); if (playBtn) playBtn.textContent = 'Pause'; });
193+
if (replay) replay.addEventListener('click', restart);
111194

112195
// public API (used by the shop "▶ Plot" buttons)
113196
window.PlotflowPlotter = { load: load };

0 commit comments

Comments
 (0)