Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e411292
Interpreter prototype
sophiedeziel Aug 15, 2024
2a099fe
arc support
sophiedeziel Aug 19, 2024
1c96b0d
units
sophiedeziel Aug 19, 2024
c4f1779
Simplify toolchange
sophiedeziel Aug 19, 2024
277866d
travelType
sophiedeziel Aug 19, 2024
a60654e
remove setInches
sophiedeziel Aug 19, 2024
58bf83b
remove targetId
sophiedeziel Aug 19, 2024
8f21b15
layers
sophiedeziel Aug 19, 2024
382a35a
render lines
sophiedeziel Aug 19, 2024
92ea42e
extract machine
sophiedeziel Aug 19, 2024
35ae334
wip
sophiedeziel Oct 8, 2024
79751db
Make it work with rerenders
sophiedeziel Oct 9, 2024
968926f
Remove some layer references
sophiedeziel Oct 10, 2024
2063445
Rename Machine to Job
sophiedeziel Oct 10, 2024
a5069bf
Simplify parsing attributes
sophiedeziel Oct 10, 2024
0ab74da
Am I going too far?
sophiedeziel Oct 10, 2024
2416e7b
get rid of geometries once used
sophiedeziel Oct 10, 2024
0b3fa37
the geometries disposition is handled by the batchMesh
sophiedeziel Oct 10, 2024
f15421b
Fix tests
sophiedeziel Oct 10, 2024
7f11d9d
Bring back some code
sophiedeziel Oct 10, 2024
b313c4a
Bring back progressive rendering
sophiedeziel Oct 11, 2024
a05869b
update dev-gui with job
sophiedeziel Oct 11, 2024
605f133
Test Path
sophiedeziel Oct 11, 2024
1ff8be8
First interpreter tests
sophiedeziel Oct 11, 2024
6d5f958
Test G0 and G1
sophiedeziel Oct 11, 2024
4f0be28
Test everything by G2
sophiedeziel Oct 11, 2024
6d1f18d
Adding missing codes
sophiedeziel Oct 11, 2024
dec8c3e
Minimize diff on app.js
sophiedeziel Oct 11, 2024
466abb4
Leave G21 for a future PR
sophiedeziel Oct 11, 2024
ac93091
Job tests (and fixes!)
sophiedeziel Oct 12, 2024
bcf7682
Keep G28 for a separate PR
sophiedeziel Oct 12, 2024
ff2911f
Improve tests
sophiedeziel Oct 13, 2024
4fc21bf
Code improvements
sophiedeziel Oct 13, 2024
14b54ca
Retractions are travel
sophiedeziel Oct 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions demo/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export const app = (window.app = createApp({
const updateUI = async () => {
const {
parser,
layers,
extrusionColor,
topLayerColor,
lastSegmentColor,
Expand All @@ -66,16 +65,17 @@ export const app = (window.app = createApp({
renderExtrusion,
lineWidth,
renderTubes,
extrusionWidth
extrusionWidth,
job
} = preview;
const { thumbnails } = parser.metadata;

thumbnail.value = thumbnails['220x124']?.src;
layerCount.value = layers.length;
layerCount.value = job.layers()?.length;
const colors = extrusionColor instanceof Array ? extrusionColor : [extrusionColor];
const currentSettings = {
maxLayer: layers.length,
endLayer: layers.length,
maxLayer: job.layers()?.length,
endLayer: job.layers()?.length,
singleLayerMode,
renderTravel,
travelColor: '#' + travelColor.getHexString(),
Expand All @@ -94,7 +94,7 @@ export const app = (window.app = createApp({
};

Object.assign(settings.value, currentSettings);
preview.endLayer = layers.length;
preview.endLayer = job.layers()?.length;
};

const loadGCodeFromServer = async (filename) => {
Expand All @@ -113,9 +113,16 @@ export const app = (window.app = createApp({
const prevDevMode = preview.devMode;
preview.clear();
preview.devMode = prevDevMode;

if (loadProgressive) {
preview.parser.parseGCode(gcode);
// await preview.renderAnimated(Math.ceil(preview.layers.length / 60));
const { commands } = preview.parser.parseGCode(gcode);
preview.interpreter.execute(commands, preview.job);
if (preview.job.layers() === null) {
console.warn('Job is not planar');
preview.render();
return;
}
await preview.renderAnimated(Math.ceil(preview.job.layers().length / 60));
} else {
preview.processGCode(gcode);
}
Expand Down Expand Up @@ -205,7 +212,7 @@ export const app = (window.app = createApp({
preview.lastSegmentColor = settings.value.highlightLastSegment ? settings.value.lastSegmentColor : undefined;

debounce(() => {
preview.renderAnimated(Math.ceil(preview.layers.length / 60));
preview.renderAnimated(Math.ceil(preview.job.layers().length / 60));
});
});
});
Expand Down
217 changes: 50 additions & 167 deletions src/__tests__/gcode-parser.ts
Original file line number Diff line number Diff line change
@@ -1,243 +1,126 @@
import { test, expect } from 'vitest';
import { GCodeCommand, MoveCommand, Parser, SelectToolCommand } from '../gcode-parser';
import { GCodeCommand, Parser } from '../gcode-parser';

test('a single extrusion cmd should result in 1 layer with 1 command', () => {
const parser = new Parser(0);
test('a single extrusion cmd should result in 1 command', () => {
const parser = new Parser();
const gcode = `G1 X0 Y0 Z1 E1`;
const parsed = parser.parseGCode(gcode);
expect(parsed).not.toBeNull();
expect(parsed.layers).not.toBeNull();
expect(parsed.layers.length).toEqual(1);
expect(parsed.layers[0].commands).not.toBeNull();
expect(parsed.layers[0].commands.length).toEqual(1);
expect(parsed.commands).not.toBeNull();
expect(parsed.commands.length).toEqual(1);
});

test('a gcode cmd w/o extrusion should not result in a layer', () => {
const parser = new Parser(0);
const gcode = `G1 X0 Y0 Z1`;
test('a single extrusion cmd should parse attributes', () => {
const parser = new Parser();
const gcode = `G1 X5 Y6 Z3 E1.9`;
const parsed = parser.parseGCode(gcode);
expect(parsed).not.toBeNull();
expect(parsed.layers).not.toBeNull();
expect(parsed.layers.length).toEqual(0);
});

test('a gcode cmd with 0 extrusion should not result in a layer', () => {
const parser = new Parser(0);
const gcode = `G1 X0 Y0 Z1 E0`;
const parsed = parser.parseGCode(gcode);
expect(parsed).not.toBeNull();
expect(parsed.layers).not.toBeNull();
expect(parsed.layers.length).toEqual(0);
const cmd = parsed.commands[0];
expect(cmd.params.x).toEqual(5);
expect(cmd.params.y).toEqual(6);
expect(cmd.params.z).toEqual(3);
expect(cmd.params.e).toEqual(1.9);
});

test('2 horizontal extrusion moves should result in 1 layer with 2 commands', () => {
const parser = new Parser(0);
const gcode = `G1 X0 Y0 Z1 E1
G1 X10 Y10 Z1 E2`;
test('multiple cmd results in an array of commands', () => {
const parser = new Parser();
const gcode = `G1 X5 Y6 Z3 E1.9
G1 X6 Y6 E1.9
G1 X5 Y7 E1.9`;
const parsed = parser.parseGCode(gcode);
expect(parsed).not.toBeNull();
expect(parsed.layers).not.toBeNull();
expect(parsed.layers.length).toEqual(1);
expect(parsed.layers[0].commands).not.toBeNull();
expect(parsed.layers[0].commands.length).toEqual(2);
});

test('2 vertical extrusion moves should result in 2 layers with 1 command', () => {
const parser = new Parser(0);
const gcode = `G1 X0 Y0 Z1 E1
G1 X0 Y0 Z2 E2`;
const parsed = parser.parseGCode(gcode);
expect(parsed).not.toBeNull();
expect(parsed.layers).not.toBeNull();
expect(parsed.layers.length).toEqual(2);
expect(parsed.layers[0].commands).not.toBeNull();
expect(parsed.layers[0].commands.length).toEqual(1);
});

test('2 vertical extrusion moves in consecutive gcode chunks should result in 2 layers with 1 command', () => {
const parser = new Parser(0);
const gcode1 = 'G1 X0 Y0 Z1 E1';
const gcode2 = 'G1 X0 Y0 Z2 E2';
const parsed = parser.parseGCode(gcode1);
parser.parseGCode(gcode2);
expect(parsed).not.toBeNull();
expect(parsed.layers).not.toBeNull();
expect(parsed.layers.length).toEqual(2);
expect(parsed.layers[0].commands).not.toBeNull();
expect(parsed.layers[0].commands.length).toEqual(1);
});

test('2 vertical extrusion moves in consecutive gcode chunks as string arrays should result in 2 layers with 1 command', () => {
const parser = new Parser(0);
const gcode1 = ['G1 X0 Y0 Z1 E1'];
const gcode2 = ['G1 X0 Y0 Z2 E2'];
const parsed = parser.parseGCode(gcode1);
parser.parseGCode(gcode2);
expect(parsed).not.toBeNull();
expect(parsed.layers).not.toBeNull();
expect(parsed.layers.length).toEqual(2);
expect(parsed.layers[0].commands).not.toBeNull();
expect(parsed.layers[0].commands.length).toEqual(1);
});

test('2 extrusion moves with a z difference below the threshold should result in only 1 layer', () => {
const threshold = 1;
const parser = new Parser(threshold);
const gcode = `G1 X0 Y0 Z1 E1
G1 X10 Y10 Z1.5 E2`;
const parsed = parser.parseGCode(gcode);
expect(parsed).not.toBeNull();
expect(parsed.layers).not.toBeNull();
expect(parsed.layers.length).toEqual(1);
expect(parsed.layers[0].commands).not.toBeNull();
expect(parsed.layers[0].commands.length).toEqual(2);
});

test('2 extrusion moves with a z difference above the threshold should result in 2 layers', () => {
const threshold = 1;
const parser = new Parser(threshold);
const gcode = `G1 X0 Y0 Z1 E1
G1 X10 Y10 Z3 E2`;
const parsed = parser.parseGCode(gcode);
expect(parsed).not.toBeNull();
expect(parsed.layers).not.toBeNull();
expect(parsed.layers.length).toEqual(2);
expect(parsed.layers[0].commands).not.toBeNull();
expect(parsed.layers[0].commands.length).toEqual(1);
expect(parsed.layers[0].commands).not.toBeNull();
expect(parsed.layers[0].commands.length).toEqual(1);
});

test('2 extrusion moves with a z diff exactly at the threshold should result in 1 layer', () => {
const threshold = 1;
const parser = new Parser(threshold);
const gcode = `G1 X0 Y0 Z1 E1
G1 X10 Y10 Z2 E2`;
const parsed = parser.parseGCode(gcode);
expect(parsed).not.toBeNull();
expect(parsed.layers).not.toBeNull();
expect(parsed.layers.length).toEqual(1);
expect(parsed.layers[0].commands).not.toBeNull();
expect(parsed.layers[0].commands.length).toEqual(2);
});

test('Layers should have calculated heights', () => {
const threshold = 0.05;
const parser = new Parser(threshold);
const gcode = `G0 X0 Y0 Z0.1 E1
G1 X10 Y10 Z0.2 E2
G1 X20 Y20 Z0.3 E3
G1 X30 Y30 Z0.5 E4
G1 X40 Y40 Z0.8 E5
`;
const parsed = parser.parseGCode(gcode);
expect(parsed).not.toBeNull();
expect(parsed.layers).not.toBeNull();
expect(parsed.layers.length).toEqual(5);
expect(parsed.layers[0].height).toEqual(expect.closeTo(0.1, 3));
expect(parsed.layers[1].height).toEqual(expect.closeTo(0.1, 3));
expect(parsed.layers[2].height).toEqual(expect.closeTo(0.1, 3));
expect(parsed.layers[3].height).toEqual(expect.closeTo(0.2, 3));
expect(parsed.commands).not.toBeNull();
expect(parsed.commands.length).toEqual(3);
});

test('T0 command should result in a tool change to tool with index 0', () => {
const parser = new Parser(0);
test('T0 command should result in a tool change', () => {
const parser = new Parser();
const gcode = `G1 X0 Y0 Z1 E1
T0`;
const parsed = parser.parseGCode(gcode);
expect(parsed).not.toBeNull();
expect(parsed.layers).not.toBeNull();
expect(parsed.layers.length).toEqual(1);
expect(parsed.layers[0].commands).not.toBeNull();
expect(parsed.commands).not.toBeNull();
expect(parsed.commands.length).toEqual(2);

const cmd = parsed.layers[0].commands[1] as SelectToolCommand;
const cmd = parsed.commands[1];
expect(cmd.gcode).toEqual('t0');
expect(cmd.toolIndex).toEqual(0);
});

test('T1 command should result in a tool change to tool with index 0', () => {
const parser = new Parser(0);
test('T1 command should result in a tool change', () => {
const parser = new Parser();
const gcode = `G1 X0 Y0 Z1 E1
T1`;
const parsed = parser.parseGCode(gcode);
const cmd = parsed.layers[0].commands[1] as SelectToolCommand;
const cmd = parsed.commands[1];
expect(cmd.gcode).toEqual('t1');
expect(cmd.toolIndex).toEqual(1);
});

test('T2 command should result in a tool change to tool with index 0', () => {
const parser = new Parser(0);
test('T2 command should result in a tool change', () => {
const parser = new Parser();
const gcode = `G1 X0 Y0 Z1 E1
T2`;
const parsed = parser.parseGCode(gcode);
const cmd = parsed.layers[0].commands[1] as SelectToolCommand;
const cmd = parsed.commands[1];
expect(cmd.gcode).toEqual('t2');
expect(cmd.toolIndex).toEqual(2);
});

test('T3 command should result in a tool change to tool with index 0', () => {
const parser = new Parser(0);
test('T3 command should result in a tool change', () => {
const parser = new Parser();
const gcode = `G1 X0 Y0 Z1 E1
T3`;
const parsed = parser.parseGCode(gcode);
const cmd = parsed.layers[0].commands[1] as SelectToolCommand;
const cmd = parsed.commands[1];
expect(cmd.gcode).toEqual('t3');
expect(cmd.toolIndex).toEqual(3);
});

// repeat fot T4 .. T7
test('T4 command should result in a tool change to tool with index 0', () => {
const parser = new Parser(0);
test('T4 command should result in a tool change', () => {
const parser = new Parser();
const gcode = `G1 X0 Y0 Z1 E1
T4`;
const parsed = parser.parseGCode(gcode);
const cmd = parsed.layers[0].commands[1] as SelectToolCommand;
const cmd = parsed.commands[1];
expect(cmd.gcode).toEqual('t4');
expect(cmd.toolIndex).toEqual(4);
});

test('T5 command should result in a tool change to tool with index 0', () => {
const parser = new Parser(0);
test('T5 command should result in a tool change', () => {
const parser = new Parser();
const gcode = `G1 X0 Y0 Z1 E1
T5`;
const parsed = parser.parseGCode(gcode);
const cmd = parsed.layers[0].commands[1] as SelectToolCommand;
const cmd = parsed.commands[1];
expect(cmd.gcode).toEqual('t5');
expect(cmd.toolIndex).toEqual(5);
});

test('T6 command should result in a tool change to tool with index 0', () => {
const parser = new Parser(0);
test('T6 command should result in a tool change', () => {
const parser = new Parser();
const gcode = `G1 X0 Y0 Z1 E1
T6`;
const parsed = parser.parseGCode(gcode);
const cmd = parsed.layers[0].commands[1] as SelectToolCommand;
const cmd = parsed.commands[1];
expect(cmd.gcode).toEqual('t6');
expect(cmd.toolIndex).toEqual(6);
});

test('T7 command should result in a tool change to tool with index 0', () => {
const parser = new Parser(0);
test('T7 command should result in a tool change', () => {
const parser = new Parser();
const gcode = `G1 X0 Y0 Z1 E1
T7`;
const parsed = parser.parseGCode(gcode);
const cmd = parsed.layers[0].commands[1] as SelectToolCommand;
const cmd = parsed.commands[1];
expect(cmd.gcode).toEqual('t7');
expect(cmd.toolIndex).toEqual(7);
});

test('gcode commands with spaces between letters and numbers should be parsed correctly', () => {
const parser = new Parser(0);
const parser = new Parser();
const gcode = `G 1 E 42 X 42`;
const parsed = parser.parseGCode(gcode);
const cmd = parsed.layers[0].commands[0] as MoveCommand;
const cmd = parsed.commands[0];
expect(cmd.gcode).toEqual('g1');
expect(cmd.params.x).toEqual(42);
expect(cmd.params.e).toEqual(42);
});

// test that a line withouth a gcode command results in a command with empty string gcode
test('gcode commands without gcode should result in a command with empty string gcode', () => {
const parser = new Parser(0);
const parser = new Parser();
const gcode = ` ; comment`;
const cmd = parser.parseCommand(gcode) as GCodeCommand;
expect(cmd.gcode).toEqual('');
Expand Down
Loading