Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interpreter improvements (wip) #269

Draft
wants to merge 9 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions src/__tests__/gcode-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ test('a single extrusion cmd should parse attributes', () => {
expect(cmd.params.e).toEqual(1.9);
});

// G1 X61.769 Y90.734 E-.27245
test("E value that doesn' have a leading 0 should be parsed as if there was a 0", () => {
const parser = new Parser();
const gcode = `G1 X61.769 Y90.734 E-.27245`;
const parsed = parser.parseGCode(gcode);
const cmd = parsed.commands[0];
expect(cmd.params.x).toEqual(61.769);
expect(cmd.params.y).toEqual(90.734);
expect(cmd.params.e).toEqual(-0.27245);
});

test('multiple cmd results in an array of commands', () => {
const parser = new Parser();
const gcode = `G1 X5 Y6 Z3 E1.9
Expand Down
16 changes: 12 additions & 4 deletions src/__tests__/path.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { test, expect, describe } from 'vitest';
import { Path, PathType } from '../path';
import { ExtrusionGeometry } from '../extrusion-geometry';
import { BufferGeometry } from 'three';

test('.addPoint adds a point to the vertices', () => {
const path = new Path(PathType.Travel, undefined, undefined, undefined);
Expand Down Expand Up @@ -123,13 +122,22 @@ describe('.geometry', () => {
expect(result.parameters.lineHeight).toEqual(7);
});

test('returns an empty BufferGeometry if there are less than 3 vertices', () => {
test('returns null if there are 0 vertices', () => {
const path = new Path(PathType.Travel, undefined, undefined, undefined);

const result = path.geometry();

expect(result).not.toBeNull();
expect(result).toBeInstanceOf(BufferGeometry);
expect(result).toBeNull();
});

test('returns null if there are less than 6 vertices', () => {
const path = new Path(PathType.Travel, undefined, undefined, undefined);

path.addPoint(0, 0, 0);

const result = path.geometry();

expect(result).toBeNull();
});
});

Expand Down
59 changes: 55 additions & 4 deletions src/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { Path, PathType } from './path';
import { GCodeCommand } from './gcode-parser';
import { Job } from './job';

// eslint-disable-next-line no-unused-vars
type Method = (...args: unknown[]) => unknown;
// type LookupTable = { [key: string]: Method | undefined };

/**
* Interprets and executes G-code commands, updating the job state accordingly
*
Expand All @@ -14,24 +18,51 @@ export class Interpreter {
// eslint-disable-next-line no-unused-vars
[key: string]: (...args: unknown[]) => unknown;

// TODO: maybe these props should move to the Job class
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
private retractions = 0;

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
private wipes = 0;

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
private feedrateChanges = 0;

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
private points = 0;

/**
* Executes an array of G-code commands, updating the provided job
* @param commands - Array of GCodeCommand objects to execute
* @param job - Job instance to update (default: new Job)
* @returns The updated job instance
*/
execute(commands: GCodeCommand[], job = new Job()): Job {
performance.mark('start execution');
job.resumeLastPath();
commands.forEach((command) => {
if (command.gcode !== undefined) {
if (this[command.gcode] === undefined) {
if (typeof this[command.gcode] !== 'function') {
return;
}
this[command.gcode](command, job);
const method = this[command.gcode] as Method;
method.bind(this)(command, job);
}
});
job.finishPath();

performance.mark('end execution');
const measure = performance.measure('execution', 'start execution', 'end execution');
console.debug('Done processing gcode', measure.duration.toFixed(0) + 'ms');
console.debug(this.retractions, 'retractions');
console.debug(this.wipes, 'wipes');
console.debug(this.feedrateChanges, 'feedrateChanges');
console.debug(this.points, 'points');

return job;
}

Expand All @@ -45,16 +76,36 @@ export class Interpreter {
* G0 is for rapid moves (non-extrusion), G1 is for linear moves (with optional extrusion).
*/
g0(command: GCodeCommand, job: Job): void {
const { x, y, z, e } = command.params;
const { state } = job;
const { x, y, z, e, f } = command.params;

// discard zero length moves
if (x === undefined && y === undefined && z === undefined) {
// console.warn('Discarding zero length move');
if (e > 0) {
this.retractions++;
} else if (e < 0) {
this.wipes++;
}

if (f !== undefined) {
this.feedrateChanges++;
}

return;
}

this.points++;

const { state } = job;
let currentPath = job.inprogressPath;
const pathType = e > 0 ? PathType.Extrusion : PathType.Travel;

if (currentPath === undefined || currentPath.travelType !== pathType) {
currentPath = this.breakPath(job, pathType);
}

// e is omitted bc currently we're assuming relative extrusion distances
// see also https://github.com/xyz-tools/gcode-preview/issues/179
state.x = x ?? state.x;
state.y = y ?? state.y;
state.z = z ?? state.z;
Expand Down
19 changes: 17 additions & 2 deletions src/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,23 @@ export class Path {
* @returns BufferGeometry representing the path
*/
geometry(opts: { extrusionWidthOverride?: number; lineHeightOverride?: number } = {}): BufferGeometry {
if (this._vertices.length < 3) {
return new BufferGeometry();
if (this._vertices.length < 6) {
// a path needs at least 2 points to be valid
console.warn('Path has less than 6 points, returning empty geometry');
return null;
}

// check for zero length paths
// do this check for each segment
for (let i = 0; i < this._vertices.length - 3; i += 3) {
const dx = this._vertices[i] - this._vertices[i + 3];
const dy = this._vertices[i + 1] - this._vertices[i + 4];
const dz = this._vertices[i + 2] - this._vertices[i + 5];
const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
if (distance < 0.0001) {
console.warn('Path has zero length, skipping');
return null;
}
}

return new ExtrusionGeometry(
Expand Down
3 changes: 3 additions & 0 deletions src/webgl-preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,9 @@ export class WebGLPreview {
extrusionWidthOverride: this.extrusionWidth,
lineHeightOverride: this.lineHeight
});

if (!geometry) return;

this.disposables.push(geometry);
geometries.push(geometry);
});
Expand Down
Loading