Skip to content

Commit 4dae155

Browse files
committed
Bring back the tolerance logic
1 parent cdaebe1 commit 4dae155

File tree

3 files changed

+76
-17
lines changed

3 files changed

+76
-17
lines changed

src/__tests__/job.ts

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { test, expect, describe } from 'vitest';
2-
import { Job, State } from '../job';
2+
import { Job, State, LayersIndexer } from '../job';
33
import { PathType, Path } from '../path';
44

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

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

102102
append_path(job, PathType.Extrusion, [
@@ -105,7 +105,7 @@ describe('.layers', () => {
105105
]);
106106
append_path(job, PathType.Travel, [
107107
[5, 6, 0],
108-
[5, 6, 1]
108+
[5, 6, LayersIndexer.DEFAULT_TOLERANCE + 0.01]
109109
]);
110110

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

120+
test('travel paths moving z under the default tolerance are on the same layer', () => {
121+
const job = new Job();
122+
123+
append_path(job, PathType.Extrusion, [
124+
[0, 0, 0],
125+
[1, 2, 0]
126+
]);
127+
append_path(job, PathType.Travel, [
128+
[5, 6, 0],
129+
[5, 6, LayersIndexer.DEFAULT_TOLERANCE - 0.01]
130+
]);
131+
132+
const layers = job.layers;
133+
134+
expect(layers).not.toBeNull();
135+
expect(layers).toBeInstanceOf(Array);
136+
expect(layers?.length).toEqual(1);
137+
expect(layers?.[0].length).toEqual(2);
138+
});
139+
140+
test('Tolerance can be set', () => {
141+
const job = new Job({ minLayerThreshold: 0.1 });
142+
143+
append_path(job, PathType.Extrusion, [
144+
[0, 0, 0],
145+
[1, 2, 0]
146+
]);
147+
append_path(job, PathType.Travel, [
148+
[5, 6, 0],
149+
[5, 6, 0.09]
150+
]);
151+
152+
const layers = job.layers;
153+
154+
expect(layers).not.toBeNull();
155+
expect(layers).toBeInstanceOf(Array);
156+
expect(layers?.length).toEqual(1);
157+
expect(layers?.[0].length).toEqual(2);
158+
});
159+
120160
test('multiple travels in a row are on the same layer', () => {
121161
const job = new Job();
122162

src/job.ts

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ export class State {
1515
}
1616
}
1717

18+
type JobOptions = {
19+
state?: State;
20+
minLayerThreshold?: number;
21+
};
22+
1823
export class Job {
1924
paths: Path[];
2025
state: State;
@@ -24,13 +29,13 @@ export class Job {
2429
private indexers: Indexer[];
2530
inprogressPath: Path | undefined;
2631

27-
constructor(state?: State) {
32+
constructor(opts: JobOptions = {}) {
2833
this.paths = [];
29-
this.state = state || State.initial;
34+
this.state = opts.state || State.initial;
3035
this.layersPaths = [[]];
3136
this.indexers = [
3237
new TravelTypeIndexer({ travel: this.travelPaths, extrusion: this.extrusionPaths }),
33-
new LayersIndexer(this.layersPaths)
38+
new LayersIndexer(this.layersPaths, opts.minLayerThreshold)
3439
];
3540
}
3641

@@ -61,7 +66,7 @@ export class Job {
6166
}
6267

6368
isPlanar(): boolean {
64-
return this.paths.find((path) => path.travelType === PathType.Extrusion && path.hasVerticalMoves()) === undefined;
69+
return this.layersPaths !== null;
6570
}
6671

6772
private indexPath(path: Path): void {
@@ -84,7 +89,7 @@ export class Job {
8489
}
8590

8691
class NonApplicableIndexer extends Error {}
87-
class Indexer {
92+
export class Indexer {
8893
protected indexes: Record<string, Path[]> | Path[][];
8994
constructor(indexes: Record<string, Path[]> | Path[][]) {
9095
this.indexes = indexes;
@@ -95,7 +100,7 @@ class Indexer {
95100
}
96101
}
97102

98-
class TravelTypeIndexer extends Indexer {
103+
export class TravelTypeIndexer extends Indexer {
99104
protected declare indexes: Record<string, Path[]>;
100105
constructor(indexes: Record<string, Path[]>) {
101106
super(indexes);
@@ -115,10 +120,13 @@ class NonPlanarPathError extends NonApplicableIndexer {
115120
super("Non-planar paths can't be indexed by layer");
116121
}
117122
}
118-
class LayersIndexer extends Indexer {
123+
export class LayersIndexer extends Indexer {
124+
static readonly DEFAULT_TOLERANCE = 0.05;
119125
protected declare indexes: Path[][];
120-
constructor(indexes: Path[][]) {
126+
private tolerance: number;
127+
constructor(indexes: Path[][], tolerance: number = LayersIndexer.DEFAULT_TOLERANCE) {
121128
super(indexes);
129+
this.tolerance = tolerance;
122130
}
123131

124132
sortIn(path: Path): void {
@@ -129,10 +137,17 @@ class LayersIndexer extends Indexer {
129137
if (path.travelType === PathType.Extrusion) {
130138
this.lastLayer().push(path);
131139
} else {
132-
if (
133-
path.vertices.some((_, i, arr) => i % 3 === 2 && arr[i] !== arr[2]) &&
134-
this.lastLayer().find((p) => p.travelType === PathType.Extrusion)
135-
) {
140+
const verticalTravels = path.vertices
141+
.map((_, i, arr) => {
142+
if (i % 3 === 2 && arr[i] - arr[2] > this.tolerance) {
143+
return arr[i] - arr[2];
144+
}
145+
})
146+
.filter((z) => z !== undefined);
147+
const hasVerticalTravel = verticalTravels.length > 0;
148+
const hasExtrusions = this.lastLayer().find((p) => p.travelType === PathType.Extrusion);
149+
150+
if (hasVerticalTravel && hasExtrusions) {
136151
this.createLayer();
137152
}
138153
this.lastLayer().push(path);

src/webgl-preview.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export type GCodePreviewOptions = {
3939
lineWidth?: number;
4040
lineHeight?: number;
4141
nonTravelMoves?: string[];
42+
minLayerThreshold?: number;
4243
renderExtrusion?: boolean;
4344
renderTravel?: boolean;
4445
startLayer?: number;
@@ -57,6 +58,7 @@ export type GCodePreviewOptions = {
5758
};
5859

5960
export class WebGLPreview {
61+
minLayerThreshold: number;
6062
scene: Scene;
6163
camera: PerspectiveCamera;
6264
renderer: WebGLRenderer;
@@ -81,8 +83,8 @@ export class WebGLPreview {
8183
nonTravelmoves: string[] = [];
8284
disableGradient = false;
8385

86+
job: Job;
8487
interpreter = new Interpreter();
85-
job = new Job();
8688
parser = new Parser();
8789

8890
// rendering
@@ -110,6 +112,8 @@ export class WebGLPreview {
110112
private devGui?: DevGUI;
111113

112114
constructor(opts: GCodePreviewOptions) {
115+
this.minLayerThreshold = opts.minLayerThreshold ?? this.minLayerThreshold;
116+
this.job = new Job({ minLayerThreshold: this.minLayerThreshold });
113117
this.scene = new Scene();
114118
this.scene.background = this._backgroundColor;
115119
if (opts.backgroundColor !== undefined) {
@@ -337,7 +341,7 @@ export class WebGLPreview {
337341
clear(): void {
338342
this.resetState();
339343
this.parser = new Parser();
340-
this.job = new Job();
344+
this.job = new Job({ minLayerThreshold: this.minLayerThreshold });
341345
}
342346

343347
// reset processing state

0 commit comments

Comments
 (0)