Skip to content

Commit 86d87cc

Browse files
Merge pull request #25 from little-did-I-know/improvements/optimization
Improvements/optimization
2 parents bc36d47 + 657c73b commit 86d87cc

12 files changed

Lines changed: 3054 additions & 493 deletions

gcode-modifier.html

Lines changed: 1476 additions & 331 deletions
Large diffs are not rendered by default.

src/analysis-manager.js

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ export class AnalysisManager {
55
this.engines = [];
66
this._overlayMap = new Map();
77
this._statsCache = {};
8+
this._analyzed = new Set();
9+
this._eagerEngines = new Set();
10+
this._storedArgs = null;
811
}
912

1013
register(engine) {
@@ -14,14 +17,114 @@ export class AnalysisManager {
1417
}
1518
}
1619

20+
markEager(engineName) {
21+
this._eagerEngines.add(engineName);
22+
}
23+
24+
analyzeEager(layerMoves, profile) {
25+
this._statsCache = {};
26+
this._storedArgs = { layerMoves, profile };
27+
for (const engine of this.engines) {
28+
if (this._eagerEngines.has(engine.name)) {
29+
engine.analyze(layerMoves, profile);
30+
this._analyzed.add(engine.name);
31+
}
32+
}
33+
}
34+
35+
ensureEngine(engineName) {
36+
if (this._analyzed.has(engineName)) return;
37+
if (!this._storedArgs) return;
38+
const engine = this.engines.find(e => e.name === engineName);
39+
if (!engine) return;
40+
const { layerMoves, profile } = this._storedArgs;
41+
engine.analyze(layerMoves, profile);
42+
this._analyzed.add(engineName);
43+
}
44+
45+
ensureAllAnalyzed() {
46+
if (!this._storedArgs) return;
47+
for (const engine of this.engines) {
48+
if (!this._analyzed.has(engine.name)) {
49+
const { layerMoves, profile } = this._storedArgs;
50+
engine.analyze(layerMoves, profile);
51+
this._analyzed.add(engine.name);
52+
}
53+
}
54+
}
55+
56+
isAnalyzed(engineName) {
57+
return this._analyzed.has(engineName);
58+
}
59+
60+
isAllAnalyzed() {
61+
return this.engines.every(e => this._analyzed.has(e.name));
62+
}
63+
64+
async ensureEngineAsync(engineName, onProgress) {
65+
if (this._analyzed.has(engineName)) return;
66+
if (!this._storedArgs) return;
67+
const engine = this.engines.find(e => e.name === engineName);
68+
if (!engine) return;
69+
const { layerMoves, profile } = this._storedArgs;
70+
if (typeof engine.analyzeAsync === 'function') {
71+
await engine.analyzeAsync(layerMoves, profile, onProgress);
72+
} else {
73+
engine.analyze(layerMoves, profile);
74+
}
75+
this._analyzed.add(engineName);
76+
}
77+
78+
async ensureAllAnalyzedAsync(onProgress) {
79+
if (!this._storedArgs) return;
80+
const toRun = this.engines.filter(e => !this._analyzed.has(e.name));
81+
if (toRun.length === 0) return;
82+
for (let i = 0; i < toRun.length; i++) {
83+
const engine = toRun[i];
84+
const { layerMoves, profile } = this._storedArgs;
85+
const engineProgress = (p) => {
86+
if (onProgress) onProgress({ engine: engine.name, engineProgress: p, overall: (i + p) / toRun.length });
87+
};
88+
if (typeof engine.analyzeAsync === 'function') {
89+
await engine.analyzeAsync(layerMoves, profile, engineProgress);
90+
} else {
91+
engine.analyze(layerMoves, profile);
92+
}
93+
this._analyzed.add(engine.name);
94+
if (onProgress) onProgress({ engine: engine.name, engineProgress: 1, overall: (i + 1) / toRun.length });
95+
}
96+
}
97+
1798
analyzeAll(layerMoves, profile) {
1899
this._statsCache = {};
100+
this._storedArgs = { layerMoves, profile };
19101
for (const engine of this.engines) {
20102
engine.analyze(layerMoves, profile);
103+
this._analyzed.add(engine.name);
104+
}
105+
}
106+
107+
async analyzeAllAsync(layerMoves, profile, onProgress) {
108+
this._statsCache = {};
109+
this._storedArgs = { layerMoves, profile };
110+
this._analyzed.clear();
111+
for (let i = 0; i < this.engines.length; i++) {
112+
const engine = this.engines[i];
113+
const engineProgress = (p) => {
114+
if (onProgress) onProgress({ engine: engine.name, engineProgress: p, overall: (i + p) / this.engines.length });
115+
};
116+
if (typeof engine.analyzeAsync === 'function') {
117+
await engine.analyzeAsync(layerMoves, profile, engineProgress);
118+
} else {
119+
engine.analyze(layerMoves, profile);
120+
}
121+
this._analyzed.add(engine.name);
122+
if (onProgress) onProgress({ engine: engine.name, engineProgress: 1, overall: (i + 1) / this.engines.length });
21123
}
22124
}
23125

