-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
494 lines (477 loc) · 39.7 KB
/
Copy pathindex.html
File metadata and controls
494 lines (477 loc) · 39.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<title>PlotterFlow - Pen Plotter Layout Editor</title>
<link rel="icon" href="data:,">
<link rel="stylesheet" href="styles.css?v=20260715-1">
<link rel="stylesheet" href="profile.css?v=20260906-1">
<link rel="stylesheet" href="drawing.css?v=20260812-2">
<link rel="stylesheet" href="serial.css?v=20260806-1">
<link rel="stylesheet" href="conversion.css">
<link rel="stylesheet" href="job-ui.css">
<link rel="stylesheet" href="simulation.css">
<link rel="stylesheet" href="simulation-load.css?v=20260711-2">
<link rel="stylesheet" href="help.css">
</head>
<body>
<header class="app-header">
<div><h1>PlotterFlow</h1><p>Pen Plotter Layout Editor</p></div>
<div id="connectionBadge" class="badge">Serial: 未接続</div>
</header>
<nav class="tabs" aria-label="メインメニュー">
<button class="tab active" data-tab="drawing">編集</button>
<button class="tab" data-tab="gcode">G-code</button>
<button class="tab" data-tab="simulation">3Dシミュレーション</button>
<button class="tab" data-tab="jobs">ジョブ実行</button>
<button class="tab" data-tab="serial">Serial</button>
<button class="tab" data-tab="settings">設定</button>
<button class="tab" data-tab="help">ヘルプ</button>
</nav>
<main>
<section id="tab-drawing" class="panel active">
<div class="drawing-layout">
<article class="card drawing-tools-card">
<section class="text-quick-add" aria-labelledby="textAddTitle">
<h2 id="textAddTitle">テキスト</h2>
<label class="sr-only" for="drawingTextInput">描画するテキスト</label>
<textarea id="drawingTextInput" rows="2" placeholder="名前・メッセージを入力"></textarea>
<div class="text-add-row">
<div class="text-writing-toggle" aria-label="文字方向">
<button class="active" data-text-writing="horizontal">横書き</button>
<button data-text-writing="vertical">縦書き</button>
</div>
<button id="drawingAddText" class="primary">追加</button>
</div>
<p id="textFontStatus" class="text-font-status">フォント読込中…</p>
</section>
<section class="stamp-quick-add" aria-labelledby="stampAddTitle">
<h2 id="stampAddTitle">素材を配置</h2>
<button id="openStampLibrary" class="secondary wide stamp-open-button">+ スタンプを追加</button>
<label class="file-button svg-object-file">SVGを読み込んで配置<input id="drawingLoadSvg" type="file" accept="image/svg+xml,.svg"></label>
<details class="svg-paste-details"><summary>SVGテキストを貼り付け</summary><textarea id="drawingSvgText" rows="4" placeholder="<svg ...>"></textarea><button id="drawingAddSvgText" class="secondary wide">キャンバスへ配置</button></details>
<p id="stampLibraryStatus" class="stamp-library-status">素材一覧を読み込み中…</p>
</section>
<h2 class="drawing-tools-title">作図ツール</h2>
<div class="drawing-tools" role="toolbar" aria-label="作図ツール">
<button class="draw-tool active" data-draw-tool="pen" title="自由線">✎<span>ペン</span></button>
<button class="draw-tool" data-draw-tool="rect" title="四角">□<span>四角</span></button>
<button class="draw-tool" data-draw-tool="circle" title="丸">○<span>丸</span></button>
<button class="draw-tool" data-draw-tool="star" title="星">☆<span>星</span></button>
<button class="draw-tool" data-draw-tool="select" title="選択・移動">↖<span>選択</span></button>
<button class="draw-tool" data-draw-tool="eraser" title="触れた範囲を部分消去">⌫<span>消しゴム</span></button>
</div>
<div class="drawing-history-actions">
<button id="drawingUndo" title="Ctrl+Z">↶ Undo</button>
<button id="drawingRedo" title="Ctrl+Y">↷ Redo</button>
<button id="drawingDuplicate">複製</button>
<button id="drawingToFront">最前面へ</button>
<button id="drawingToBack">最背面へ</button>
<button id="drawingDelete" class="danger">選択を削除</button>
<button id="drawingClear" class="danger">全消去</button>
</div>
<section id="drawingSelectionProperties" class="drawing-selection-properties" hidden aria-labelledby="drawingSelectionPropertiesTitle">
<div class="selection-properties-heading"><div><h2 id="drawingSelectionPropertiesTitle">選択オブジェクト</h2><p id="drawingSelectionType">図形</p></div><button id="drawingSelectionDetailed" class="secondary" hidden>詳細</button></div>
<div class="selection-property-grid"><label>中心 X (mm)<input id="drawingSelectionX" type="number" step="0.1"></label><label>中心 Y (mm)<input id="drawingSelectionY" type="number" step="0.1"></label></div>
<div class="selection-property-grid"><label>幅 (mm)<input id="drawingSelectionWidth" type="number" min="0.01" step="0.1"></label><label>高さ (mm)<input id="drawingSelectionHeight" type="number" min="0.01" step="0.1"></label></div>
<label>回転角度 (°)<input id="drawingSelectionRotation" type="number" min="-360" max="360" step="1"></label>
<p class="muted selection-size-hint">幅または高さを変更すると、縦横比を保って拡大縮小します。</p>
<button id="drawingSelectionApply" class="primary wide">プロパティを適用</button>
</section>
<hr>
<h2>キャンバス</h2>
<label>プリセット<select id="drawingCanvasPreset"><option value="30x30">30 × 30 mm</option><option value="100x100">100 × 100 mm</option><option value="150x150">150 × 150 mm</option><option value="200x200">200 × 200 mm</option><option value="297x210">297 × 210 mm</option><option value="custom">カスタム</option></select></label>
<div class="inline compact-inputs"><label>幅 (mm)<input id="drawingWidth" type="number" min="1" max="2000" value="30"></label><label>高さ (mm)<input id="drawingHeight" type="number" min="1" max="2000" value="30"></label></div>
<label>星の頂点数<input id="drawingStarPoints" type="number" min="3" max="24" value="5"></label>
<label>線の単純化 (mm)<input id="drawingSimplify" type="number" min="0" max="10" step="0.05" value="0.2"></label>
<label>消しゴム直径 (mm)<input id="drawingEraserSize" type="number" min="0.2" max="100" step="0.2" value="2"></label>
<p class="drawing-hint">指、Apple Pencil、スタイラス、マウスに対応。選択した図形はドラッグで移動できます。</p>
</article>
<article class="card drawing-canvas-card">
<div class="card-title"><h2>キャンバス</h2><div class="drawing-canvas-actions"><button id="drawingGenerateGcode" class="primary">G-code生成</button><button id="drawingProperties" class="secondary" hidden>プロパティ</button><span id="drawingObjectCount" class="muted">0オブジェクト</span></div></div>
<div id="drawingCanvasWrap" class="drawing-canvas-wrap">
<svg id="drawingCanvas" viewBox="0 0 30 30" aria-label="お絵描きキャンバス"></svg>
</div>
<p id="drawingCoordinates" class="drawing-coordinates">30 × 30 mm</p>
</article>
<article class="card drawing-files-card">
<h2>保存・出力</h2>
<label>保存済み<select id="drawingLibrary"><option value="">新規</option></select></label>
<label>ファイル名<input id="drawingName" value="drawing.plotter.json"></label>
<div class="stack">
<button id="drawingNew">新規</button>
<button id="drawingSave" class="primary">保存 / 上書き</button>
<button id="drawingSaveAs">名前を付けて保存</button>
<button id="drawingDownloadJson">JSONダウンロード</button>
<label class="file-button">JSON読み込み<input id="drawingLoadJson" type="file" accept=".json,.plotter.json,application/json"></label>
<button id="drawingDownloadSvg">SVGダウンロード</button>
</div>
<p id="drawingStatus" class="status">描いてすぐG-code化できます。</p>
</article>
</div>
<dialog id="textPropertiesDialog" class="text-properties-dialog">
<div class="dialog-title"><h2>文字プロパティ</h2><button id="textPropertiesClose" aria-label="閉じる">×</button></div>
<label>フォントサイズ (mm)<input id="textFontSize" type="number" min="1" max="500" step="0.5"></label>
<label>文字間隔 (mm)<input id="textLetterSpacing" type="number" min="-20" max="100" step="0.1"></label>
<label>行間(倍率)<input id="textLineHeight" type="number" min="0.5" max="5" step="0.1"></label>
<label>方向<select id="textWritingMode"><option value="horizontal">横書き</option><option value="vertical">縦書き</option></select></label>
<label>回転角度 (°)<input id="textRotation" type="number" min="-360" max="360" step="1"></label>
<p class="muted">アウトライン描画。将来のシングルライン方式に拡張可能な形式で保存します。</p>
<div class="actions"><button id="textPropertiesApply" class="primary">適用</button><button id="textPropertiesCancel">キャンセル</button></div>
</dialog>
<dialog id="stampLibraryDialog" class="stamp-dialog stamp-library-dialog">
<div class="dialog-title"><h2>スタンプを追加</h2><button id="stampLibraryClose" aria-label="閉じる">×</button></div>
<label>カテゴリ<select id="stampCategory"><option value="all">すべて</option><option value="character">キャラクター</option><option value="bubble">吹き出し</option><option value="effect">効果</option><option value="preset_line">定型線画</option></select></label>
<div id="stampGrid" class="stamp-grid" aria-live="polite"></div>
</dialog>
<dialog id="stampPropertiesDialog" class="stamp-dialog stamp-properties-dialog">
<div class="dialog-title"><h2>SVG素材プロパティ</h2><button id="stampPropertiesClose" aria-label="閉じる">×</button></div>
<p id="stampPropertiesName" class="stamp-properties-name"></p>
<label>サイズ(倍率)<input id="stampScale" type="number" min="0.05" max="50" step="0.05"></label>
<label>回転角度(°)<input id="stampRotation" type="number" min="-360" max="360" step="1"></label>
<div class="stamp-flip-row"><label class="check"><input id="stampFlipX" type="checkbox">左右反転</label><label class="check"><input id="stampFlipY" type="checkbox">上下反転</label></div>
<div class="stamp-layer-actions"><button id="stampToFront">最前面へ</button><button id="stampToBack">最背面へ</button></div>
<div class="actions"><button id="stampPropertiesApply" class="primary">適用</button><button id="stampPropertiesDelete" class="danger">削除</button><button id="stampPropertiesCancel">キャンセル</button></div>
</dialog>
</section>
<section id="tab-convert" class="panel legacy-convert-panel" aria-hidden="true">
<div class="grid two">
<article class="card">
<div class="card-title convert-title"><h2>SVG入力</h2><div class="actions convert-primary-actions"><button id="generateGcode" class="primary">G-code生成</button><button id="generateAndSendSvg" class="secondary">生成して実行</button></div></div>
<label class="file-drop" id="dropZone">
<input id="svgFile" type="file" accept="image/svg+xml,.svg">
<strong>SVGを選択</strong><span>またはここへドロップ</span>
</label>
<label>SVGテキスト(任意)<textarea id="svgText" rows="9" placeholder="<svg ...>"></textarea></label>
<label class="orientation-fix"><input id="svgOrientationFlip" type="checkbox" checked><span><strong>180°回転+左右反転</strong><small>描画方向を実機向けに補正(合成結果はY反転)</small></span></label>
<div class="actions"><button id="loadSvgText" class="secondary">テキストを読み込む</button></div>
<p id="svgStatus" class="status">SVGを読み込んでください。</p>
</article>
</div>
</section>
<section id="tab-gcode" class="panel">
<div class="grid sidebar-layout">
<article class="card library">
<h2>ライブラリ</h2>
<label class="file-drop compact-file-drop"><input id="gcodeFile" type="file" accept=".gcode,.nc,.tap,.txt,text/plain"><strong>G-codeを開く</strong><span>.gcode / .nc / .tap</span></label>
<label>保存済み<select id="gcodeLibrary"><option value="">未選択</option></select></label>
<div class="stack"><button id="newGcode">新規</button><button id="duplicateGcode">複製</button><button id="renameGcode">名前変更</button><button id="deleteGcode" class="danger">削除</button></div>
</article>
<article class="card">
<div class="card-title"><h2>G-code編集</h2><div class="actions"><button id="sendFromEditor" class="primary">Serialで送信</button><span id="gcodeStats" class="muted">0行</span></div></div>
<label>名前<input id="gcodeName" value="untitled.gcode"></label>
<div class="actions"><button id="saveGcode" class="primary">保存</button><button id="downloadGcode">ダウンロード</button><button id="downloadSdGcode" hidden>SD用ダウンロード</button></div>
<textarea id="gcodeEditor" class="code" rows="24" spellcheck="false"></textarea>
</article>
</div>
<article class="card preview-card gcode-preview-card">
<div class="card-title"><h2>G-codeプレビュー</h2><div class="segmented legacy-preview-switch"><button id="showSvgPreview">SVG</button><button id="showGcodePreview" class="active">G-code</button></div></div>
<div id="preview" class="preview"><svg id="previewSvg" aria-label="G-code描画プレビュー"></svg></div>
<div class="legend"><span><i class="draw"></i>描画</span><span><i class="travel"></i>空走</span><span><i class="position"></i>現在位置</span></div>
</article>
</section>
<section id="tab-serial" class="panel">
<div class="grid two">
<article class="card">
<h2>接続・操作</h2>
<div class="serial-profile-strip"><div><small>コントローラープロファイル</small><strong id="serialControllerProfile">GRBL / FluidNC(標準)</strong></div><button id="initializeController" class="secondary" hidden>初期化</button></div>
<div class="inline"><label>Baudrate<input id="serialBaud" type="number" value="115200"></label><button id="connectSerial" class="primary">接続</button><button id="disconnectSerial">切断</button></div>
<div class="control-grid"><button data-command="?">?</button><button data-command="$$">$$</button><button data-command="$X">$X</button><button id="penUpButton">ペンアップ</button><button id="penDownButton">ペンダウン</button><button id="reloadButton" class="secondary">リロード</button><button id="pauseSend">Pause (!)</button><button id="resumeSend">Resume (~)</button><button id="stopSend" class="danger">Stop</button><button id="resetController" class="danger">Reset</button></div>
<section id="serialSendPanel" class="serial-send-panel" aria-labelledby="serialSendTitle">
<h3 id="serialSendTitle">G-code送信</h3>
<label id="serialSourceLabel">送信対象<select id="serialSource"><option value="editor">現在のエディタ</option></select></label>
<div id="serialDestinationGroup" class="serial-destination" hidden>
<label>M5Stack操作モード<select id="m5OperationMode"><option value="normal">通常操作</option><option value="sd-manager">SDカード管理</option></select></label>
<label id="serialDestinationLabel">送信先<select id="serialDestination"><option value="execute">コントローラーで実行</option><option value="sd">M5Stack SDカードへ保存</option></select></label>
<label id="sdFilenameLabel" hidden>SDファイル名<input id="sdFilename" value="untitled.gcode" maxlength="48" inputmode="latin" spellcheck="false"></label>
<p id="sdTransferHint" class="muted" hidden>M5StackをUSB SERIALモードにして転送します。完了後、SD CARDモードから実行できます。</p>
</div>
<div id="serialTransferControls">
<button id="startSend" class="primary wide">送信開始</button>
<div class="progress-row"><progress id="sendProgress" value="0" max="1"></progress><span id="sendProgressText">0 / 0 (0%)</span></div>
</div>
<section id="sdManager" class="sd-manager" hidden aria-labelledby="sdManagerTitle">
<div class="sd-manager-heading">
<div><h3 id="sdManagerTitle">SDカード内のG-code</h3><p id="sdManagerStatus">管理モードを開始してください</p></div>
<div class="actions"><button id="refreshSdFiles">再同期</button><button id="closeSdManager">管理終了</button></div>
</div>
<div id="sdFileList" class="sd-file-list" aria-live="polite"></div>
</section>
</section>
<section class="jog-section" aria-labelledby="jogTitle">
<div class="jog-heading">
<div><h3 id="jogTitle">XYジョグ</h3><p id="jogHint">1タップで選択距離だけ移動</p></div>
<div class="jog-heading-actions">
<span id="jogUnitLabel" class="jog-unit">mm</span>
<label class="keyboard-jog-toggle" title="PCの矢印キーでXYジョグ">
<input id="keyboardJogToggle" type="checkbox">
<span class="keyboard-jog-switch" aria-hidden="true"></span>
<span id="keyboardJogLabel">矢印キー</span><strong id="keyboardJogState">OFF</strong>
</label>
</div>
</div>
<div id="jogCoordinates" class="coordinate-readout" aria-live="polite">
<div class="coordinate-status"><i id="machineStateDot"></i><span id="machineState">未接続</span><small id="positionSource">ワーク座標</small></div>
<div class="coordinate-axis"><span>X</span><strong id="serialXPosition">—</strong><small>mm</small></div>
<div class="coordinate-axis"><span>Y</span><strong id="serialYPosition">—</strong><small>mm</small></div>
<button id="setXyZero" class="zero-button" title="現在位置をワーク座標のX0 Y0に設定"><span>⊙</span> XYを0セット</button>
</div>
<div class="jog-controller">
<div class="jog-pad" aria-label="XY移動">
<div class="jog-rings" aria-hidden="true"></div>
<button class="jog-button jog-up" data-jog-axis="Y" data-jog-sign="-1" aria-label="Y上方向"><span>↑</span><small>Y↑</small></button>
<button class="jog-button jog-left" data-jog-axis="X" data-jog-sign="1" aria-label="X左方向"><span>←</span><small>X←</small></button>
<button id="jogCancel" class="jog-center" aria-label="ジョグ停止" title="ジョグ停止"><span>■</span><small>STOP</small></button>
<button class="jog-button jog-right" data-jog-axis="X" data-jog-sign="-1" aria-label="X右方向"><span>→</span><small>X→</small></button>
<button class="jog-button jog-down" data-jog-axis="Y" data-jog-sign="1" aria-label="Y下方向"><span>↓</span><small>Y↓</small></button>
</div>
<div class="jog-options">
<div id="stsZJogControls" class="sts-z-jog-controls" hidden><button data-jog-axis="Z" data-jog-sign="-1" data-jog-degrees="5">Z −5°</button><button data-jog-axis="Z" data-jog-sign="1" data-jog-degrees="5">Z +5°</button></div>
<label><span id="jogStepLabel">移動量</span><select id="jogStep"><option value="0.15625">0.15625 mm</option><option value="0.625">0.625 mm</option><option value="0.1">0.1 mm</option><option value="1" selected>1 mm</option><option value="2.5">2.5 mm</option><option value="10">10 mm</option><option value="40">40 mm</option><option value="50">50 mm</option></select></label>
<label><span id="jogFeedLabel">速度</span><input id="jogFeed" type="number" min="1" max="50000" step="1" value="1000"><span id="jogFeedUnit">mm/min</span></label>
<div class="jog-command-preview" id="jogCommandPreview">$J=G91 G21 X±1 F1000</div>
</div>
</div>
</section>
<section id="planarArmPanel" class="planar-arm-panel" hidden aria-labelledby="planarArmTitle">
<div class="planar-arm-heading">
<div><h3 id="planarArmTitle">2関節アーム</h3><p>AS5600 J1 / J2</p></div>
<label class="position-telemetry-toggle" title="Pico 2 Wから関節角を取得">
<input id="positionTelemetryToggle" type="checkbox">
<span class="position-telemetry-switch" aria-hidden="true"></span>
<span>位置センサ通信</span><strong id="positionTelemetryState">OFF</strong>
</label>
</div>
<div class="planar-arm-content">
<svg id="planarArmSvg" class="planar-arm-svg" viewBox="0 0 240 210" role="img" aria-label="2関節アームの姿勢">
<line class="arm-guide" x1="120" y1="20" x2="120" y2="200"></line>
<circle class="arm-base" cx="120" cy="20" r="9"></circle>
<line id="planarArmLink1" class="arm-link arm-link-1" x1="120" y1="20" x2="120" y2="98"></line>
<line id="planarArmLink2" class="arm-link arm-link-2" x1="120" y1="98" x2="120" y2="176"></line>
<circle id="planarArmJoint" class="arm-joint" cx="120" cy="98" r="8"></circle>
<circle id="planarArmTip" class="arm-tip" cx="120" cy="176" r="7"></circle>
</svg>
<div class="planar-arm-data">
<div class="arm-sensor-row"><span>J1 / I2C0</span><strong id="armJ1Angle">--.-- deg</strong><small id="armJ1Status">未接続</small></div>
<div class="arm-sensor-row"><span>J2 / I2C1</span><strong id="armJ2Angle">--.-- deg</strong><small id="armJ2Status">未接続</small></div>
<div class="arm-calibration-controls">
<label><input id="armJ1Invert" type="checkbox"> J1反転</label>
<label><input id="armJ2Invert" type="checkbox"> J2反転</label>
</div>
<button id="calibrateArmDown" class="secondary">現在を真下基準</button>
</div>
</div>
</section>
<div class="inline"><input id="manualCommand" placeholder="手動コマンド"><button id="sendManual">送信</button></div>
</article>
<article class="card">
<h2>通信ログ</h2>
<div id="serialLog" class="log" aria-live="off"></div>
<button id="clearLog">ログを消去</button>
</article>
</div>
<article id="serialTrajectoryCard" class="card preview-card serial-trajectory-card" tabindex="-1">
<div class="card-title"><div><h2>送信軌跡</h2><p id="serialTrajectoryName" class="muted">現在のエディタ</p></div><button id="scrollToSerialControls" class="secondary">操作へ戻る</button></div>
<div class="preview serial-trajectory-preview"><svg id="serialTrajectorySvg" aria-label="送信するG-code軌跡"></svg></div>
<div class="legend"><span><i class="draw"></i>描画</span><span><i class="travel"></i>空走</span><span><i class="position"></i>現在位置</span></div>
</article>
</section>
<section id="tab-jobs" class="panel">
<article class="card">
<div class="card-title"><h2>ジョブリスト</h2><button id="addJob" class="primary">ジョブ追加</button></div>
<label class="check"><input id="autoReloadBetweenJobs" type="checkbox">ジョブ間と最後にリロードを自動挿入</label>
<div id="jobList" class="job-list"></div>
<div class="inline job-footer"><label>全体ループ回数<input id="jobLoops" type="number" min="1" value="1"></label><button id="runJobs" class="primary">実行</button><button id="stopJobs" class="danger">停止</button></div>
<div class="progress-row"><progress id="jobProgress" value="0" max="1"></progress><span id="jobProgressText">待機中</span></div>
</article>
<article class="card">
<div class="card-title"><h2>ジョブセット</h2><span class="muted">.plotter-jobs.json</span></div>
<div class="inline">
<label>名前<input id="jobSetName" value="job-set.plotter-jobs.json"></label>
<label>保存済み<select id="jobSetLibrary"><option value="">未選択</option></select></label>
</div>
<div class="actions">
<button id="saveJobSet" class="primary">現在のジョブを保存</button>
<button id="loadJobSet">読み出し</button>
<button id="downloadJobSet">ダウンロード</button>
<button id="deleteJobSet" class="danger">削除</button>
</div>
<label class="file-drop compact-file-drop"><input id="jobSetFile" type="file" accept=".plotter-jobs.json,application/json"><strong>ジョブセットを開く</strong><span>.plotter-jobs.json</span></label>
<p class="muted">ジョブ定義に加えて、参照している保存済みG-codeもスナップショットとして同梱します。別ブラウザで読み込む時も復元しやすい形式です。</p>
</article>
</section>
<section id="tab-simulation" class="panel">
<div class="simulation-layout">
<section class="simulation-stage" aria-labelledby="simulationTitle">
<div class="simulation-toolbar">
<div>
<h2 id="simulationTitle">rackn_delta_assy</h2>
<p id="simulationModelStatus">Fusionアセンブリを読み込み中</p>
<div class="simulation-load-progress" id="simulationLoadRow">
<div class="simulation-load-track" role="progressbar" aria-label="モデル読み込み" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><span id="simulationLoadProgress"></span></div>
<span id="simulationLoadText">0%</span>
</div>
</div>
<div class="simulation-actions">
<button id="simulationResetView" type="button">視点を戻す</button>
<button id="simulationPlay" type="button" class="primary" disabled>G-codeを再生</button>
<button id="simulationStop" type="button" disabled>停止</button>
</div>
</div>
<div id="plotter3dViewport" class="simulation-viewport" aria-label="ペンプロッタ3D表示"></div>
</section>
<aside class="simulation-controls" aria-label="シミュレーション操作">
<div class="simulation-readout">
<span>X<strong id="simulationXValue">0.0 mm</strong></span>
<span>Y<strong id="simulationYValue">0.0 mm</strong></span>
<span>Z<strong id="simulationZValue">0.0 mm</strong></span>
</div>
<label>描画データ<select id="simulationGcodeSource"><option value="__demo__">サンプルの円</option></select></label>
<label>X軸<input id="simulationX" type="range" min="0" max="60" step="0.1" value="0" disabled></label>
<label>Y軸<input id="simulationY" type="range" min="-60" max="60" step="0.1" value="0" disabled></label>
<label>Z軸<input id="simulationZ" type="range" min="0" max="8" step="0.1" value="0" disabled></label>
<label>再生速度<input id="simulationSpeed" type="range" min="10" max="120" step="5" value="45"></label>
<div class="simulation-paper-actions">
<button id="simulationReload" type="button" class="secondary" disabled>リロード動作</button>
<button id="simulationPaperReset" type="button" disabled>紙を戻す</button>
</div>
<div class="simulation-progress-row">
<progress id="simulationProgress" max="1" value="0"></progress>
<span id="simulationProgressText">待機中</span>
</div>
<div class="simulation-legend" aria-label="部品グループ">
<span><i class="fixed"></i>固定</span>
<span><i class="axis-x"></i>X</span>
<span><i class="axis-y"></i>Y</span>
<span><i class="axis-z"></i>Z・ペン</span>
</div>
</aside>
</div>
</section>
<section id="tab-help" class="panel help-panel">
<article class="card help-hero">
<div>
<h2>PlotterFlow ヘルプ</h2>
<p>SVG・手書き・テキスト・スタンプを配置して、GRBLやFluidNC向けG-codeを生成・保存・送信するペンプロッタ用ツールです。自作ペンプロッタらっくん6S向けの機能がありますが、調整することである程度汎用的に使える部分もあると思います。</p>
</div>
<div class="help-note">GitHub Pages / 静的サーバ / localhost 対応</div>
</article>
<div class="help-grid">
<article class="card help-card">
<h3>1. レイアウトを作る</h3>
<ol>
<li>「編集」タブでキャンバスサイズを選びます。</li>
<li>SVG読み込み、手書き、図形、テキスト、スタンプを配置します。</li>
<li>選択ツールで移動・複製・前面/背面・削除を調整します。</li>
<li>「G-code生成」でキャンバス全体を変換します。</li>
</ol>
</article>
<article class="card help-card">
<h3>2. G-codeを確認する</h3>
<ol>
<li>「G-code」タブでプレビューを確認します。</li>
<li>必要ならG-codeを直接編集します。</li>
<li>保存・ダウンロードして再利用できます。</li>
<li>上部の「Serialで送信」から現在のG-codeをすぐ送れます。</li>
</ol>
</article>
<article class="card help-card">
<h3>3. 実機へ送る</h3>
<ol>
<li>Chrome / Edgeで開きます。Web SerialはHTTPSまたはlocalhostが必要です。</li>
<li>「Serial」タブでBaudrateを確認して接続します。</li>
<li>ペンアップ/ダウン、XYジョグ、XY 0セットで動作確認します。</li>
<li>送信は1行ごとにGRBL/FluidNC系の <code>ok</code> を待って進みます。</li>
</ol>
</article>
<article class="card help-card">
<h3>4. 3Dシミュレーションで確認する</h3>
<ol>
<li>「3Dシミュレーション」タブでプロッタのモデルとG-code軌跡を確認します。</li>
<li>「G-codeを再生」で、現在のG-codeエディタ内容を3D上で再生できます。</li>
<li>再生速度、視点リセット、停止を使って、描画範囲や動作方向を事前に確認します。</li>
<li>実機送信前の向き・サイズ・移動範囲確認に使います。</li>
</ol>
</article>
<article class="card help-card">
<h3>5. 複数ジョブを流す</h3>
<ol>
<li>「ジョブ実行」タブでG-code、回数、ループ回数を設定します。</li>
<li>必要な行だけ「+」で前後delay・前後コマンドを展開します。</li>
<li>「ジョブ間と最後にリロード」をONにすると、各実行後に設定のリロードG-codeを挿入します。</li>
<li>ジョブセットは保存・読み込み・ダウンロードできます。</li>
</ol>
</article>
</div>
<article class="card help-card">
<h3>Tips</h3>
<ul class="help-checklist">
<li>GitHub PagesではWeb Serial送信はHTTPS上でChrome/Edgeから確認してください。</li>
<li>フォント、スタンプ、シミュレーション用ファイルはすべてローカル配信です。CDNは不要です。</li>
<li>ブラウザのlocalStorageに設定・G-code・ジョブセットが保存されます。重要データはJSON/G-codeでダウンロードしてください。</li>
<li>実機で向きが逆の場合は「設定」のY軸反転・scale・offsetを確認してください。</li>
<li>引きずりが出る場合はペンアップdelay、移動前リフト待ち、overlap_up設定を調整してください。</li>
</ul>
</article>
</section>
<section id="tab-settings" class="panel">
<form id="settingsForm" class="settings-grid">
<article class="card controller-profile-card">
<div class="development-heading"><div><h2>コントローラープロファイル</h2><p>選択すると関連する接続・出力設定をまとめて反映します。</p></div><label class="development-mode-toggle"><input id="developmentModeToggle" type="checkbox"><span>開発中機能を表示</span></label></div>
<label>プロファイル<select id="controllerProfile" name="controllerProfile"><option value="grbl-fluidnc">GRBL / FluidNC(標準)</option><option value="rp2040-geek-sts3215-id2-id3">RP2040/RP2350-GEEK STS3215 XYZ直結G-code(XY実機テスト)</option><option value="custom">カスタム(値を維持)</option></select></label>
<div id="controllerProfileDescription" class="controller-profile-description" aria-live="polite"></div>
</article>
<article id="stsDirectAxesSettings" class="card" hidden><h2>STS3215 直結軸</h2>
<p class="muted">XYZごとにサーボIDと1 mmあたりのpulse数を指定します。初期値128 pulse/mmでは32 mmが1回転です。</p>
<label>X ID<input name="stsAxisXId" type="number" min="0" max="253" step="1"></label><label>X pulse/mm<input name="stsAxisXPulsesPerMm" type="number" min="0.001" max="28672" step="0.001"></label><label class="check"><input name="stsAxisXInvert" type="checkbox">X反転</label>
<label>Y ID<input name="stsAxisYId" type="number" min="0" max="253" step="1"></label><label>Y pulse/mm<input name="stsAxisYPulsesPerMm" type="number" min="0.001" max="28672" step="0.001"></label><label class="check"><input name="stsAxisYInvert" type="checkbox">Y反転</label>
<label>Z ID<input name="stsAxisZId" type="number" min="0" max="253" step="1"></label><label>Z pulse/mm<input name="stsAxisZPulsesPerMm" type="number" min="0.001" max="28672" step="0.001"></label><label class="check"><input name="stsAxisZInvert" type="checkbox">Z反転</label><label class="check"><input name="stsAxisZEnabled" type="checkbox">Z軸を有効化</label>
</article>
<article class="card"><h2>ペン・Delay</h2>
<label>ペンアップ command<input name="penUpCommand"></label><label>ペンダウン command<input name="penDownCommand"></label>
<label>ペンアップ delay (秒)<input name="penUpDelay" type="number" min="0" step="0.01"></label><label>移動前リフト待ち (秒)<input name="penUpClearanceDelay" type="number" min="0" step="0.01"></label><label>ペンダウン delay (秒)<input name="penDownDelay" type="number" min="0" step="0.01"></label>
<label>アップDelay方式<select name="upDelayMode"><option value="fixed">fixed</option><option value="threshold">threshold</option><option value="distance">distance_based</option></select></label>
<label>長距離しきい値 (mm)<input name="longMoveThreshold" type="number" min="0" step="1"></label><label>short delay (秒)<input name="penUpDelayShort" type="number" min="0" step="0.01"></label><label>long delay (秒)<input name="penUpDelayLong" type="number" min="0" step="0.01"></label>
<label>base delay (秒)<input name="baseDelay" type="number" min="0" step="0.01"></label><label>100mmごとのdelay (秒)<input name="delayPer100" type="number" min="0" step="0.01"></label><label>最大delay (秒)<input name="maxDelay" type="number" min="0" step="0.01"></label>
</article>
<article class="card"><h2>変換</h2>
<label>描画 feed (mm/min)<input name="drawFeed" type="number" min="1"></label><label>空走 feed (mm/min)<input name="travelFeed" type="number" min="1"></label><label>サンプル間隔 (mm)<input name="sampleInterval" type="number" min="0.05" step="0.05"></label>
<label>scale<input name="scale" type="number" step="0.01"></label><label>offset X (mm)<input name="offsetX" type="number" step="0.1"></label><label>offset Y (mm)<input name="offsetY" type="number" step="0.1"></label><label class="check"><input name="yFlip" type="checkbox">Y軸反転</label>
<label>最適化<select name="optimization"><option value="safe">safe</option><option value="overlap_up">overlap_up(推奨)</option><option value="overlap_down" hidden disabled>overlap_down(実験)</option><option value="off">off</option></select></label>
<label>down lead距離 (mm)<input name="downLeadDistance" type="number" min="0" step="0.1"></label><label>必要down時間 (秒)<input name="requiredPenDownTime" type="number" min="0" step="0.01"></label>
</article>
<article class="card"><h2>出力・接続</h2>
<label>Baudrate<input name="baudrate" type="number"></label><label>ok応答 timeout (ms)<input name="okTimeoutMs" type="number" min="1000" step="1000"></label>
<label>停止方式<select name="stopStrategy"><option value="hold-pen-up">feed hold (!) → ペンアップ</option><option value="cancel-pen-up">motion cancel (0x85) → ペンアップ</option></select></label>
<label>初期化 command<textarea name="initializeCommand" rows="5"></textarea></label><label>切断 command<input name="disconnectCommand"></label>
<label>ヘッダ<textarea name="header" rows="5"></textarea></label><label>フッタ<textarea name="footer" rows="5"></textarea></label>
<label>リロード動作 G-code<textarea name="reloadGcode" class="code" rows="18" spellcheck="false"></textarea></label><p class="muted">Serialの「リロード」とジョブの「リロード動作(設定)」で共通使用します。</p>
<button type="submit" class="primary">設定を保存</button><button type="button" id="resetSettings">初期値へ戻す</button><p id="settingsStatus" class="status"></p>
</article>
</form>
</section>
</main>
<div id="toast" role="status"></div>
<script src="app.js?v=20260906-1"></script>
<script src="vendor/opentypejs/opentype.min.js"></script>
<script src="drawing.js?v=20260812-2"></script>
<script>
(() => {
let simulationStarted = false;
const startSimulation = () => {
if (simulationStarted) return;
simulationStarted = true;
document.querySelector("#simulationLoadProgress").style.width = "1%";
document.querySelector("#simulationLoadText").textContent = "JS読込中";
const script = document.createElement("script");
script.src = "plotter-simulator.bundle.js?v=20260711-4";
script.onerror = () => { document.querySelector("#simulationLoadText").textContent = "JS読込失敗"; };
document.body.appendChild(script);
};
document.querySelector('[data-tab="simulation"]').addEventListener("click", startSimulation, { once: true });
})();
</script>
</body>
</html>