Guest User

3DloopBambulabA1MINI

a guest
Jul 15th, 2025
19
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 30.87 KB | None | 0 0
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>G-code Farm Loop Modifier (.3mf Support)</title>
  7. <style>
  8. * { margin: 0; padding: 0; box-sizing: border-box; }
  9. body { font-family: Arial, sans-serif; background: #f5f5f5; padding: 20px; }
  10. .container { max-width: 1000px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
  11. h1 { color: #333; margin-bottom: 20px; text-align: center; }
  12. .section { margin-bottom: 30px; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
  13. .section h2 { color: #555; margin-bottom: 15px; font-size: 1.2em; }
  14. .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px; }
  15. .param-group { display: flex; flex-direction: column; }
  16. .param-group label { margin-bottom: 5px; font-weight: bold; color: #666; }
  17. .param-group input, .param-group select { padding: 8px; border: 1px solid #ccc; border-radius: 4px; }
  18. .btn { padding: 10px 20px; color: white; border: none; border-radius: 4px; cursor: pointer; margin-right: 10px; margin-bottom: 10px; }
  19. .btn:disabled { background: #ccc; cursor: not-allowed; }
  20. .btn-primary { background: #007bff; }
  21. .btn-primary:hover:not(:disabled) { background: #0056b3; }
  22. .btn-success { background: #28a745; }
  23. .btn-success:hover:not(:disabled) { background: #218838; }
  24. .btn-info { background: #17a2b8; }
  25. .btn-info:hover:not(:disabled) { background: #138496; }
  26. .file-input { padding: 10px; border: 2px dashed #ccc; border-radius: 4px; text-align: center; cursor: pointer; }
  27. .file-input:hover { border-color: #007bff; }
  28. .file-info { background: #e8f5e8; padding: 10px; border-radius: 4px; margin-top: 10px; }
  29. .status { padding: 10px; border-radius: 4px; margin-bottom: 15px; }
  30. .status.success { background: #d4edda; color: #155724; }
  31. .status.error { background: #f8d7da; color: #721c24; }
  32. .status.info { background: #cce7ff; color: #004085; }
  33. .status.warning { background: #fff3cd; color: #856404; }
  34. .preview { background: #f8f9fa; padding: 15px; border-radius: 4px; max-height: 400px; overflow-y: auto; }
  35. .preview pre { margin: 0; white-space: pre-wrap; font-family: monospace; font-size: 0.9em; }
  36. .hidden { display: none; }
  37. .file-type-badge { display: inline-block; padding: 3px 8px; color: white; border-radius: 12px; font-size: 0.8em; margin-left: 10px; }
  38. .file-type-badge.threemf { background: #ff6b35; }
  39. .file-type-badge.gcode { background: #28a745; }
  40. .progress { width: 100%; height: 20px; background: #e9ecef; border-radius: 10px; overflow: hidden; margin: 10px 0; }
  41. .progress-bar { height: 100%; background: #007bff; transition: width 0.3s ease; }
  42. </style>
  43. </head>
  44. <body>
  45. <div class="container">
  46. <h1>G-code Farm Loop Modifier (.3mf Support)</h1>
  47.  
  48. <div class="section">
  49. <h2>Load File</h2>
  50. <div class="file-input" onclick="document.getElementById('fileInput').click()">
  51. <input type="file" id="fileInput" accept=".gcode,.g,.txt,.3mf" style="display: none;">
  52. Click to select G-code file (.gcode, .g, .txt) or 3MF file (.3mf)
  53. </div>
  54. <div id="fileInfo" class="file-info hidden"></div>
  55. <div id="progressContainer" class="hidden">
  56. <div class="progress">
  57. <div id="progressBar" class="progress-bar" style="width: 0%"></div>
  58. </div>
  59. <div id="progressText">Processing...</div>
  60. </div>
  61. </div>
  62.  
  63. <div class="section">
  64. <h2>Parameters</h2>
  65. <div class="grid">
  66. <div class="param-group">
  67. <label for="loopCount">Number of Loops</label>
  68. <input type="number" id="loopCount" min="1" max="50" value="3">
  69. </div>
  70. <div class="param-group">
  71. <label for="cooldownTemp">Cooldown Temperature (°C)</label>
  72. <input type="number" id="cooldownTemp" min="20" max="200" value="25">
  73. </div>
  74. <div class="param-group">
  75. <label for="waitTime">Wait Time (seconds)</label>
  76. <input type="number" id="waitTime" min="0" max="3600" value="30">
  77. </div>
  78. <div class="param-group">
  79. <label for="wipeEnabled">Enable Wipe Sequence</label>
  80. <select id="wipeEnabled" disabled>
  81. <option value="true" selected>Yes (Always Enabled)</option>
  82. </select>
  83. </div>
  84. </div>
  85. </div>
  86.  
  87. <div class="section">
  88. <h2>Process</h2>
  89. <div id="status" class="status hidden"></div>
  90. <button id="processBtn" class="btn btn-primary" disabled>Process G-code</button>
  91. <button id="previewBtn" class="btn btn-primary" disabled>Preview Added Blocks</button>
  92. <div id="downloadSection" class="hidden">
  93. <button id="downloadGcodeBtn" class="btn btn-success">Download G-code</button>
  94. <button id="download3mfBtn" class="btn btn-info hidden">Download 3MF</button>
  95. </div>
  96. </div>
  97.  
  98. <div id="previewSection" class="section hidden">
  99. <h2>Preview - New G-code Blocks Added (First Loop)</h2>
  100. <div class="preview">
  101. <pre id="previewContent"></pre>
  102. </div>
  103. </div>
  104. </div>
  105.  
  106. <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
  107. <script>
  108. class GCodeFarmModifier {
  109. constructor() {
  110. this.originalGCode = '';
  111. this.modifiedGCode = '';
  112. this.fileName = '';
  113. this.fileType = '';
  114. this.addedBlocks = '';
  115. this.threemfZip = null;
  116. this.gcodeFilePath = null;
  117. this.init();
  118. }
  119.  
  120. init() {
  121. const $ = id => document.getElementById(id);
  122. $('fileInput').addEventListener('change', e => this.loadFile(e));
  123. $('processBtn').addEventListener('click', () => this.process());
  124. $('downloadGcodeBtn').addEventListener('click', () => this.downloadGcode());
  125. $('download3mfBtn').addEventListener('click', () => this.download3mf());
  126. $('previewBtn').addEventListener('click', () => this.preview());
  127. }
  128.  
  129. updateProgress(percent, text) {
  130. const container = document.getElementById('progressContainer');
  131. const bar = document.getElementById('progressBar');
  132. const textEl = document.getElementById('progressText');
  133.  
  134. container.classList.toggle('hidden', false);
  135. bar.style.width = percent + '%';
  136. textEl.textContent = text;
  137.  
  138. if (percent >= 100) setTimeout(() => container.classList.add('hidden'), 2000);
  139. }
  140.  
  141. async loadFile(event) {
  142. const file = event.target.files[0];
  143. if (!file) return;
  144.  
  145. this.fileName = file.name;
  146. this.fileType = file.name.toLowerCase().endsWith('.3mf') ? '3mf' : 'gcode';
  147. this.reset();
  148.  
  149. try {
  150. this.fileType === '3mf' ? await this.load3mf(file) : await this.loadGcode(file);
  151. } catch (error) {
  152. this.showStatus(`Error loading file: ${error.message}`, 'error');
  153. }
  154. }
  155.  
  156. reset() {
  157. this.originalGCode = '';
  158. this.modifiedGCode = '';
  159. this.addedBlocks = '';
  160. this.threemfZip = null;
  161. this.gcodeFilePath = null;
  162. }
  163.  
  164. async loadGcode(file) {
  165. this.updateProgress(30, 'Reading G-code file...');
  166.  
  167. const text = await new Promise((resolve, reject) => {
  168. const reader = new FileReader();
  169. reader.onload = e => resolve(e.target.result);
  170. reader.onerror = () => reject(new Error('Failed to read file'));
  171. reader.readAsText(file);
  172. });
  173.  
  174. if (!text?.trim()) throw new Error('G-code file is empty');
  175.  
  176. this.originalGCode = text;
  177. this.updateProgress(100, 'G-code loaded successfully!');
  178. this.showFileInfo(file);
  179. this.updateButtons();
  180. }
  181.  
  182. async load3mf(file) {
  183. this.updateProgress(30, 'Parsing 3MF structure...');
  184.  
  185. const buffer = await new Promise((resolve, reject) => {
  186. const reader = new FileReader();
  187. reader.onload = e => resolve(e.target.result);
  188. reader.onerror = () => reject(new Error('Failed to read file'));
  189. reader.readAsArrayBuffer(file);
  190. });
  191.  
  192. const zip = new JSZip();
  193. this.threemfZip = await zip.loadAsync(buffer);
  194.  
  195. this.updateProgress(70, 'Extracting G-code...');
  196. const gcodeResult = await this.extractGcodeFrom3mf(this.threemfZip);
  197.  
  198. if (!gcodeResult?.content?.trim()) throw new Error('No G-code found in 3MF file');
  199.  
  200. this.originalGCode = gcodeResult.content;
  201. this.gcodeFilePath = gcodeResult.path;
  202.  
  203. this.updateProgress(100, 'Complete!');
  204. this.showFileInfo(file);
  205. this.updateButtons();
  206. }
  207.  
  208. async extractGcodeFrom3mf(zip) {
  209. const paths = ['Metadata/plate_1.gcode', 'Metadata/plate_2.gcode', 'Metadata/plate_3.gcode',
  210. 'Metadata/plate_4.gcode', 'Metadata/plate_5.gcode', 'Metadata/print.gcode'];
  211.  
  212. for (const path of paths) {
  213. const file = zip.file(path);
  214. if (file) {
  215. try {
  216. const content = await file.async('text');
  217. if (this.isGcode(content)) return { content, path };
  218. } catch (e) { continue; }
  219. }
  220. }
  221.  
  222. // Search all files
  223. const allFiles = [];
  224. zip.forEach((path, file) => !file.dir && allFiles.push(path));
  225.  
  226. for (const path of allFiles.sort((a, b) => (a.includes('.gcode') ? -1 : 1))) {
  227. const file = zip.file(path);
  228. if (file) {
  229. try {
  230. const content = await file.async('text');
  231. if (this.isGcode(content)) return { content, path };
  232. } catch (e) { continue; }
  233. }
  234. }
  235.  
  236. return null;
  237. }
  238.  
  239. isGcode(content) {
  240. if (!content || content.length < 50) return false;
  241. const patterns = [/^G[0-9]+/m, /^M[0-9]+/m, /^T[0-9]+/m, /; generated by/i, /G28/, /G1.*E/];
  242. return patterns.filter(p => p.test(content)).length >= 3;
  243. }
  244.  
  245. showFileInfo(file) {
  246. const info = document.getElementById('fileInfo');
  247. const badge = this.fileType === '3mf' ?
  248. '<span class="file-type-badge threemf">3MF</span>' :
  249. '<span class="file-type-badge gcode">G-code</span>';
  250. const lines = this.originalGCode.split('\n').length;
  251. const size = (file.size / 1024).toFixed(1);
  252.  
  253. info.innerHTML = `<strong>File:</strong> ${file.name} ${badge}<br>
  254. <strong>Size:</strong> ${size} KB<br>
  255. <strong>G-code Lines:</strong> ${lines}`;
  256. info.classList.remove('hidden');
  257. }
  258.  
  259. updateButtons() {
  260. const hasFile = !!this.originalGCode;
  261. const hasModified = !!this.modifiedGCode;
  262.  
  263. document.getElementById('processBtn').disabled = !hasFile;
  264. document.getElementById('previewBtn').disabled = !hasFile;
  265. document.getElementById('downloadSection').classList.toggle('hidden', !hasFile);
  266. document.getElementById('downloadGcodeBtn').disabled = !hasModified;
  267. document.getElementById('download3mfBtn').disabled = !hasModified;
  268. document.getElementById('download3mfBtn').classList.toggle('hidden', this.fileType !== '3mf');
  269. }
  270.  
  271. showStatus(message, type = 'info') {
  272. const status = document.getElementById('status');
  273. status.textContent = message;
  274. status.className = `status ${type}`;
  275. status.classList.remove('hidden');
  276. }
  277.  
  278. process() {
  279. if (!this.originalGCode) return this.showStatus('Please load a file first.', 'error');
  280.  
  281. try {
  282. const params = {
  283. loopCount: parseInt(document.getElementById('loopCount').value),
  284. cooldownTemp: parseInt(document.getElementById('cooldownTemp').value),
  285. waitTime: parseInt(document.getElementById('waitTime').value)
  286. };
  287.  
  288. this.showStatus('Processing G-code...', 'info');
  289.  
  290. const { header, printCore, footer } = this.parseGCode();
  291. this.modifiedGCode = this.generateLoops(header, printCore, footer, params);
  292. this.generatePreview(params);
  293.  
  294. this.showStatus(`Successfully processed G-code with ${params.loopCount} loops!`, 'success');
  295. this.updateButtons();
  296. } catch (error) {
  297. this.showStatus(`Error: ${error.message}`, 'error');
  298. }
  299. }
  300.  
  301. parseGCode() {
  302. const lines = this.originalGCode.split('\n');
  303. let headerEnd = lines.findIndex(line => {
  304. const l = line.trim();
  305. return (l.startsWith('G1') && l.includes('E')) || l.includes('LAYER_CHANGE') ||
  306. l.includes('layer') || (l.includes('Z0.') && l.startsWith('G1'));
  307. });
  308.  
  309. let footerStart = -1;
  310. for (let i = lines.length - 1; i >= 0; i--) {
  311. const l = lines[i].trim();
  312. if (l.includes('M400') || l.includes('M104 S0') || l.includes('M140 S0') ||
  313. l.includes('M84') || (l.startsWith('G1') && l.includes('Z'))) {
  314. footerStart = i;
  315. break;
  316. }
  317. }
  318.  
  319. if (headerEnd === -1) headerEnd = Math.min(50, Math.floor(lines.length * 0.1));
  320. if (footerStart === -1) footerStart = Math.max(lines.length - 20, Math.floor(lines.length * 0.9));
  321.  
  322. return {
  323. header: lines.slice(0, headerEnd).join('\n'),
  324. printCore: lines.slice(headerEnd, footerStart).join('\n'),
  325. footer: lines.slice(footerStart).join('\n')
  326. };
  327. }
  328.  
  329. generateLoops(header, printCore, footer, params) {
  330. let result = '';
  331. const { loopCount, cooldownTemp, waitTime } = params;
  332.  
  333. for (let i = 1; i <= loopCount; i++) {
  334. result += `\n; === LOOP ${i} OF ${loopCount} ===\n`;
  335.  
  336. if (header?.trim()) {
  337. result += (i === 1 ? this.updateTimeEstimates(header, params) : header);
  338. if (!header.endsWith('\n')) result += '\n';
  339. }
  340.  
  341. if (printCore?.trim()) {
  342. result += printCore;
  343. if (!printCore.endsWith('\n')) result += '\n';
  344. }
  345.  
  346. result += '\n' + this.getWipeSequence(cooldownTemp) + '\n';
  347.  
  348. if (i < loopCount) {
  349. result += `\n; === SETUP FOR NEXT LOOP ===\n`;
  350. result += `M104 S150 ; Set hotend temperature\n`;
  351. result += `M140 S${cooldownTemp} ; Set bed temperature\n`;
  352. result += `M109 S150 ; Wait for hotend temperature\n`;
  353. result += `M190 S${cooldownTemp} ; Wait for bed temperature\n`;
  354. result += `G4 S${waitTime} ; Wait between loops\n`;
  355. }
  356. }
  357.  
  358. if (footer?.trim()) {
  359. result += '\n' + footer;
  360. if (!footer.endsWith('\n')) result += '\n';
  361. }
  362.  
  363. return result;
  364. }
  365.  
  366. generatePreview(params) {
  367. const { loopCount, cooldownTemp, waitTime } = params;
  368. const bedCooldownTime = this.calculateBedCooldownTime(cooldownTemp);
  369. const formatTime = s => `${Math.floor(s/60)}m ${s%60}s`;
  370.  
  371. this.addedBlocks = `; === LOOP 1 OF ${loopCount} ===
  372. ; HEADER_BLOCK_START
  373. ; BambuStudio [version]
  374. ; model printing time: [original_time × ${loopCount}]; total estimated time: [includes cooldown & wipe]
  375. ; [Original G-code header and print core would be here]
  376.  
  377. ; === COOLDOWN & WIPE SEQUENCE ===
  378. ; Bed cooldown time (65°C → ${cooldownTemp}°C): ${formatTime(bedCooldownTime)}
  379. ; Wipe sequence time: 1m 10s (optimized back-to-front push)
  380. ${this.getWipeSequence(cooldownTemp)}
  381.  
  382. ${loopCount > 1 ? `; === SETUP FOR NEXT LOOP ===
  383. ; Wait time between loops: ${formatTime(waitTime)}
  384. M104 S150 ; Set hotend temperature
  385. M140 S${cooldownTemp} ; Set bed temperature
  386. M109 S150 ; Wait for hotend temperature
  387. M190 S${cooldownTemp} ; Wait for bed temperature
  388. G4 S${waitTime} ; Wait between loops
  389.  
  390. ` : ''}; === TIME BREAKDOWN FOR ${loopCount} LOOPS ===
  391. ; Total bed cooldown time: ${formatTime(bedCooldownTime * loopCount)}
  392. ; Total wipe time: ${formatTime(70 * loopCount)}
  393. ; Total wait time between loops: ${formatTime(waitTime * (loopCount - 1))}
  394. ; Print time multiplied by ${loopCount} loops`;
  395. }
  396.  
  397. updateTimeEstimates(header, params) {
  398. const { loopCount, cooldownTemp, waitTime } = params;
  399. const lines = header.split('\n');
  400. let foundBambuStudio = false;
  401.  
  402. return lines.map((line, index) => {
  403. // Check if this line is HEADER_BLOCK_START
  404. if (line.trim() === '; HEADER_BLOCK_START') {
  405. foundBambuStudio = false;
  406. return line;
  407. }
  408.  
  409. // Check if this line is BambuStudio version line
  410. if (line.trim().match(/^;\s*BambuStudio\s+[\d.]+/)) {
  411. foundBambuStudio = true;
  412. return line;
  413. }
  414.  
  415. // Check if this is the time estimation line after BambuStudio
  416. if (foundBambuStudio && line.includes('model printing time:') && line.includes('total estimated time:')) {
  417. const bambuTimeMatch = line.match(/^(;\s*model printing time:\s*)([^;]+)(;\s*total estimated time:\s*)([^;]*)(.*?)$/);
  418. if (bambuTimeMatch) {
  419. const [, prefix, modelTime, middle, totalTime, suffix] = bambuTimeMatch;
  420. const originalSeconds = this.parseTime(modelTime.trim());
  421. const additionalTime = (180 + this.calculateBedCooldownTime(cooldownTemp) + 70) * loopCount + waitTime * (loopCount - 1);
  422. const newModelTime = originalSeconds * loopCount;
  423. const newTotalTime = newModelTime + additionalTime;
  424.  
  425. foundBambuStudio = false; // Reset for next occurrence
  426. return `${prefix}${this.formatTime(newModelTime)}${middle}${this.formatTime(newTotalTime)}${suffix}`;
  427. }
  428. }
  429.  
  430. // Fallback for other time estimation formats
  431. const timeMatch = line.match(/^(;\s*estimated printing time.*?:?\s*)(\d+[hmsd\s]+)(.*)$/i);
  432. if (timeMatch) {
  433. const [, prefix, timeStr, suffix] = timeMatch;
  434. const originalSeconds = this.parseTime(timeStr);
  435. const additionalTime = (180 + this.calculateBedCooldownTime(cooldownTemp) + 70) * loopCount + waitTime * (loopCount - 1);
  436.  
  437. return `${prefix}${this.formatTime(originalSeconds * loopCount + additionalTime)}${suffix}`;
  438. }
  439.  
  440. return line;
  441. }).join('\n');
  442. }
  443.  
  444. calculateBedCooldownTime(cooldownTemp) {
  445. let totalTime = 0;
  446. let currentTemp = 65;
  447.  
  448. while (currentTemp > cooldownTemp) {
  449. totalTime += currentTemp > 50 ? 12 : currentTemp > 40 ? 18 : 30;
  450. currentTemp--;
  451. }
  452.  
  453. // Add extra time for temperature stability and verification
  454. totalTime += 90; // 30s + 60s additional cooling time
  455.  
  456. return totalTime;
  457. }
  458.  
  459. parseTime(timeStr) {
  460. const matches = {
  461. d: timeStr.match(/(\d+)d/),
  462. h: timeStr.match(/(\d+)h/),
  463. m: timeStr.match(/(\d+)m/),
  464. s: timeStr.match(/(\d+)s/)
  465. };
  466.  
  467. return (matches.d ? parseInt(matches.d[1]) * 86400 : 0) +
  468. (matches.h ? parseInt(matches.h[1]) * 3600 : 0) +
  469. (matches.m ? parseInt(matches.m[1]) * 60 : 0) +
  470. (matches.s ? parseInt(matches.s[1]) : 0);
  471. }
  472.  
  473. formatTime(seconds) {
  474. const units = [
  475. [86400, 'd'],
  476. [3600, 'h'],
  477. [60, 'm'],
  478. [1, 's']
  479. ];
  480.  
  481. let result = '';
  482. for (const [divisor, unit] of units) {
  483. const count = Math.floor(seconds / divisor);
  484. if (count > 0) {
  485. result += `${count}${unit} `;
  486. seconds %= divisor;
  487. }
  488. }
  489.  
  490. return result.trim() || '0s';
  491. }
  492.  
  493. getWipeSequence(cooldownTemp) {
  494. return `; === COOLDOWN BEFORE WIPE ===
  495. M104 S0 ; Turn off hotend
  496. ; === ENHANCED COOLING WAIT ===
  497. M117 bed cooldown ; Display message
  498. M140 S${cooldownTemp} ; Set bed to cooldown temperature
  499. ; Wait for bed to cool down with temperature monitoring
  500. M190 S${cooldownTemp} ; Wait for bed to reach cooldown temperature
  501. M117 wait 5s ; Display message
  502. G4 S5 ; wait to ensure temperature stability
  503.  
  504. ; === WARNING SOUND BEFORE WIPE ===
  505. M17 ; Enable motors
  506. M400 S1 ; Wait for movement to complete
  507. M1006 S1 ; Sound system on
  508.  
  509. ; Sharp, alternating pitch alarm pattern
  510. M1006 A60 B20 L80 C45 D8 M100 E60 F8 N100 ; High pitch tone
  511. M1006 A20 B20 L80 C35 D8 M100 E25 F8 N100 ; Low pitch tone
  512. M1006 A60 B20 L80 C45 D8 M100 E60 F8 N100 ; High pitch again
  513. M1006 A20 B20 L80 C35 D8 M100 E25 F8 N100 ; Low again
  514. M1006 A60 B20 L80 C45 D8 M100 E60 F8 N100 ; Final high pitch
  515.  
  516. M1006 W ; Wait for sound to finish
  517. M18 ; Disable motors
  518. G4 S5 ; Pause for 5 seconds
  519.  
  520. ; === OPTIMIZED BACK-TO-FRONT WIPE SEQUENCE ===
  521. G1 X160 Y185 F10000 ; Move to rear-right
  522. G1 Z2 F600 ; Set wipe height
  523. M400 ; Wait for moves
  524. M117 Wipe Level 1...
  525.  
  526. ; Sweep across X160→X0 Between Y185 and Y140
  527. G1 X160 F10000
  528. G1 Y140 F2000
  529. G1 Y185 F10000
  530. G1 X140 F10000
  531. G1 Y140 F2000
  532. G1 Y185 F10000
  533. G1 X120 F10000
  534. G1 Y140 F2000
  535. G1 Y185 F10000
  536. G1 X100 F10000
  537. G1 Y140 F2000
  538. G1 Y185 F10000
  539. G1 X80 F10000
  540. G1 Y140 F2000
  541. G1 Y185 F10000
  542. G1 X60 F10000
  543. G1 Y140 F2000
  544. G1 Y185 F10000
  545. G1 X40 F10000
  546. G1 Y140 F2000
  547. G1 Y185 F10000
  548. G1 X20 F10000
  549. G1 Y140 F2000
  550. G1 Y185 F10000
  551. G1 X0 F10000
  552. G1 Y140 F2000
  553.  
  554. G1 Y180 F30000; Move Y axis
  555. G1 Y165 F30000
  556.  
  557. ; --- Sweep Y140→Y95 ---
  558. M117 Wipe Level 2...
  559. G1 X160 Y150 F10000
  560. G1 Y95 F2000
  561. G1 Y150 F10000
  562. G1 X140 F10000
  563. G1 Y95 F2000
  564. G1 Y150 F10000
  565. G1 X120 F10000
  566. G1 Y95 F2000
  567. G1 Y150 F10000
  568. G1 X100 F10000
  569. G1 Y95 F2000
  570. G1 Y150 F10000
  571. G1 X80 F10000
  572. G1 Y95 F2000
  573. G1 Y150 F10000
  574. G1 X60 F10000
  575. G1 Y95 F2000
  576. G1 Y150 F10000
  577. G1 X40 F10000
  578. G1 Y95 F2000
  579. G1 Y150 F10000
  580. G1 X20 F10000
  581. G1 Y95 F2000
  582. G1 Y150 F10000
  583. G1 X0 F10000
  584. G1 Y95 F2000
  585. G1 Y150 F10000
  586.  
  587. G1 Y180 F30000; Move Y axis
  588. G1 Y165 F30000
  589.  
  590. ; --- Sweep Y95→Y50 ---
  591. G1 X160 Y105 F10000
  592. G1 Y50 F2000
  593. G1 Y105 F10000
  594. G1 X140 F10000
  595. G1 Y50 F2000
  596. G1 Y105 F10000
  597. G1 X120 F10000
  598. G1 Y50 F2000
  599. G1 Y105 F10000
  600. G1 X100 F10000
  601. G1 Y50 F2000
  602. G1 Y105 F10000
  603. G1 X80 F10000
  604. G1 Y50 F2000
  605. G1 Y105 F10000
  606. G1 X60 F10000
  607. G1 Y50 F2000
  608. G1 Y105 F10000
  609. G1 X40 F10000
  610. G1 Y50 F2000
  611. G1 Y105 F10000
  612. G1 X20 F10000
  613. G1 Y50 F2000
  614. G1 Y105 F10000
  615. G1 X0 F10000
  616. G1 Y50 F2000
  617. G1 Y105 F10000
  618.  
  619. G1 Y180 F30000; Move Y axis
  620. G1 Y165 F30000
  621.  
  622. ; --- Sweep Y60→Y0 ---
  623. G1 X160 Y60 F20000
  624. G1 Y0 F2000
  625. G1 Y60 F20000
  626. G1 X140 F20000
  627. G1 Y0 F2000
  628. G1 Y60 F20000
  629. G1 X120 F20000
  630. G1 Y0 F2000
  631. G1 Y60 F20000
  632. G1 X100 F20000
  633. G1 Y0 F2000
  634. G1 Y60 F20000
  635. G1 X80 F20000
  636. G1 Y0 F2000
  637. G1 Y60 F20000
  638. G1 X60 F20000
  639. G1 Y0 F2000
  640. G1 Y60 F20000
  641. G1 X40 F20000
  642. G1 Y0 F2000
  643. G1 Y60 F20000
  644. G1 X20 F20000
  645. G1 Y0 F2000
  646. G1 Y60 F20000
  647. G1 X0 F20000
  648. G1 Y0 F2000
  649. G1 Y60 F20000
  650.  
  651. G1 Y180 F30000; Move Y axis
  652. G1 Y165 F30000
  653.  
  654. M117 Wipe Complete
  655.  
  656. ; === COMPLETION ===
  657. M400 ; Wait for wipe complete
  658. ; All parts should now be pushed to front of bed for easy removal`;
  659. }
  660.  
  661. downloadGcode() {
  662. if (!this.modifiedGCode) return this.showStatus('Please process the G-code first.', 'warning');
  663.  
  664. const blob = new Blob([this.modifiedGCode], { type: 'text/plain' });
  665. const url = URL.createObjectURL(blob);
  666. const a = document.createElement('a');
  667.  
  668. a.href = url;
  669. a.download = this.generateFileName('gcode');
  670. a.click();
  671. URL.revokeObjectURL(url);
  672.  
  673. this.showStatus('G-code downloaded successfully!', 'success');
  674. }
  675.  
  676. async download3mf() {
  677. if (!this.modifiedGCode || !this.threemfZip) return this.showStatus('Please process the G-code first.', 'warning');
  678.  
  679. try {
  680. this.showStatus('Creating modified 3MF file...', 'info');
  681.  
  682. const newZip = new JSZip();
  683.  
  684. // Copy all files
  685. const promises = [];
  686. this.threemfZip.forEach((path, file) => {
  687. if (!file.dir) {
  688. promises.push(file.async('arraybuffer').then(content => newZip.file(path, content)));
  689. }
  690. });
  691.  
  692. await Promise.all(promises);
  693.  
  694. // Update G-code
  695. newZip.file(this.gcodeFilePath || 'Metadata/print.gcode', this.modifiedGCode);
  696.  
  697. const content = await newZip.generateAsync({
  698. type: 'blob',
  699. compression: 'DEFLATE',
  700. compressionOptions: { level: 6 }
  701. });
  702.  
  703. const url = URL.createObjectURL(content);
  704. const a = document.createElement('a');
  705.  
  706. a.href = url;
  707. a.download = this.generateFileName('3mf');
  708. a.click();
  709. URL.revokeObjectURL(url);
  710.  
  711. this.showStatus('3MF file downloaded successfully!', 'success');
  712. } catch (error) {
  713. this.showStatus(`Error creating 3MF file: ${error.message}`, 'error');
  714. }
  715. }
  716.  
  717. generateFileName(extension) {
  718. const params = {
  719. loopCount: document.getElementById('loopCount').value,
  720. cooldownTemp: document.getElementById('cooldownTemp').value
  721. };
  722. const now = new Date();
  723. const timestamp = now.toISOString().slice(0, 10).replace(/-/g, '');
  724. const time = now.toTimeString().slice(0, 8).replace(/:/g, '');
  725. const baseName = this.fileName.replace(/\.[^/.]+$/, '');
  726.  
  727. return `${time}-${baseName}_${timestamp}-${params.loopCount}loop_${params.cooldownTemp}bed.${extension}`;
  728. }
  729.  
  730. preview() {
  731. const section = document.getElementById('previewSection');
  732. const content = document.getElementById('previewContent');
  733.  
  734. content.textContent = this.addedBlocks || 'No processed G-code available. Please process the file first.';
  735. section.classList.remove('hidden');
  736. section.scrollIntoView({ behavior: 'smooth' });
  737. }
  738. }
  739.  
  740. document.addEventListener('DOMContentLoaded', () => new GCodeFarmModifier());
  741. </script>
  742. </body>
  743. </html>
Advertisement
Add Comment
Please, Sign In to add comment