24126
getAllFindings() {
127+
this.ensureAllAnalyzed();
25128
const all = [];
26129
for (const engine of this.engines) {
27130
all.push(...engine.getFindings());
@@ -31,6 +134,7 @@ export class AnalysisManager {
31134
}
32135

33136
getFindingsByEngine(engineName) {
137+
this.ensureEngine(engineName);
34138
const engine = this.engines.find(e => e.name === engineName);
35139
if (!engine) return [];
36140
const findings = engine.getFindings();
@@ -49,6 +153,7 @@ export class AnalysisManager {
49153
getOverlayValue(overlayId, layerNum, moveIndex) {
50154
const engine = this._overlayMap.get(overlayId);
51155
if (!engine) return 0;
156+
this.ensureEngine(engine.name);
52157
return engine.getOverlayData(overlayId, layerNum, moveIndex);
53158
}
54159

@@ -76,6 +181,8 @@ export class AnalysisManager {
76181

77182
clear() {
78183
this._statsCache = {};
184+
this._analyzed.clear();
185+
this._storedArgs = null;
79186
for (const engine of this.engines) {
80187
engine.clear();
81188
}

src/app.js

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ analysisManager.register(structuralAnalyzer);
3737
analysisManager.register(thermalAnalyzer);
3838
analysisManager.register(retractionAnalyzer);
3939
analysisManager.register(flowAnalyzer);
40+
analysisManager.markEager('motion');
41+
analysisManager.markEager('flow');
4042

4143
let analysisProfile = {
4244
printer: {},
@@ -203,8 +205,30 @@ function resetMotionTypeState() {
203205
if (currentView === 'visual') viewer.render(viewer.currentLayer);
204206
}
205207

206-
function setColorMode(mode) {
208+
async function setColorMode(mode) {
207209
colorMode = mode;
210+
// Ensure the engine for this overlay has been analyzed (async to avoid freeze)
211+
const engine = analysisManager._overlayMap.get(mode);
212+
if (engine && !analysisManager.isAnalyzed(engine.name)) {
213+
const ENGINE_LABELS = { structural: 'Structural', thermal: 'Thermal', retraction: 'Retraction', motion: 'Motion', flow: 'Flow' };
214+
const label = ENGINE_LABELS[engine.name] || engine.name;
215+
// Show loading overlay on the 3D canvas area (visible on Visual tab)
216+
const canvasArea = document.querySelector('.viewer-canvas-area');
217+
let overlay = null;
218+
if (canvasArea) {
219+
overlay = document.createElement('div');
220+
overlay.className = 'viewer-analysis-loading';
221+
overlay.innerHTML = '<div style="text-align:center"><div class="bar-label" id="viewerAnalysisLabel">Analyzing ' + label + '...</div><div class="bar-wrap"><div class="bar-fill" id="viewerAnalysisBar" style="width:0%"></div></div></div>';
222+
canvasArea.appendChild(overlay);
223+
}
224+
await analysisManager.ensureEngineAsync(engine.name, (p) => {
225+
const bar = document.getElementById('viewerAnalysisBar');
226+
const lbl = document.getElementById('viewerAnalysisLabel');
227+
if (bar) bar.style.width = (p * 100).toFixed(0) + '%';
228+
if (lbl) lbl.textContent = 'Analyzing ' + label + '... ' + (p * 100).toFixed(0) + '%';
229+
});
230+
if (overlay) overlay.remove();
231+
}
208232
heatmapLayerStats = {};
209233
resetSimulation();
210234
viewer.clearBuffers();
@@ -261,14 +285,37 @@ function getHeatmapLayerStats(layerNum) {
261285
return stats;
262286
}
263287

264-
function runAnalysis() {
288+
function runEagerAnalysis() {
289+
const inferredMaterial = inferMaterial(parser.lines);
290+
if (!analysisProfile._manualMaterial) {
291+
analysisProfile.material = getMaterialProfile(inferredMaterial);
292+
}
293+
analysisProfile.printer = { ...motionAnalyzer.profile };
294+
analysisProfile._parsedLines = parser.lines;
295+
analysisManager.analyzeEager(parser.layerMoves, analysisProfile);
296+
heatmapLayerStats = {};
297+
}
298+
299+
async function runAnalysis() {
265300
const inferredMaterial = inferMaterial(parser.lines);
266301
if (!analysisProfile._manualMaterial) {
267302
analysisProfile.material = getMaterialProfile(inferredMaterial);
268303
}
269304
analysisProfile.printer = { ...motionAnalyzer.profile };
270305
analysisProfile._parsedLines = parser.lines;
271-
analysisManager.analyzeAll(parser.layerMoves, analysisProfile);
306+
307+
const container = document.getElementById('analysisResults');
308+
if (container) {
309+
container.innerHTML = '<div class="analysis-progress"><div class="bar-label" id="analysisLabel">Analyzing...</div><div class="bar-wrap"><div class="bar-fill" id="analysisBar" style="width:0%"></div></div></div>';
310+
}
311+
const ENGINE_LABELS = { structural: 'Structural', thermal: 'Thermal', retraction: 'Retraction', motion: 'Motion', flow: 'Flow' };
312+
await analysisManager.analyzeAllAsync(parser.layerMoves, analysisProfile, (info) => {
313+
const label = document.getElementById('analysisLabel');
314+
const bar = document.getElementById('analysisBar');
315+
if (label) label.textContent = 'Analyzing ' + (ENGINE_LABELS[info.engine] || info.engine) + '... ' + (info.overall * 100).toFixed(0) + '%';
316+
if (bar) bar.style.width = (info.overall * 100).toFixed(0) + '%';
317+
});
318+
272319
heatmapLayerStats = {};
273320
if (typeof renderAnalysisPanel === 'function') renderAnalysisPanel();
274321
if (currentView === 'visual') {

src/index.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,14 @@
291291
.parse-progress .bar-wrap { width: 300px; height: 6px; background: var(--surface2); border-radius: 3px; overflow: hidden; margin-top: 8px; }
292292
.parse-progress .bar-fill { height: 100%; background: var(--accent); border-radius: 3px; transition: width 0.1s; }
293293
.parse-progress .bar-label { font-size: 12px; color: var(--text-dim); }
294+
.analysis-progress { padding: 24px 16px; text-align: center; }
295+
.analysis-progress .bar-wrap { width: 100%; max-width: 300px; height: 6px; background: var(--surface2); border-radius: 3px; overflow: hidden; margin: 8px auto 0; }
296+
.analysis-progress .bar-fill { height: 100%; background: var(--accent); border-radius: 3px; transition: width 0.1s; }
297+
.analysis-progress .bar-label { font-size: 12px; color: var(--text-dim); }
298+
.viewer-analysis-loading { position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,.45); z-index: 20; pointer-events: none; }
299+
.viewer-analysis-loading .bar-wrap { width: 220px; height: 6px; background: rgba(255,255,255,.15); border-radius: 3px; overflow: hidden; margin: 6px auto 0; }
300+
.viewer-analysis-loading .bar-fill { height: 100%; background: var(--accent); border-radius: 3px; transition: width 0.1s; }
301+
.viewer-analysis-loading .bar-label { font-size: 13px; color: #fff; text-shadow: 0 1px 4px rgba(0,0,0,.6); }
294302

295303
/* RIGHT PANEL — TOOLS */
296304
.panel-right { width: 400px; min-width: 280px; max-width: 60vw; display: flex; flex-direction: column; background: var(--surface); border-left: 1px solid var(--border); position: relative; }

src/material-profiles.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const MATERIAL_PROFILES = {
1717
maxPrintSpeed: 300, // mm/s
1818
maxVolumetricFlow: 15, // mm³/s
1919
minLayerTime: 8,
20+
optimalBridgeSpeed: 25, // mm/s — ideal bridge print speed
2021
waterAbsorption: 0.43, // % saturated
2122
needsDrying: false,
2223
needsEnclosure: false,
@@ -40,6 +41,7 @@ export const MATERIAL_PROFILES = {
4041
maxPrintSpeed: 300,
4142
maxVolumetricFlow: 12, // mm³/s
4243
minLayerTime: 10,
44+
optimalBridgeSpeed: 20, // mm/s — ideal bridge print speed
4345
waterAbsorption: 0.40,
4446
needsDrying: true,
4547
needsEnclosure: false,
@@ -63,6 +65,7 @@ export const MATERIAL_PROFILES = {
6365
maxPrintSpeed: 300,
6466
maxVolumetricFlow: 11, // mm³/s
6567
minLayerTime: 12,
68+
optimalBridgeSpeed: 15, // mm/s — ideal bridge print speed
6669
waterAbsorption: 0.65,
6770
needsDrying: false,
6871
needsEnclosure: true,
@@ -86,6 +89,7 @@ export const MATERIAL_PROFILES = {
8689
maxPrintSpeed: 300,
8790
maxVolumetricFlow: 11, // mm³/s
8891
minLayerTime: 12,
92+
optimalBridgeSpeed: 15, // mm/s — ideal bridge print speed
8993
waterAbsorption: 0.45,
9094
needsDrying: false,
9195
needsEnclosure: true,
@@ -109,6 +113,7 @@ export const MATERIAL_PROFILES = {
109113
maxPrintSpeed: 200,
110114
maxVolumetricFlow: 5, // mm³/s
111115
minLayerTime: 15,
116+
optimalBridgeSpeed: 15, // mm/s — ideal bridge print speed
112117
waterAbsorption: 1.08,
113118
needsDrying: true,
114119
needsEnclosure: false,
@@ -132,6 +137,7 @@ export const MATERIAL_PROFILES = {
132137
maxPrintSpeed: 300,
133138
maxVolumetricFlow: 10, // mm³/s
134139
minLayerTime: 15,
140+
optimalBridgeSpeed: 15, // mm/s — ideal bridge print speed
135141
waterAbsorption: 0.25,
136142
needsDrying: true,
137143
needsEnclosure: true,
@@ -157,6 +163,7 @@ export const MATERIAL_PROFILES = {
157163
maxPrintSpeed: 250,
158164
maxVolumetricFlow: 10, // mm³/s
159165
minLayerTime: 8,
166+
optimalBridgeSpeed: 25, // mm/s — ideal bridge print speed
160167
waterAbsorption: 0.42,
161168
needsDrying: false,
162169
needsEnclosure: false,
@@ -181,6 +188,7 @@ export const MATERIAL_PROFILES = {
181188
maxPrintSpeed: 200,
182189
maxVolumetricFlow: 10, // mm³/s
183190
minLayerTime: 10,
191+
optimalBridgeSpeed: 20, // mm/s — ideal bridge print speed
184192
waterAbsorption: 0.30,
185193
needsDrying: false,
186194
needsEnclosure: false,
@@ -205,6 +213,7 @@ export const MATERIAL_PROFILES = {
205213
maxPrintSpeed: 180,
206214
maxVolumetricFlow: 9, // mm³/s
207215
minLayerTime: 12,
216+
optimalBridgeSpeed: 20, // mm/s — ideal bridge print speed
208217
waterAbsorption: 0.53,
209218
needsDrying: false,
210219
needsEnclosure: true,
@@ -229,6 +238,7 @@ export const MATERIAL_PROFILES = {
229238
maxPrintSpeed: 250,
230239
maxVolumetricFlow: 9, // mm³/s
231240
minLayerTime: 12,
241+
optimalBridgeSpeed: 20, // mm/s — ideal bridge print speed
232242
waterAbsorption: 0.33,
233243
needsDrying: false,
234244
needsEnclosure: true,
@@ -253,6 +263,7 @@ export const MATERIAL_PROFILES = {
253263
maxPrintSpeed: 300,
254264
maxVolumetricFlow: 10, // mm³/s
255265
minLayerTime: 15,
266+
optimalBridgeSpeed: 20, // mm/s — ideal bridge print speed
256267
waterAbsorption: 0.12,
257268
needsDrying: true,
258269
needsEnclosure: true,
@@ -278,6 +289,7 @@ export const MATERIAL_PROFILES = {
278289
maxPrintSpeed: 100,
279290
maxVolumetricFlow: 8, // mm³/s
280291
minLayerTime: 15,
292+
optimalBridgeSpeed: 20, // mm/s — ideal bridge print speed
281293
waterAbsorption: 0.37,
282294
needsDrying: true,
283295
needsEnclosure: false,
@@ -302,6 +314,7 @@ export const MATERIAL_PROFILES = {
302314
maxPrintSpeed: 100,
303315
maxVolumetricFlow: 8, // mm³/s
304316
minLayerTime: 15,
317+
optimalBridgeSpeed: 20, // mm/s — ideal bridge print speed
305318
waterAbsorption: 0.88,
306319
needsDrying: true,
307320
needsEnclosure: true,
@@ -326,6 +339,7 @@ export const MATERIAL_PROFILES = {
326339
maxPrintSpeed: 100,
327340
maxVolumetricFlow: 8, // mm³/s
328341
minLayerTime: 15,
342+
optimalBridgeSpeed: 20, // mm/s — ideal bridge print speed
329343
waterAbsorption: 2.35,
330344
needsDrying: true,
331345
needsEnclosure: true,
@@ -350,6 +364,7 @@ export const MATERIAL_PROFILES = {
350364
maxPrintSpeed: 130,
351365
maxVolumetricFlow: 9, // mm³/s
352366
minLayerTime: 15,
367+
optimalBridgeSpeed: 20, // mm/s — ideal bridge print speed
353368
waterAbsorption: 2.56,
354369
needsDrying: true,
355370
needsEnclosure: true,
@@ -374,6 +389,7 @@ export const MATERIAL_PROFILES = {
374389
maxPrintSpeed: 100,
375390
maxVolumetricFlow: 8, // mm³/s
376391
minLayerTime: 15,
392+
optimalBridgeSpeed: 20, // mm/s — ideal bridge print speed
377393
waterAbsorption: 1.30,
378394
needsDrying: true,
379395
needsEnclosure: true,
@@ -398,6 +414,7 @@ export const MATERIAL_PROFILES = {
398414
maxPrintSpeed: 100,
399415
maxVolumetricFlow: 8, // mm³/s
400416
minLayerTime: 15,
417+
optimalBridgeSpeed: 15, // mm/s — ideal bridge print speed
401418
waterAbsorption: 0.05,
402419
needsDrying: true,
403420
needsEnclosure: true,
@@ -424,6 +441,7 @@ export const MATERIAL_PROFILES = {
424441
maxPrintSpeed: 100,
425442
maxVolumetricFlow: 10, // mm³/s
426443
minLayerTime: 15,
444+
optimalBridgeSpeed: 20, // mm/s — ideal bridge print speed
427445
waterAbsorption: 2.35,
428446
needsDrying: true,
429447
needsEnclosure: true,

0 commit comments

Comments
 (0)