Skip to content
Merged
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
46 changes: 43 additions & 3 deletions src/__tests__/job.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { test, expect, describe } from 'vitest';
import { Job, State } from '../job';
import { Job, State, LayersIndexer } from '../job';
import { PathType, Path } from '../path';

test('it has an initial state', () => {
Expand Down Expand Up @@ -96,7 +96,7 @@ describe('.layers', () => {
expect(layers?.[0].length).toEqual(2);
});

test('travel paths moving z create a new layer', () => {
test('travel paths moving z above the default tolerance create a new layer', () => {
const job = new Job();

append_path(job, PathType.Extrusion, [
Expand All @@ -105,7 +105,7 @@ describe('.layers', () => {
]);
append_path(job, PathType.Travel, [
[5, 6, 0],
[5, 6, 1]
[5, 6, LayersIndexer.DEFAULT_TOLERANCE + 0.01]
]);

const layers = job.layers;
Expand All @@ -117,6 +117,46 @@ describe('.layers', () => {
expect(layers?.[1].length).toEqual(1);
});

test('travel paths moving z under the default tolerance are on the same layer', () => {
const job = new Job();

append_path(job, PathType.Extrusion, [
[0, 0, 0],
[1, 2, 0]
]);
append_path(job, PathType.Travel, [
[5, 6, 0],
[5, 6, LayersIndexer.DEFAULT_TOLERANCE - 0.01]
]);

const layers = job.layers;

expect(layers).not.toBeNull();
expect(layers).toBeInstanceOf(Array);
expect(layers?.length).toEqual(1);
expect(layers?.[0].length).toEqual(2);
});

test('Tolerance can be set', () => {
const job = new Job({ minLayerThreshold: 0.1 });

append_path(job, PathType.Extrusion, [
[0, 0, 0],
[1, 2, 0]
]);
append_path(job, PathType.Travel, [
[5, 6, 0],
[5, 6, 0.09]
]);

const layers = job.layers;

expect(layers).not.toBeNull();
expect(layers).toBeInstanceOf(Array);
expect(layers?.length).toEqual(1);
expect(layers?.[0].length).toEqual(2);
});

test('multiple travels in a row are on the same layer', () => {
const job = new Job();

Expand Down
34 changes: 22 additions & 12 deletions src/job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ export class Job {
private indexers: Indexer[];
inprogressPath: Path | undefined;

constructor(state?: State) {
constructor(opts: { state?: State; minLayerThreshold?: number } = {}) {
this.paths = [];
this.state = state || State.initial;
this.state = opts.state || State.initial;
this.layersPaths = [[]];
this.indexers = [
new TravelTypeIndexer({ travel: this.travelPaths, extrusion: this.extrusionPaths }),
new LayersIndexer(this.layersPaths)
new LayersIndexer(this.layersPaths, opts.minLayerThreshold)
];
}

Expand Down Expand Up @@ -61,7 +61,7 @@ export class Job {
}

isPlanar(): boolean {
return this.paths.find((path) => path.travelType === PathType.Extrusion && path.hasVerticalMoves()) === undefined;
return this.layersPaths !== null;
}

private indexPath(path: Path): void {
Expand All @@ -84,7 +84,7 @@ export class Job {
}

class NonApplicableIndexer extends Error {}
class Indexer {
export class Indexer {
protected indexes: Record<string, Path[]> | Path[][];
constructor(indexes: Record<string, Path[]> | Path[][]) {
this.indexes = indexes;
Expand All @@ -95,7 +95,7 @@ class Indexer {
}
}

class TravelTypeIndexer extends Indexer {
export class TravelTypeIndexer extends Indexer {
protected declare indexes: Record<string, Path[]>;
constructor(indexes: Record<string, Path[]>) {
super(indexes);
Expand All @@ -115,10 +115,13 @@ class NonPlanarPathError extends NonApplicableIndexer {
super("Non-planar paths can't be indexed by layer");
}
}
class LayersIndexer extends Indexer {
export class LayersIndexer extends Indexer {
static readonly DEFAULT_TOLERANCE = 0.05;
protected declare indexes: Path[][];
constructor(indexes: Path[][]) {
private tolerance: number;
constructor(indexes: Path[][], tolerance: number = LayersIndexer.DEFAULT_TOLERANCE) {
super(indexes);
this.tolerance = tolerance;
}

sortIn(path: Path): void {
Expand All @@ -129,10 +132,17 @@ class LayersIndexer extends Indexer {
if (path.travelType === PathType.Extrusion) {
this.lastLayer().push(path);
} else {
if (
path.vertices.some((_, i, arr) => i % 3 === 2 && arr[i] !== arr[2]) &&
this.lastLayer().find((p) => p.travelType === PathType.Extrusion)
) {
const verticalTravels = path.vertices
.map((_, i, arr) => {
if (i % 3 === 2 && arr[i] - arr[2] > this.tolerance) {
return arr[i] - arr[2];
}
})
.filter((z) => z !== undefined);
const hasVerticalTravel = verticalTravels.length > 0;
const hasExtrusions = this.lastLayer().find((p) => p.travelType === PathType.Extrusion);

if (hasVerticalTravel && hasExtrusions) {
this.createLayer();
}
this.lastLayer().push(path);
Expand Down
8 changes: 6 additions & 2 deletions src/webgl-preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export type GCodePreviewOptions = {
lineWidth?: number;
lineHeight?: number;
nonTravelMoves?: string[];
minLayerThreshold?: number;
renderExtrusion?: boolean;
renderTravel?: boolean;
startLayer?: number;
Expand All @@ -61,6 +62,7 @@ export type GCodePreviewOptions = {
};

export class WebGLPreview {
minLayerThreshold: number;
/**
* @deprecated Please use the `canvas` param instead.
*/
Expand Down Expand Up @@ -89,8 +91,8 @@ export class WebGLPreview {
nonTravelmoves: string[] = [];
disableGradient = false;

job: Job;
interpreter = new Interpreter();
job = new Job();
parser = new Parser();

// rendering
Expand Down Expand Up @@ -118,6 +120,8 @@ export class WebGLPreview {
private devGui?: DevGUI;

constructor(opts: GCodePreviewOptions) {
this.minLayerThreshold = opts.minLayerThreshold ?? this.minLayerThreshold;
this.job = new Job({ minLayerThreshold: this.minLayerThreshold });
this.scene = new Scene();
this.scene.background = this._backgroundColor;
if (opts.backgroundColor !== undefined) {
Expand Down Expand Up @@ -366,7 +370,7 @@ export class WebGLPreview {
clear(): void {
this.resetState();
this.parser = new Parser();
this.job = new Job();
this.job = new Job({ minLayerThreshold: this.minLayerThreshold });
}

// reset processing state
Expand Down