+
{
+ if (container) {
+ container.addEventListener('dragover', event => event.preventDefault());
+ container.addEventListener('drop', this.onDrop);
+ }
+ }}
+ >
renderer.render(scene, camera);
@@ -74,64 +65,111 @@ export function createScene(canvas, props, state) {
render();
};
- let editorControls;
- let renderer;
const updateCanvas = (canvas) => {
if (!renderer || renderer.domElement !== canvas) {
if (renderer) renderer.dispose();
- renderer = new WebGLRenderer({ canvas, alpha: true, antialias: true });
+ renderer = new THREE.WebGLRenderer({ canvas, alpha: true, antialias: true });
renderer.setClearColor(0xffffff, 0);
}
if (!editorControls || editorControls.domElement !== canvas) {
if (editorControls) editorControls.dispose();
editorControls = new THREE.EditorControls(camera, canvas);
- editorControls.focus(mesh);
editorControls.addEventListener('change', render);
}
render();
};
- updateCanvas(canvas);
const focus = () => editorControls.focus(mesh);
return { editorControls, scene, mesh, camera, renderer, render, box, setSize, updateCanvas, focus };
}
-export function fetchProgress(url, { method = 'get', headers = {}, body = {} } = {}, onProgress) {
+export function fetchProgress(url, data = {}, onProgress) {
return new Promise((resolve, reject) => {
+ const request = new Request(url, data);
const xhr = new XMLHttpRequest();
- xhr.open(method, url);
- if (headers) {
- for (const key in headers) {
- const header = headers[key];
- xhr.setRequestHeader(key, header);
- }
+
+ xhr.onload = () => {
+ resolve(new Response(xhr.response));
+ // const headers = new Headers(xhr.getAllResponseHeaders() || '');
+ // const { status, statusText, response, responseText, responseURL: url = headers.get('X-Request-URL') } = xhr;
+ // resolve(new Response(response || responseText, { headers, status, statusText, url }));
+ }
+ xhr.onerror = () => reject(new TypeError('Network request failed'));
+ xhr.ontimeout = () => reject(new TypeError('Network request failed'));
+
+ xhr.open(request.method, url, true);
+
+ if (request.credentials === 'include') {
+ xhr.withCredentials = true
+ } else if (request.credentials === 'omit') {
+ xhr.withCredentials = false
}
- xhr.onload = event => resolve(event.target.responseText);
- xhr.onerror = reject;
if (xhr.upload && onProgress) xhr.upload.onprogress = onProgress;
- xhr.send(body);
+ if (xhr.responseType) xhr.responseType = 'blob';
+
+ // request.headers.forEach((value, name) => xhr.setRequestHeader(name, value));
+
+ xhr.send(data.body);
});
}
+export function getMalyanStatus(ip) {
+ return fetch(`http://${ip}/inquiry`, { method: 'GET' })
+ .then(response => response.text())
+ .then(statusText => {
+ const [nozzleTemperature, nozzleTargetTemperature, bedTemperature, bedTargetTemperature, progress] = statusText.match(/\d+/g);
+ const status = { nozzleTemperature, nozzleTargetTemperature, bedTemperature, bedTargetTemperature, progress };
+
+ switch (statusText.charAt(statusText.length - 1)) {
+ case 'I':
+ status.state = 'idle';
+ break;
+ case 'P':
+ status.state = 'printing';
+ break;
+ default:
+ status.state = 'unknown';
+ break;
+ }
+ return status;
+ })
+}
+
+export function sleep(time) {
+ return new Promise(resolve => setTimeout(resolve, time));
+}
+
const GCODE_SERVER_URL = 'https://gcodeserver.doodle3d.com';
-const CONNECT_URL = 'http://connect.doodle3d.com/';
-
-export async function slice(target, name, mesh, settings, printers, quality, material, updateProgress) {
- if (!printers) throw new Error('Please select a printer');
+export async function slice(action, name, mesh, settings, updateProgress) {
let steps;
let currentStep = 0;
- switch (target) {
+ let wifiBox;
+ switch (action.target) {
case 'DOWNLOAD':
steps = 1;
break;
- case 'WIFI':
+ case 'WIFI_PRINT':
+ if (settings.printer === 'doodle3d_printer') {
+ const { state } = await getMalyanStatus(settings.ip);
+ if (state !== 'idle') throw { message: 'printer is busy', code: 0 };
+
+ } else {
+ wifiBox = new Doodle3DBox(settings.ip);
+ if (!await wifiBox.checkAlive()) throw { message: `can't connect to printer`, code: 4 }
+
+ const { state } = await wifiBox.info.status();
+ if (state !== 'idle') throw { message: 'printer is busy', code: 0 };
+ }
+ steps = 2;
+ break;
+ case 'CUSTOM_UPLOAD':
steps = 2;
break;
default:
- steps = 1;
+ throw { message: 'unknown target', code: 1 };
break;
}
@@ -139,64 +177,99 @@ export async function slice(target, name, mesh, settings, printers, quality, mat
const centerX = dimensions.x / 2;
const centerY = dimensions.y / 2;
- const matrix = new Matrix4().makeTranslation(centerY, 0, centerX).multiply(mesh.matrix);
- const { gcode } = await sliceGeometry(settings, mesh.geometry, mesh.material, matrix, false, false, ({ progress }) => {
+ const matrix = new THREE.Matrix4().makeTranslation(centerY, 0, centerX)
+ .multiply(new THREE.Matrix4().makeRotationY(-Math.PI / 2.0))
+ .multiply(mesh.matrix);
+
+ const { gcode } = await sliceGeometry({
+ ...settings,
+ name: `${name}.gcode`,
+ printer: { type: settings.printers, title: printerSettings[settings.printer].title },
+ material: { type: settings.material, title: materialSettings[settings.material].title },
+ quality: { type: settings.quality, title: qualitySettings[settings.quality].title }
+ }, mesh.geometry, mesh.material, matrix, false, false, ({ progress }) => {
updateProgress({
action: progress.action,
- percentage: currentStep / steps + progress.done / progress.total / steps
+ percentage: (currentStep + progress.done / progress.total) / steps
});
+ }).catch(error => {
+ throw { message: `error during slicing: ${error.message}`, code: 2 };
});
currentStep ++;
- switch (target) {
+ switch (action.target) {
case 'DOWNLOAD': {
- const blob = new File([gcode], `${name}.gcode`, { type: 'text/plain;charset=utf-8' });
- fileSaver.saveAs(blob);
+ fileSaver.saveAs(gcode, `${name}.gcode`);
break;
}
- case 'WIFI': {
- // upload G-code file to AWS S3
- const { data: { reservation, id } } = await fetch(`${GCODE_SERVER_URL}/upload`, { method: 'POST' })
- .then(response => response.json());
+ case 'WIFI_PRINT': {
+ if (settings.printer === 'doodle3d_printer') {
+ const body = new FormData();
+ body.append('file', gcode, 'doodle.gcode');
- const body = new FormData();
- const { fields } = reservation;
- for (const key in fields) {
- body.append(key, fields[key]);
- }
+ // because fetch has no way of retrieving progress we fake progress
+ let loaded = 0;
+ const interval = setInterval(() => {
+ loaded += 15 * 1024;
+ updateProgress({
+ action: 'Uploading to printer',
+ percentage: (currentStep + loaded / file.size) / steps
+ });
+ }, 1000);
- const file = ';' + JSON.stringify({
- name: `${name}.gcode`,
- ...settings,
- printer: {
- type: printers,
- title: printerSettings[printers].title
- },
- material: {
- type: material,
- title: materialSettings[material].title
- },
- quality: {
- type: quality,
- title: qualitySettings[quality].title
+ // await fetchProgress(`http://${settings.ip}/set?code=M563 S4`, { method: 'GET' });
+ await fetch(`http://${settings.ip}/upload`, { method: 'POST', body, mode: 'no-cors' }, (progress) => {
+ updateProgress({
+ action: 'Uploading to printer',
+ percentage: (currentStep + progress.loaded / progress.total) / steps
+ });
+ });
+ clearInterval(interval);
+ await fetch(`http://${settings.ip}/set?code=M566 ${name}.gcode`, { method: 'GET', mode: 'no-cors' });
+ await fetch(`http://${settings.ip}/set?code=M565`, { method: 'GET', mode: 'no-cors' });
+
+ currentStep ++;
+ } else {
+ // upload G-code file to AWS S3
+ const { data: { reservation: { fields, url }, id } } = await fetch(`${GCODE_SERVER_URL}/upload`, { method: 'POST' })
+ .then(response => response.json());
+
+ const body = new FormData();
+ for (const key in fields) {
+ body.append(key, fields[key]);
}
- }).trim() + '\n' + gcode;
- body.append('file', file);
- await fetchProgress(reservation.url, { method: 'POST', body }, (progess) => {
+ body.append('file', gcode, 'doodle.gcode');
+
+ await fetchProgress(url, { method: 'POST', body }, progress => {
+ updateProgress({
+ action: 'Uploading',
+ percentage: (currentStep + progress.loaded / progress.total) / steps
+ });
+ });
+ currentStep ++;
+
+ const result = await wifiBox.printer.fetch(id);
+ }
+ break;
+ }
+ case 'CUSTOM_UPLOAD': {
+ const body = new FormData();
+ body.append('file', gcode, 'doodle.gcode');
+
+ await fetchProgress(action.url, { method: 'POST', body }, progress => {
updateProgress({
action: 'Uploading',
- percentage: currentStep / steps + progess.loaded / progess.total / steps
+ percentage: (currentStep + progress.loaded / progress.total) / steps
});
});
currentStep ++;
-
- const popup = window.open(`${CONNECT_URL}?uuid=${id}`, '_blank');
- if (!popup) throw new Error('popup was blocked by browser');
+ break;
}
default:
+ throw { message: 'unknown target', code: 1 };
break;
}
}
diff --git a/src/settings/default.yml b/src/settings/default.yml
index 30f552d..8167357 100644
--- a/src/settings/default.yml
+++ b/src/settings/default.yml
@@ -1,4 +1,30 @@
-zOffset: 0.3
+startCode: |-
+ M109 S{temperature} ;set target temperature
+ {if heatedBed}M190 S{bedTemperature} ;set target bed temperature
+ G21 ;metric values
+ M107 ;start with the fan off
+ G28 X0 Y0 ;move X/Y to min endstops
+ G28 Z0 ;move Z to min endstops
+ G1 Z15 F9000 ;move the platform down 15mm
+ G92 E0 ;zero the extruded length
+ G91 ;relative positioning
+ G1 F200 E10 ;extrude 10mm of feed stock
+ G92 E0 ;zero the extruded length again
+ G92 E0 ;zero the extruded length again
+ G1 F9000
+ G90 ;absolute positioning
+ M117 Printing Doodle...
+endCode: |-
+ M107 ;fan off
+ G91 ;relative positioning
+ G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure
+ G1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more
+ G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way
+ M84 ;disable axes / steppers
+ G90 ;absolute positioning
+ M104 S0
+ {if heatedBed}M140 S0
+ M117 Done
dimensions:
x: 200
y: 200
@@ -7,7 +33,7 @@ heatedBed: false
nozzleDiameter: 0.4
filamentThickness: 2.85
temperature: 210
-bedTemperature: 70
+bedTemperature: 50
layerHeight: 0.15
combing: true
thickness:
@@ -23,11 +49,10 @@ travel:
speed: 200.0
support:
enabled: false
- acceptanceMargin: 1.5
+ minArea: 2
distanceY: 0.4
- gridSize: 6.0
+ density: 5.0
margin: 2.0
- plateSize: 4.0
flowRate: 0.8
speed: 40.0
innerShell:
@@ -39,12 +64,12 @@ outerShell:
innerInfill:
flowRate: 1.0
speed: 80.0
- gridSize: 15.0
+ density: 20.0
outerInfill:
flowRate: 1.0
speed: 50.0
brim:
- offset: 4.0
+ size: 8.0
flowRate: 1.0
speed: 40.0
firstLayer:
diff --git a/src/settings/infill.yml b/src/settings/infill.yml
new file mode 100644
index 0000000..14cabb6
--- /dev/null
+++ b/src/settings/infill.yml
@@ -0,0 +1,21 @@
+0pct:
+ title: Hollow (0%)
+ innerInfill:
+ density: 0.0
+10pct:
+ title: Light (10%)
+ innerInfill:
+ density: 10.0
+20pct:
+ title: Normal (20%)
+ innerInfill:
+ density: 20.0
+50pct:
+ title: Dense (50%)
+ innerInfill:
+ density: 50.0
+100pct:
+ title: Solid (100%)
+ innerInfill:
+ density: 100.0
+
diff --git a/src/settings/printer.yml b/src/settings/printer.yml
index 3bc6504..efd5b61 100644
--- a/src/settings/printer.yml
+++ b/src/settings/printer.yml
@@ -1,3 +1,33 @@
+doodle3d_printer:
+ startCode: |-
+ M140 S{bedTemperature}
+ {if heatedBed}M104 S{temperature}
+ G28
+ M109 S{temperature}
+ {if heatedBed}M190 S{bedTemperature}
+ G90
+ M82
+ G1 Z10.0 F6000
+ G92 E0
+ G1 F200 E3
+ G92 E0
+ endCode: |-
+ M104 S0
+ M140 S0
+ G92 E1
+ G1 E-1 F300
+ G28 X0 Y0
+ M84
+ M82
+ M104 S0
+ title: Doodle3D Printer
+ heatedBed: true
+ bedTemperature: 50
+ filamentThickness: 1.75
+ dimensions:
+ x: 120
+ y: 120
+ z: 120
_3Dison_plus:
title: 3Dison plus
heatedBed: false
@@ -267,11 +297,50 @@ ultimaker2_plus:
title: Ultimaker 2+
heatedBed: true
filamentThickness: 2.85
+ dimensions:
+ x: 223
+ y: 223
+ z: 205
+ultimaker2_plus_extended:
+ title: Ultimaker 2+ Extended
+ heatedBed: true
+ filamentThickness: 2.85
dimensions:
x: 223
y: 223
z: 305
ultimaker2go:
+ startCode: |-
+ M10000
+ M10000
+ M10001 X8 Y28 SDoodle3D heat up...
+ M109 S{temperature} ;set target temperature
+ {if heatedBed}M190 S{bedTemperature} ;set target bed temperature
+ G21 ;metric values
+ G90 ;absolute positioning
+ M107 ;start with the fan off
+ G28 ; home to endstops
+ G1 Z15 F9000 ;move the platform down 15mm
+ G92 E0 ;zero the extruded length
+ G1 F200 E10 ;extrude 10mm of feed stock
+ G92 E0 ;zero the extruded length again
+ G1 F9000
+ M10000
+ M10000
+ M10001 X8 Y28 SDoodle3D printing...
+ endCode: |-
+ M10000
+ M10000
+ M10001 X20 Y28 SDoodle3D done!
+ M107 ;fan off
+ G91 ;relative positioning
+ G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure
+ G1 Z+5.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more
+ G28 ;home the printer
+ M84 ;disable axes / steppers
+ G90 ;absolute positioning
+ M104 S0
+ {if heatedBed}M140 S0
title: Ultimaker 2 Go
heatedBed: false
filamentThickness: 2.85
diff --git a/src/settings/quality.yml b/src/settings/quality.yml
index 967a7b0..96134d9 100644
--- a/src/settings/quality.yml
+++ b/src/settings/quality.yml
@@ -15,7 +15,7 @@ low:
speed: 70.0
innerInfill:
speed: 80.0
- gridSize: 25.0
+ density: 10.0
medium:
title: "Medium"
layerHeight: .15
@@ -33,7 +33,7 @@ medium:
speed: 40.0
innerInfill:
speed: 80.0
- gridSize: 25.0
+ density: 10.0
high:
title: "High"
thickness:
@@ -51,4 +51,4 @@ high:
speed: 30.0
innerInfill:
speed: 70.0
- gridSize: 10.0
+ density: 20.0
diff --git a/src/sliceActions/addBrim.js b/src/sliceActions/addBrim.js
index 122f140..201d0fc 100644
--- a/src/sliceActions/addBrim.js
+++ b/src/sliceActions/addBrim.js
@@ -1,24 +1,36 @@
import Shape from 'clipper-js';
import { PRECISION } from '../constants.js';
-const offsetOptions = {
+const OFFSET_OPTIONS = {
jointType: 'jtRound',
miterLimit: 2.0,
- roundPrecision: 0.25
+ roundPrecision: 0.25,
+ endType: 'etClosedPolygon'
};
export default function addBrim(slices, settings) {
let {
- brim: { offset: brimOffset }
+ brim: { size: brimSize },
+ nozzleDiameter
} = settings;
- brimOffset /= PRECISION;
+
+ nozzleDiameter /= PRECISION;
+ brimSize /= PRECISION;
+ const nozzleRadius = nozzleDiameter / 2;
const [firstLayer] = slices;
- firstLayer.brim = firstLayer.parts.reduce((brim, { shape }) => (
- brim.join(shape.offset(brimOffset, {
- ...offsetOptions,
+ const brim = firstLayer.parts.reduce((brim, { shape }) => (
+ brim.join(shape.offset(nozzleRadius, {
+ ...OFFSET_OPTIONS,
endType: shape.closed ? 'etClosedPolygon' : 'etOpenRound'
}))
), new Shape([], true)).simplify('pftNonZero');
+
+ firstLayer.brim = new Shape([], true);
+
+ for (let offset = 0; offset < brimSize; offset += nozzleDiameter) {
+ const brimPart = brim.offset(offset, OFFSET_OPTIONS);
+ firstLayer.brim = firstLayer.brim.join(brimPart);
+ }
}
diff --git a/src/sliceActions/applyPrecision.js b/src/sliceActions/applyPrecision.js
index 0f23b23..6f20854 100644
--- a/src/sliceActions/applyPrecision.js
+++ b/src/sliceActions/applyPrecision.js
@@ -1,4 +1,4 @@
-import { devide } from './helpers/VectorUtils.js';
+import { divide } from './helpers/vector2.js';
import { PRECISION } from '../constants.js'
export default function applyPrecision(layers) {
@@ -16,7 +16,7 @@ function scaleUpShape(shape) {
const path = shape[i];
for (let i = 0; i < path.length; i ++) {
- path[i] = devide(path[i], PRECISION);
+ path[i] = divide(path[i], PRECISION);
}
}
}
diff --git a/src/sliceActions/calculateLayersIntersections.js b/src/sliceActions/calculateLayersIntersections.js
index c077bd9..1d91f51 100644
--- a/src/sliceActions/calculateLayersIntersections.js
+++ b/src/sliceActions/calculateLayersIntersections.js
@@ -1,26 +1,25 @@
+import { Z_OFFSET } from '../constants.js';
+
export default function calculateLayersIntersections(lines, settings) {
const {
dimensions: { z: dimensionsZ },
- layerHeight,
- zOffset
+ layerHeight
} = settings;
- const numLayers = Math.floor((dimensionsZ - zOffset) / layerHeight);
+ const numLayers = Math.floor((dimensionsZ - Z_OFFSET) / layerHeight);
- const layers = Array.from(Array(numLayers)).map(() => ({
- points: {},
- faceIndexes: []
- }));
+ const layerPoints = Array.from(Array(numLayers)).map(() => ({}));
+ const layerFaceIndexes = Array.from(Array(numLayers)).map(() => []);
for (let lineIndex = 0; lineIndex < lines.length; lineIndex ++) {
const { line, faces } = lines[lineIndex];
- const min = Math.ceil((Math.min(line.start.y, line.end.y) - zOffset) / layerHeight);
- const max = Math.floor((Math.max(line.start.y, line.end.y) - zOffset) / layerHeight);
+ const min = Math.ceil((Math.min(line.start.y, line.end.y) - Z_OFFSET) / layerHeight);
+ const max = Math.floor((Math.max(line.start.y, line.end.y) - Z_OFFSET) / layerHeight);
for (let layerIndex = min; layerIndex <= max; layerIndex ++) {
if (layerIndex >= 0 && layerIndex < numLayers) {
- const y = layerIndex * layerHeight + zOffset;
+ const y = layerIndex * layerHeight + Z_OFFSET;
let x, z;
if (line.start.y === line.end.y) {
@@ -33,20 +32,14 @@ export default function calculateLayersIntersections(lines, settings) {
z = line.end.z * alpha + line.start.z * alpha1;
}
- layers[layerIndex].points[lineIndex] = { x: z, y: x };
- layers[layerIndex].faceIndexes.push(...faces);
+ layerPoints[layerIndex][lineIndex] = { x: z, y: x };
+ for (const faceIndex of faces) {
+ const layerFaceIndex = layerFaceIndexes[layerIndex];
+ if (!layerFaceIndex.includes(faceIndex)) layerFaceIndex.push(faceIndex);
+ }
}
}
}
- for (let i = 0; i < layers.length; i ++) {
- const layer = layers[i];
-
- layer.faceIndexes = layer.faceIndexes.reduce((result, faceIndex) => {
- if (!result.includes(faceIndex)) result.push(faceIndex);
- return result;
- }, []);
- }
-
- return layers;
+ return { layerPoints, layerFaceIndexes };
}
diff --git a/src/sliceActions/createLines.js b/src/sliceActions/createLines.js
index 62b7c19..9fff900 100644
--- a/src/sliceActions/createLines.js
+++ b/src/sliceActions/createLines.js
@@ -1,42 +1,70 @@
-import { Line3 } from 'three/src/math/Line3.js';
-import { normalize } from './helpers/VectorUtils.js';
+import * as vector2 from './helpers/vector2.js';
+import * as vector3 from './helpers/vector3.js';
-function addLine(geometry, lineLookup, lines, a, b, faceIndex) {
+export default function createLines(geometry, settings) {
+ const faces = [];
+ const lines = [];
+ const lineLookup = {};
+
+ for (let i = 0; i < geometry.objectIndexes.length; i ++) {
+ const objectIndex = geometry.objectIndexes[i];
+ const { x: a, y: b, z: c } = getVertex(geometry.faces, i);
+ const normal = calculateNormal(geometry.vertices, a, b, c);
+
+ // skip faces that point up or down
+ if (normal.y > .999 || normal.y < -.999) {
+ faces.push(null);
+ continue;
+ }
+
+ const indexA = addLine(geometry.vertices, lineLookup, lines, a, b, i);
+ const indexB = addLine(geometry.vertices, lineLookup, lines, b, c, i);
+ const indexC = addLine(geometry.vertices, lineLookup, lines, c, a, i);
+
+ const flatNormal = vector2.normalize({ x: normal.z, y: normal.x });
+ const lineIndexes = [indexA, indexB, indexC];
+
+ faces.push({ lineIndexes, flatNormal, objectIndex });
+ }
+
+ return { lines, faces };
+}
+
+function addLine(vertices, lineLookup, lines, a, b, faceIndex) {
let index;
if (typeof lineLookup[`${b}_${a}`] !== 'undefined') {
index = lineLookup[`${b}_${a}`];
} else {
+ const start = getVertex(vertices, a);
+ const end = getVertex(vertices, b);
+ const line = { start, end };
+ const faces = [];
+
index = lines.length;
lineLookup[`${a}_${b}`] = index;
-
- const line = new Line3(geometry.vertices[a], geometry.vertices[b]);
- lines.push({ line, faces: [] });
+ lines.push({ line, faces });
}
-
- const { faces } = lines[index];
- faces.push(faceIndex);
-
+ lines[index].faces.push(faceIndex);
return index;
}
-export default function createLines(geometry, settings) {
- const lines = [];
- const lineLookup = {};
+function calculateNormal(vertices, a, b, c) {
+ a = getVertex(vertices, a);
+ b = getVertex(vertices, b);
+ c = getVertex(vertices, c);
- const faces = geometry.faces.map((face, i) => {
- const { normal, materialIndex: objectIndex, a, b, c } = geometry.faces[i];
+ const cb = vector3.subtract(c, b);
+ const ab = vector3.subtract(a, b);
+ const normal = vector3.normalize(vector3.cross(cb, ab));
- // skip faces that point up or down
- if (normal.y > .999 || normal.y < -.999) return;
-
- const indexA = addLine(geometry, lineLookup, lines, a, b, i);
- const indexB = addLine(geometry, lineLookup, lines, b, c, i);
- const indexC = addLine(geometry, lineLookup, lines, c, a, i);
-
- const flatNormal = normalize({ x: normal.z, y: normal.x });
- const lineIndexes = [indexA, indexB, indexC];
- return { lineIndexes, flatNormal, objectIndex };
- });
-
- return { lines, faces };
+ return normal;
+}
+
+function getVertex(vertices, i) {
+ const i3 = i * 3;
+ return {
+ x: vertices[i3],
+ y: vertices[i3 + 1],
+ z: vertices[i3 + 2]
+ };
}
diff --git a/src/sliceActions/generateInfills.js b/src/sliceActions/generateInfills.js
index 3ddfa56..fd04edc 100644
--- a/src/sliceActions/generateInfills.js
+++ b/src/sliceActions/generateInfills.js
@@ -5,7 +5,7 @@ import Shape from 'clipper-js';
export default function generateInfills(slices, settings) {
let {
layerHeight,
- innerInfill: { gridSize: infillGridSize },
+ innerInfill: { density },
thickness: {
top: topThickness,
bottom: bottomThickness
@@ -13,13 +13,16 @@ export default function generateInfills(slices, settings) {
nozzleDiameter
} = settings;
- infillGridSize /= PRECISION;
+ density /= 100;
nozzleDiameter /= PRECISION;
- const bottomSkinCount = Math.ceil(bottomThickness/layerHeight);
- const topSkinCount = Math.ceil(topThickness/layerHeight);
+ const bidirectionalInfill = density < 0.8;
+ const infillGridSize = nozzleDiameter * (bidirectionalInfill ? 2 : 1) / density;
+
+ const bottomSkinCount = Math.ceil(bottomThickness / layerHeight);
+ const topSkinCount = Math.ceil(topThickness / layerHeight);
const nozzleRadius = nozzleDiameter / 2;
- const outerFillTemplateSize = Math.sqrt(2 * Math.pow(nozzleDiameter, 2));
+ const outerFillTemplateSize = nozzleDiameter;
for (let layer = 0; layer < slices.length; layer ++) {
const slice = slices[layer];
@@ -32,6 +35,7 @@ export default function generateInfills(slices, settings) {
}
for (let i = 0; i < slice.parts.length; i ++) {
+ const even = (layer % 2 === 0);
const part = slice.parts[i];
if (!part.closed) continue;
@@ -52,14 +56,13 @@ export default function generateInfills(slices, settings) {
if (innerFillArea && innerFillArea.paths.length > 0) {
const bounds = innerFillArea.shapeBounds();
- const innerFillTemplate = getFillTemplate(bounds, infillGridSize, true, true);
+ const innerFillTemplate = getFillTemplate(bounds, infillGridSize, bidirectionalInfill || even, bidirectionalInfill || !even);
part.innerFill.join(innerFillTemplate.intersect(innerFillArea));
}
if (outerFillArea.paths.length > 0) {
const bounds = outerFillArea.shapeBounds();
- const even = (layer % 2 === 0);
const outerFillTemplate = getFillTemplate(bounds, outerFillTemplateSize, even, !even);
part.outerFill.join(outerFillTemplate.intersect(outerFillArea));
diff --git a/src/sliceActions/generateInnerLines.js b/src/sliceActions/generateInnerLines.js
index 04e981f..2d7e751 100644
--- a/src/sliceActions/generateInnerLines.js
+++ b/src/sliceActions/generateInnerLines.js
@@ -1,6 +1,6 @@
import { PRECISION } from '../constants.js'
-const offsetOptions = {
+const OFFSET_OPTIONS = {
jointType: 'jtSquare',
endType: 'etClosedPolygon',
miterLimit: 2.0,
@@ -29,7 +29,7 @@ export default function generateInnerLines(slices, settings) {
if (!part.closed) continue;
- const outerLine = part.shape.offset(-nozzleRadius, offsetOptions);
+ const outerLine = part.shape.offset(-nozzleRadius, OFFSET_OPTIONS);
if (outerLine.paths.length === 0) continue;
@@ -39,7 +39,7 @@ export default function generateInnerLines(slices, settings) {
for (let inset = 1; inset < numShells; inset += 1) {
const offset = inset * nozzleDiameter;
- const shell = outerLine.offset(-offset, offsetOptions);
+ const shell = outerLine.offset(-offset, OFFSET_OPTIONS);
if (shell.paths.length === 0) {
break;
diff --git a/src/sliceActions/generateSupport.js b/src/sliceActions/generateSupport.js
index 8e9e0c3..f04ffa7 100644
--- a/src/sliceActions/generateSupport.js
+++ b/src/sliceActions/generateSupport.js
@@ -2,75 +2,40 @@ import getFillTemplate from './getFillTemplate.js';
import Shape from 'clipper-js';
import { PRECISION } from '../constants.js';
+const PRECISION_SQUARED = Math.pow(PRECISION, 2);
+
export default function generateSupport(slices, settings) {
if (!settings.support.enabled) return;
let {
layerHeight,
- support: {
- gridSize: supportGridSize,
- margin: supportMargin,
- plateSize: plateSize,
- distanceY: supportDistanceY
- },
+ support: { density, margin, minArea, distanceY },
nozzleDiameter
} = settings;
- supportGridSize /= PRECISION;
- supportMargin /= PRECISION;
- plateSize /= PRECISION;
+ density /= 100;
+ margin /= PRECISION;
nozzleDiameter /= PRECISION;
- var supportDistanceLayers = Math.max(Math.ceil(supportDistanceY / layerHeight), 1);
- var supportAreas = new Shape([], true);
+ const infillGridSize = nozzleDiameter * 2 / density;
+ const supportDistanceLayers = Math.max(Math.ceil(distanceY / layerHeight), 1);
- for (var layer = slices.length - 1 - supportDistanceLayers; layer >= 0; layer --) {
- var currentSlice = slices[layer];
+ let supportArea = new Shape([], true);
- if (supportAreas.length > 0) {
+ for (let layer = slices.length - 1 - supportDistanceLayers; layer >= 0; layer --) {
+ const currentLayer = slices[layer + supportDistanceLayers - 1];
+ const upSkin = slices[layer + supportDistanceLayers];
+ const downSkin = slices[layer - supportDistanceLayers];
- if (layer >= supportDistanceLayers) {
- var sliceSkin = slices[layer - supportDistanceLayers].outline;
- sliceSkin = sliceSkin;
+ const neededSupportArea = upSkin.outline.difference(currentLayer.outline.offset(margin));
- var supportAreasSlimmed = supportAreas.difference(sliceSkin.offset(supportMargin));
- if (supportAreasSlimmed.area() < 100.0) {
- supportAreas = supportAreas.difference(sliceSkin);
- }
- else {
- supportAreas = supportAreasSlimmed;
- }
- }
+ if (neededSupportArea.totalArea() * PRECISION_SQUARED > minArea) supportArea = supportArea.union(neededSupportArea);
+ if (downSkin) supportArea = supportArea.difference(downSkin.outline.offset(margin));
- var supportTemplate = getFillTemplate(supportAreas.bounds(), supportGridSize, true, true);
- var supportFill = supportTemplate.intersect(supportAreas);
- if (supportFill.length === 0) {
- currentSlice.support = supportAreas.clone();
- }
- else {
- currentSlice.support = supportFill;
- }
- }
+ const bounds = supportArea.shapeBounds();
+ const innerFillTemplate = getFillTemplate(bounds, infillGridSize, true, true);
- var supportSkin = slices[layer + supportDistanceLayers - 1].outline;
-
- var slice = slices[layer + supportDistanceLayers];
- for (var i = 0; i < slice.parts.length; i ++) {
- var slicePart = slice.parts[i];
-
- if (slicePart.intersect.closed) {
- var outerLine = slicePart.outerLine;
- }
- else {
- var outerLine = slicePart.intersect.offset(supportMargin);
- }
-
- var overlap = supportSkin.offset(supportMargin).intersect(outerLine);
- var overhang = outerLine.difference(overlap);
-
- if (overlap.length === 0 || overhang.length > 0) {
- supportAreas = supportAreas.join(overhang);
- }
- }
+ slices[layer].support = supportArea.clone().join(supportArea.intersect(innerFillTemplate));
+ slices[layer].supportOutline = supportArea;
}
}
diff --git a/src/sliceActions/getFillTemplate.js b/src/sliceActions/getFillTemplate.js
index 98ee8be..6b3db95 100644
--- a/src/sliceActions/getFillTemplate.js
+++ b/src/sliceActions/getFillTemplate.js
@@ -1,8 +1,10 @@
import Shape from 'clipper-js';
-export default function getFillTemplate(bounds, size, even, uneven) {
+export default function getFillTemplate(bounds, gridSize, even, uneven) {
const paths = [];
+ const size = Math.sqrt(2 * Math.pow(gridSize, 2));
+
const left = Math.floor(bounds.left / size) * size;
const right = Math.ceil(bounds.right / size) * size;
const top = Math.floor(bounds.top / size) * size;
diff --git a/src/sliceActions/helpers/GCode.js b/src/sliceActions/helpers/GCode.js
index 6955cbf..aa7a04f 100644
--- a/src/sliceActions/helpers/GCode.js
+++ b/src/sliceActions/helpers/GCode.js
@@ -1,5 +1,5 @@
-import { Vector2 } from 'three/src/math/Vector2.js';
-import { PRECISION } from '../../constants.js';
+import { scale, distanceTo } from './vector2.js';
+import { PRECISION, VERSION } from '../../constants.js';
export const MOVE = 'G';
export const M_COMMAND = 'M';
@@ -10,13 +10,15 @@ export const POSITION_X = 'X';
export const POSITION_Y = 'Y';
export const POSITION_Z = 'Z';
-export default class {
- constructor(nozzleToFilamentRatio) {
- this._nozzleToFilamentRatio = nozzleToFilamentRatio;
-
- this._gcode = [];
+export default class GCode {
+ constructor(settings) {
+ this._nozzleToFilamentRatio = 1;
+ this._gcode = [
+ `; ${JSON.stringify(settings).trim()}`,
+ `; Generated with Doodle3D Slicer V${VERSION}`
+ ];
this._currentValues = {};
- this._nozzlePosition = new Vector2(0, 0);
+ this._nozzlePosition = { x: 0, y: 0 };
this._extruder = 0.0;
this._duration = 0.0;
this._isRetracted = false;
@@ -27,10 +29,16 @@ export default class {
this._gcode.push(command);
}
+ updateLayerHeight(layerHeight, nozzleDiameter, filamentThickness) {
+ const filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI;
+ const lineSurfaceArea = nozzleDiameter * layerHeight;
+ this._nozzleToFilamentRatio = lineSurfaceArea / filamentSurfaceArea;
+ }
+
turnFanOn(fanSpeed) {
this._isFanOn = true;
- const gcode = { [M_COMMAND]: 106 }
+ const gcode = { [M_COMMAND]: 106 };
if (typeof fanSpeed !== 'undefined') gcode[FAN_SPEED] = fanSpeed;
this._addGCode(gcode);
@@ -47,41 +55,41 @@ export default class {
}
moveTo(x, y, z, { speed }) {
- const newNozzlePosition = new Vector2(x, y).multiplyScalar(PRECISION);
- const lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
+ const newNozzlePosition = scale({ x, y }, PRECISION);
+ const lineLength = distanceTo(this._nozzlePosition, newNozzlePosition);
this._duration += lineLength / speed;
this._addGCode({
[MOVE]: 0,
- [POSITION_X]: newNozzlePosition.x.toFixed(3),
- [POSITION_Y]: newNozzlePosition.y.toFixed(3),
- [POSITION_Z]: z.toFixed(3),
- [SPEED]: (speed * 60).toFixed(3)
+ [POSITION_X]: newNozzlePosition.x,
+ [POSITION_Y]: newNozzlePosition.y,
+ [POSITION_Z]: z,
+ [SPEED]: speed * 60
});
- this._nozzlePosition.copy(newNozzlePosition);
+ this._nozzlePosition = newNozzlePosition;
return this;
}
lineTo(x, y, z, { speed, flowRate }) {
- const newNozzlePosition = new Vector2(x, y).multiplyScalar(PRECISION);
- const lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
+ const newNozzlePosition = scale({ x, y }, PRECISION);
+ const lineLength = distanceTo(this._nozzlePosition, newNozzlePosition);
this._extruder += this._nozzleToFilamentRatio * lineLength * flowRate;
this._duration += lineLength / speed;
this._addGCode({
[MOVE]: 1,
- [POSITION_X]: newNozzlePosition.x.toFixed(3),
- [POSITION_Y]: newNozzlePosition.y.toFixed(3),
- [POSITION_Z]: z.toFixed(3),
- [SPEED]: (speed * 60).toFixed(3),
- [EXTRUDER]: this._extruder.toFixed(3)
+ [POSITION_X]: newNozzlePosition.x,
+ [POSITION_Y]: newNozzlePosition.y,
+ [POSITION_Z]: z,
+ [SPEED]: speed * 60,
+ [EXTRUDER]: this._extruder
});
- this._nozzlePosition.copy(newNozzlePosition);
+ this._nozzlePosition = newNozzlePosition;
return this;
}
@@ -95,8 +103,8 @@ export default class {
this._addGCode({
[MOVE]: 0,
- [EXTRUDER]: this._extruder.toFixed(3),
- [SPEED]: (speed * 60).toFixed(3)
+ [EXTRUDER]: this._extruder,
+ [SPEED]: speed * 60
});
}
}
@@ -113,8 +121,8 @@ export default class {
this._addGCode({
[MOVE]: 0,
- [EXTRUDER]: (this._extruder - amount).toFixed(3),
- [SPEED]: (speed * 60).toFixed(3)
+ [EXTRUDER]: this._extruder - amount,
+ [SPEED]: speed * 60
});
}
}
@@ -122,6 +130,15 @@ export default class {
return this;
}
+ addGCode(gcode, { temperature, bedTemperature, heatedBed }) {
+ gcode = gcode
+ .replace(/{temperature}/gi, temperature)
+ .replace(/{bedTemperature}/gi, bedTemperature)
+ .replace(/{if heatedBed}/gi, heatedBed ? '' : ';');
+
+ this._addGCode(gcode);
+ }
+
getGCode() {
return {
gcode: this._gcode,
diff --git a/src/sliceActions/helpers/Slice.js b/src/sliceActions/helpers/Slice.js
index 10b22fe..2f5c085 100644
--- a/src/sliceActions/helpers/Slice.js
+++ b/src/sliceActions/helpers/Slice.js
@@ -1,6 +1,6 @@
import Shape from 'clipper-js';
-export default class {
+export default class Slice {
constructor() {
this.parts = [];
}
diff --git a/src/sliceActions/helpers/color.js b/src/sliceActions/helpers/color.js
new file mode 100644
index 0000000..c6f6f33
--- /dev/null
+++ b/src/sliceActions/helpers/color.js
@@ -0,0 +1,24 @@
+export function hslToRgb(h, s, l){
+ let r, g, b;
+
+ if (s === 0) {
+ r = g = b = lightness;
+ } else {
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ const p = 2 * l - q;
+ r = hueToRgb(p, q, h + 1 / 3);
+ g = hueToRgb(p, q, h);
+ b = hueToRgb(p, q, h - 1 / 3);
+ }
+
+ return [r, g, b];
+}
+
+function hueToRgb(p, q, t){
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
+ if (t < 1 / 2) return q;
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
+ return p;
+}
diff --git a/src/sliceActions/helpers/comb.js b/src/sliceActions/helpers/comb.js
index 67a0ea5..671e8fa 100644
--- a/src/sliceActions/helpers/comb.js
+++ b/src/sliceActions/helpers/comb.js
@@ -1,8 +1,8 @@
import Shape from 'clipper-js';
-import { subtract, add, scale, normalize, dot, length, distanceTo } from './VectorUtils.js';
+import { subtract, add, scale, normalize, dot, length, distanceTo } from './vector2.js';
import { PRECISION } from '../../constants.js';
-const TOLERANCE = 5 / PRECISION;
+const TOLERANCE = 1 / PRECISION;
export default function comb(outline, start, end) {
if (distanceTo(start, end) < TOLERANCE) {
@@ -45,9 +45,9 @@ export default function comb(outline, start, end) {
if (snappedCombPaths.length === 0) {
snappedCombPaths.push([start], [end]);
- } else if (distanceTo(firstPath[0], start) > 1.0) {
+ } else if (distanceTo(firstPath[0], start) > 1.) {
snappedCombPaths.unshift([start]);
- } else if (distanceTo(lastPath[lastPath.length - 1], end) > 1.0) {
+ } else if (distanceTo(lastPath[lastPath.length - 1], end) > 1.) {
snappedCombPaths.push([end]);
}
diff --git a/src/sliceActions/helpers/VectorUtils.js b/src/sliceActions/helpers/vector2.js
similarity index 54%
rename from src/sliceActions/helpers/VectorUtils.js
rename to src/sliceActions/helpers/vector2.js
index 83d33f0..bf10575 100644
--- a/src/sliceActions/helpers/VectorUtils.js
+++ b/src/sliceActions/helpers/vector2.js
@@ -6,18 +6,20 @@ export const add = (a, b) => ({
x: a.x + b.x,
y: a.y + b.y
});
-export const scale = (a, factor) => ({
- x: a.x * factor,
- y: a.y * factor
+export const scale = (v, factor) => ({
+ x: v.x * factor,
+ y: v.y * factor
});
-export const devide = (a, factor) => ({
- x: a.x / factor,
- y: a.y / factor
+export const divide = (v, factor) => ({
+ x: v.x / factor,
+ y: v.y / factor
});
-export const normal = (a) => ({
- x: -a.y,
- y: a.x
+export const normal = (v) => ({
+ x: -v.y,
+ y: v.x
});
+export const equals = (a, b) => a.x === b.x && a.y === b.y;
+export const almostEquals = (a, b) => Math.abs(a.x - b.x) < 0.001 && Math.abs(a.y - b.y) < 0.001;
export const dot = (a, b) => a.x * b.x + a.y * b.y;
export const length = (v) => Math.sqrt(v.x * v.x + v.y * v.y);
export const distanceTo = (a, b) => length(subtract(a, b));
@@ -28,5 +30,4 @@ export const normalize = (v) => {
x: v.x / l,
y: v.y / l
};
-}
-export const clone = (v) => ({ x: v.x, y: v.y });
+};
diff --git a/src/sliceActions/helpers/vector3.js b/src/sliceActions/helpers/vector3.js
new file mode 100644
index 0000000..7d5493e
--- /dev/null
+++ b/src/sliceActions/helpers/vector3.js
@@ -0,0 +1,38 @@
+export const subtract = (a, b) => ({
+ x: a.x - b.x,
+ y: a.y - b.y,
+ z: a.z - b.z
+});
+export const add = (a, b) => ({
+ x: a.x + b.x,
+ y: a.y + b.y,
+ z: a.z + b.z
+});
+export const scale = (v, factor) => ({
+ x: v.x * factor,
+ y: v.y * factor,
+ z: v.z * factor
+});
+export const divide = (v, factor) => ({
+ x: v.x / factor,
+ y: v.y / factor,
+ z: v.z / factor
+});
+export const cross = (a, b) => ({
+ x: a.y * b.z - a.z * b.y,
+ y: a.z * b.x - a.x * b.z,
+ z: a.x * b.y - a.y * b.x
+});
+export const equals = (a, b) => a.x === b.x && a.y === b.y && a.z === b.z;
+export const almostEquals = (a, b) => Math.abs(a.x - b.x) < 0.001 && Math.abs(a.y - b.y) < 0.001 && Math.abs(a.z - b.z) < 0.001;
+export const length = (v) => Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
+export const distanceTo = (a, b) => length(subtract(a, b));
+export const normalize = (v) => {
+ const l = length(v);
+
+ return {
+ x: v.x / l,
+ y: v.y / l,
+ z: v.z / l
+ };
+};
diff --git a/src/sliceActions/intersectionsToShapes.js b/src/sliceActions/intersectionsToShapes.js
index 0993d1e..25ba3e2 100644
--- a/src/sliceActions/intersectionsToShapes.js
+++ b/src/sliceActions/intersectionsToShapes.js
@@ -1,136 +1,157 @@
-import { subtract, normal, normalize, dot, distanceTo, clone } from './helpers/VectorUtils.js';
+import { subtract, normal, normalize, dot, almostEquals } from './helpers/vector2.js';
-export default function intersectionsToShapes(intersectionLayers, faces, openObjectIndexes, settings) {
+export default function intersectionsToShapes(layerPoints, layerFaceIndexes, faces, openObjectIndexes, settings) {
const layers = [];
- for (let layer = 0; layer < intersectionLayers.length; layer ++) {
+ for (let layer = 0; layer < layerPoints.length; layer ++) {
const fillShapes = [];
const lineShapesOpen = [];
const lineShapesClosed = [];
- const { points, faceIndexes } = intersectionLayers[layer];
+ const points = layerPoints[layer];
+ const faceIndexes = layerFaceIndexes[layer];
if (faceIndexes.length === 0) continue;
const shapes = {};
+ const startConnects = {};
+ const endConnects = {};
+
for (let i = 0; i < faceIndexes.length; i ++) {
- const { lineIndexes, objectIndex, flatNormal } = faces[faceIndexes[i]];
+ const faceIndex = faceIndexes[i];
+ const { lineIndexes, flatNormal, objectIndex } = faces[faceIndex];
- const a = points[lineIndexes[0]];
- const b = points[lineIndexes[1]];
- const c = points[lineIndexes[2]];
+ const a = lineIndexes[0];
+ const b = lineIndexes[1];
+ const c = lineIndexes[2];
- const lineSegment = [];
- if (a && b) {
- lineSegment.push(a, b);
- } else if (b && c) {
- lineSegment.push(b, c);
- } else if (c && a) {
- lineSegment.push(c, a);
+ let pointA;
+ let pointB;
+ if (points[a] && points[b]) {
+ pointA = a;
+ pointB = b;
+ } else if (points[b] && points[c]) {
+ pointA = b;
+ pointB = c;
+ } else if (points[c] && points[a]) {
+ pointA = c;
+ pointB = a;
} else {
+ // should never happen
continue;
}
- const segmentNormal = normalize(normal(subtract(lineSegment[1], lineSegment[0])));
- if (dot(segmentNormal, flatNormal) < 0) lineSegment.reverse();
+ const segmentNormal = normalize(normal(subtract(points[pointA], points[pointB])));
+ if (dot(segmentNormal, flatNormal) < 0) {
+ const temp = pointB;
+ pointB = pointA;
+ pointA = temp;
+ }
- if (!shapes[objectIndex]) shapes[objectIndex] = { lineSegments: [] };
- const shape = shapes[objectIndex];
+ if (endConnects[pointA]) {
+ const lineSegment = endConnects[pointA];
+ delete endConnects[pointA];
+ if (startConnects[pointB]) {
+ if (startConnects[pointB] === lineSegment) {
+ delete startConnects[pointB];
+ lineSegment.push(pointB);
+ } else {
+ lineSegment.push(...startConnects[pointB]);
+ endConnects[lineSegment[lineSegment.length - 1]] = lineSegment;
+ }
+ } else {
+ lineSegment.push(pointB);
+ endConnects[pointB] = lineSegment;
+ }
+ } else if (startConnects[pointB]) {
+ const lineSegment = startConnects[pointB];
+ delete startConnects[pointB];
+ if (endConnects[pointA]) {
+ lineSegment.unshift(...endConnects[pointA]);
+ startConnects[lineSegment[0]] = lineSegment;
+ } else {
+ lineSegment.unshift(pointA);
+ startConnects[pointA] = lineSegment;
+ }
+ } else {
+ const lineSegment = [pointA, pointB];
+ startConnects[pointA] = lineSegment;
+ endConnects[pointB] = lineSegment;
- shape.lineSegments.push(lineSegment)
+ if (!shapes[objectIndex]) shapes[objectIndex] = [];
+ shapes[objectIndex].push(lineSegment);
+ }
}
for (const objectIndex in shapes) {
- const shape = shapes[objectIndex];
+ const shape = shapes[objectIndex]
+ .map(lineSegment => lineSegment.map(pointIndex => points[pointIndex]))
+ .filter(lineSegment => lineSegment.some(i => !almostEquals(lineSegment[0], lineSegment[1])));
const openShape = openObjectIndexes[objectIndex];
- const lines = [shape.lineSegments.pop()];
+ const connectPoints = [];
+ for (let pathIndex = 0; pathIndex < shape.length; pathIndex ++) {
+ const path = shape[pathIndex];
- loop: while (shape.lineSegments.length !== 0) {
- for (let i = 0; i < lines.length; i ++) {
- const line = lines[i];
-
- const lastPoint = line[line.length - 1];
-
- let closestSegmentEnd;
- let endHit = false;
- const distanceEnd = new WeakMap();
- for (let i = 0; i < shape.lineSegments.length; i ++) {
- const lineSegment = shape.lineSegments[i];
- if (lastPoint === lineSegment[0]) {
- closestSegmentEnd = lineSegment;
- endHit = true;
- break;
- }
- const distance = distanceTo(lastPoint, lineSegment[0]);
- distanceEnd.set(lineSegment, distance);
- }
-
- if (!endHit) {
- closestSegmentEnd = shape.lineSegments.sort((a, b) => {
- const distanceA = distanceEnd.get(a);
- const distanceB = distanceEnd.get(b);
- if (distanceA === distanceB) return distanceTo(a[0], a[1]) - distanceTo(b[0], b[1]);
- return distanceA - distanceB;
- })[0];
-
- if (distanceTo(closestSegmentEnd[0], lastPoint) < .001) endHit = true;
- }
-
- if (endHit) {
- shape.lineSegments.splice(shape.lineSegments.indexOf(closestSegmentEnd), 1);
- line.splice(line.length, 0, closestSegmentEnd[1]);
- continue loop;
- }
-
- const firstPoint = line[0];
-
- let closestSegmentStart;
- let hitStart = false;
- const distanceStart = new WeakMap();
- for (let i = 0; i < shape.lineSegments.length; i ++) {
- const lineSegment = shape.lineSegments[i];
- if (firstPoint === lineSegment[1]) {
- closestSegmentStart = lineSegment;
- hitStart = true;
- break;
- }
- const distance = distanceTo(firstPoint, lineSegment[1]);
- distanceStart.set(lineSegment, distance);
- }
-
- if (!hitStart) {
- closestSegmentStart = shape.lineSegments.sort((a, b) => {
- const distanceA = distanceStart.get(a);
- const distanceB = distanceStart.get(b);
- if (distanceA === distanceB) return distanceTo(a[0], a[1]) - distanceTo(b[0], b[1]);
- return distanceA - distanceB;
- })[0];
-
- if (distanceTo(closestSegmentStart[1], firstPoint) < .001) hitStart = true;
- }
-
- if (hitStart) {
- shape.lineSegments.splice(shape.lineSegments.indexOf(closestSegmentStart), 1);
- line.splice(0, 0, closestSegmentStart[0]);
- continue loop;
+ if (almostEquals(path[0], path[path.length - 1])) {
+ if (openShape) {
+ lineShapesClosed.push(path);
+ } else {
+ fillShapes.push(path);
}
+ continue;
+ }
+
+ let shapeStartPoint = path[0];
+ const connectNext = connectPoints.find(({ point }) => almostEquals(point, shapeStartPoint));
+ if (connectNext) {
+ connectNext.next = pathIndex;
+ } else {
+ connectPoints.push({ point: shapeStartPoint, next: pathIndex, previous: -1 });
+ }
+
+ let shapeEndPoint = path[path.length - 1];
+ const connectPrevious = connectPoints.find(({ point }) => almostEquals(point, shapeEndPoint));
+ if (connectPrevious) {
+ connectPrevious.previous = pathIndex;
+ } else {
+ connectPoints.push({ point: shapeEndPoint, next: -1, previous: pathIndex });
}
- lines.push(shape.lineSegments.pop());
}
- if (openShape) {
- for (const line of lines) {
- const closed = distanceTo(line[0], line[line.length - 1]) < .001;
- if (closed) {
+ connectPoints.sort(({ previous }) => -previous);
+
+ while (connectPoints.length !== 0) {
+ let { next, previous } = connectPoints.pop();
+
+ const line = [];
+ if (previous !== -1) line.push(...shape[previous]);
+
+ while (true) {
+ const pointIndex = connectPoints.findIndex(point => point.previous === next);
+ if (pointIndex === -1) break;
+
+ const point = connectPoints[pointIndex];
+ line.push(...shape[point.previous]);
+
+ connectPoints.splice(pointIndex, 1);
+
+ if (point.next === -1) break;
+ if (point.next === previous) break;
+
+ next = point.next;
+ }
+
+ if (openShape) {
+ if (almostEquals(line[0], line[line.length - 1])) {
lineShapesClosed.push(line);
} else {
lineShapesOpen.push(line);
}
+ } else {
+ fillShapes.push(line);
}
- } else {
- fillShapes.push(...lines);
}
}
diff --git a/src/sliceActions/optimizePaths.js b/src/sliceActions/optimizePaths.js
index 661e7e0..bdafffe 100644
--- a/src/sliceActions/optimizePaths.js
+++ b/src/sliceActions/optimizePaths.js
@@ -1,15 +1,15 @@
-import { Vector2 } from 'three/src/math/Vector2.js';
+import { length, distanceTo } from './helpers/vector2.js';
import Shape from 'clipper-js';
export default function optimizePaths(slices, settings) {
- const start = new Vector2(0, 0);
+ let start = { x: 0, y: 0 };
for (let layer = 0; layer < slices.length; layer ++) {
const slice = slices[layer];
if (typeof slice.brim !== 'undefined' && slice.brim.paths.length > 0) {
slice.brim = optimizeShape(slice.brim, start);
- start.copy(slice.brim.lastPoint(true));
+ start = slice.brim.lastPoint(true);
}
const parts = [];
@@ -54,36 +54,34 @@ export default function optimizePaths(slices, settings) {
if (shell.paths.length === 0) continue;
part.shell[i] = optimizeShape(shell, start);
- start.copy(part.shell[i].lastPoint(true));
+ start = part.shell[i].lastPoint(true);
}
if (part.outerFill.paths.length > 0) {
part.outerFill = optimizeShape(part.outerFill, start);
- start.copy(part.outerFill.lastPoint(true));
+ start = part.outerFill.lastPoint(true);
}
if (part.innerFill.paths.length > 0) {
part.innerFill = optimizeShape(part.innerFill, start);
- start.copy(part.innerFill.lastPoint(true));
+ start = part.innerFill.lastPoint(true);
}
} else {
part.shape = optimizeShape(part.shape, start);
- start.copy(part.shape.lastPoint(true));
+ start = part.shape.lastPoint(true);
}
}
slice.parts = parts;
- if (typeof slice.support !== 'undefined' && slice.support.length > 0) {
+ if (typeof slice.support !== 'undefined' && slice.support.paths.length > 0) {
slice.support = optimizeShape(slice.support, start);
- start.copy(slice.support.lastPoint(true));
+ start = slice.support.lastPoint(true);
}
}
}
function optimizeShape(shape, start) {
- start = start.clone();
-
const inputPaths = shape.mapToLower();
const optimizedPaths = [];
const donePaths = [];
@@ -102,8 +100,7 @@ function optimizeShape(shape, start) {
if (shape.closed) {
for (let j = 0; j < path.length; j += 1) {
- const point = new Vector2().copy(path[j]);
- const length = point.sub(start).length();
+ const length = distanceTo(path[j], start);
if (minLength === false || length < minLength) {
minPath = path;
minLength = length;
@@ -112,8 +109,7 @@ function optimizeShape(shape, start) {
}
}
} else {
- const startPoint = new Vector2().copy(path[0]);
- const lengthToStart = startPoint.sub(start).length();
+ const lengthToStart = distanceTo(path[0], start);
if (minLength === false || lengthToStart < minLength) {
minPath = path;
minLength = lengthToStart;
@@ -121,8 +117,7 @@ function optimizeShape(shape, start) {
pathIndex = i;
}
- const endPoint = new Vector2().copy(path[path.length - 1]);
- const lengthToEnd = endPoint.sub(start).length();
+ const lengthToEnd = distanceTo(path[path.length - 1], start);
if (lengthToEnd < minLength) {
minPath = path;
minLength = lengthToEnd;
@@ -132,20 +127,15 @@ function optimizeShape(shape, start) {
}
}
- let point;
if (shape.closed) {
minPath = minPath.concat(minPath.splice(0, offset));
- point = minPath[0];
+ start = minPath[0];
} else {
- if (reverse) {
- minPath.reverse();
- }
- point = minPath[minPath.length - 1];
+ if (reverse) minPath.reverse();
+ start = minPath[minPath.length - 1];
}
donePaths.push(pathIndex);
- start.copy(point);
-
optimizedPaths.push(minPath);
}
diff --git a/src/sliceActions/shapesToSlices.js b/src/sliceActions/shapesToSlices.js
index a6ace31..9e439cc 100644
--- a/src/sliceActions/shapesToSlices.js
+++ b/src/sliceActions/shapesToSlices.js
@@ -1,7 +1,7 @@
import Shape from 'clipper-js';
import Slice from './helpers/Slice.js';
-import { PRECISION } from '../constants.js';
+import { PRECISION, MIN_AREA } from '../constants.js';
export default function shapesToSlices(shapes, settings) {
const sliceLayers = [];
@@ -13,6 +13,7 @@ export default function shapesToSlices(shapes, settings) {
.fixOrientation()
.simplify('pftNonZero')
.clean(1)
+ .thresholdArea(MIN_AREA / Math.pow(PRECISION, 2))
.seperateShapes();
lineShapesClosed = new Shape(lineShapesClosed, true, true, true, true)
@@ -27,23 +28,16 @@ export default function shapesToSlices(shapes, settings) {
for (let i = 0; i < fillShapes.length; i ++) {
const fillShape = fillShapes[i];
+ if (fillShape.paths.length === 0) continue;
+
slice.add(fillShape, true);
- // if (lineShapesClosed.paths.length > 0) {
- // lineShapesClosed = lineShapesClosed.difference(closedShape);
- // }
- // if (lineShapesOpen.paths.length > 0) {
- // lineShapesOpen = lineShapesOpen.difference(closedShape);
- // }
+ if (lineShapesClosed.paths.length > 0) lineShapesClosed = lineShapesClosed.difference(fillShape);
+ if (lineShapesOpen.paths.length > 0) lineShapesOpen = lineShapesOpen.difference(fillShape);
}
- if (lineShapesClosed.paths.length > 0) {
- slice.add(lineShapesClosed, false);
- }
-
- if (lineShapesOpen.paths.length > 0) {
- slice.add(lineShapesOpen, false);
- }
+ if (lineShapesClosed.paths.length > 0) slice.add(lineShapesClosed, false);
+ if (lineShapesOpen.paths.length > 0) slice.add(lineShapesOpen, false);
sliceLayers.push(slice);
}
diff --git a/src/sliceActions/slice.js b/src/sliceActions/slice.js
index 57b8232..e56b678 100644
--- a/src/sliceActions/slice.js
+++ b/src/sliceActions/slice.js
@@ -1,9 +1,3 @@
-import { Color } from 'three/src/math/Color.js';
-import { BufferGeometry } from 'three/src/core/BufferGeometry.js';
-import { BufferAttribute } from 'three/src/core/BufferAttribute.js';
-import { LineBasicMaterial } from 'three/src/materials/LineBasicMaterial.js';
-import { VertexColors } from 'three/src/constants.js';
-import { LineSegments } from 'three/src/objects/LineSegments.js';
import calculateLayersIntersections from './calculateLayersIntersections.js';
import createLines from './createLines.js';
import generateInfills from './generateInfills.js';
@@ -16,32 +10,25 @@ import optimizePaths from './optimizePaths.js';
import shapesToSlices from './shapesToSlices.js';
import slicesToGCode from './slicesToGCode.js';
import applyPrecision from './applyPrecision.js';
+import { hslToRgb } from './helpers/color.js';
// // import removePrecision from './removePrecision.js';
export default function(settings, geometry, openObjectIndexes, constructLinePreview, onProgress) {
- const totalStages = 11;
- let current = -1;
- const updateProgress = (action) => {
- current ++;
- if (typeof onProgress !== 'undefined') {
- onProgress({
- progress: {
- done: current,
- total: totalStages,
- action
- }
- });
- }
+ const total = 11;
+ let done = -1;
+ const updateProgress = action => {
+ done ++;
+ if (onProgress) onProgress({ progress: { done, total, action } });
};
updateProgress('Constructing unique lines from geometry');
const { lines, faces } = createLines(geometry, settings);
updateProgress('Calculating layer intersections');
- const layers = calculateLayersIntersections(lines, settings);
+ const { layerPoints, layerFaceIndexes } = calculateLayersIntersections(lines, settings);
updateProgress('Constructing shapes from intersections');
- const shapes = intersectionsToShapes(layers, faces, openObjectIndexes, settings);
+ const shapes = intersectionsToShapes(layerPoints, layerFaceIndexes, faces, openObjectIndexes, settings);
applyPrecision(shapes);
@@ -69,23 +56,34 @@ export default function(settings, geometry, openObjectIndexes, constructLinePrev
updateProgress('Finished');
if (constructLinePreview) gcode.linePreview = createGcodeGeometry(gcode.gcode);
- gcode.gcode = gcodeToString(gcode.gcode);
+
+ gcode.gcode = new Blob([gcodeToString(gcode.gcode)], { type: 'text/plain' });
+
return gcode;
}
+const PRECISION = 1000;
+function toFixedTrimmed(value) {
+ return (Math.round(value * PRECISION) / PRECISION).toString();
+}
+
function gcodeToString(gcode) {
const currentValues = {};
return gcode.reduce((string, command) => {
- let first = true;
- for (const action in command) {
- const value = command[action];
- const currentValue = currentValues[action];
- if (first) {
- string += `${action}${value}`;
- first = false;
- } else if (currentValue !== value) {
- string += ` ${action}${value}`;
- currentValues[action] = value;
+ if (typeof command === 'string') {
+ string += command;
+ } else {
+ let first = true;
+ for (const action in command) {
+ const value = toFixedTrimmed(command[action]);
+ const currentValue = currentValues[action];
+ if (first) {
+ string += `${action}${value}`;
+ first = false;
+ } else if (currentValue !== value) {
+ string += ` ${action}${value}`;
+ currentValues[action] = value;
+ }
}
}
string += '\n';
@@ -94,35 +92,31 @@ function gcodeToString(gcode) {
}
const MAX_SPEED = 100 * 60;
-const COLOR = new Color();
function createGcodeGeometry(gcode) {
const positions = [];
const colors = [];
let lastPoint = [0, 0, 0];
for (let i = 0; i < gcode.length; i ++) {
- const { G, F, X, Y, Z } = gcode[i];
+ const command = gcode[i];
+ if (typeof command === 'string') continue;
+
+ const { G, F, X, Y, Z } = command;
if (X || Y || Z) {
if (G === 1) {
positions.push(lastPoint.Y, lastPoint.Z, lastPoint.X);
positions.push(Y, Z, X);
- const color = (G === 0) ? COLOR.setHex(0x00ff00) : COLOR.setHSL(F / MAX_SPEED, 0.5, 0.5);
- colors.push(color.r, color.g, color.b);
- colors.push(color.r, color.g, color.b);
+ const color = (G === 0) ? [0, 1, 0] : hslToRgb(F / MAX_SPEED, 0.5, 0.5);
+ colors.push(...color, ...color);
}
lastPoint = { X, Y, Z };
}
}
- const geometry = new BufferGeometry();
-
- geometry.addAttribute('position', new BufferAttribute(new Float32Array(positions), 3));
- geometry.addAttribute('color', new BufferAttribute(new Float32Array(colors), 3));
-
- const material = new LineBasicMaterial({ vertexColors: VertexColors });
- const linePreview = new LineSegments(geometry, material);
-
- return linePreview;
+ return {
+ positions: new Float32Array(positions),
+ colors: new Float32Array(colors)
+ };
}
diff --git a/src/sliceActions/slicesToGCode.js b/src/sliceActions/slicesToGCode.js
index 790385d..7a06918 100644
--- a/src/sliceActions/slicesToGCode.js
+++ b/src/sliceActions/slicesToGCode.js
@@ -1,6 +1,7 @@
import GCode from './helpers/GCode.js';
import comb from './helpers/comb.js';
-import { PRECISION } from '../constants.js';
+import { divide } from './helpers/vector2.js';
+import { PRECISION, Z_OFFSET } from '../constants.js';
const PROFILE_TYPES = ['support', 'innerShell', 'outerShell', 'innerInfill', 'outerInfill', 'brim'];
@@ -12,15 +13,13 @@ export default function slicesToGCode(slices, settings) {
travelSpeed,
retraction,
travel,
- combing,
- zOffset
+ combing
} = settings;
- const filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI;
- const lineSurfaceArea = nozzleDiameter * layerHeight;
- const nozzleToFilamentRatio = lineSurfaceArea / filamentSurfaceArea;
+ const gcode = new GCode(settings);
+ gcode.updateLayerHeight(Z_OFFSET, nozzleDiameter, filamentThickness)
- const gcode = new GCode(nozzleToFilamentRatio);
+ if (settings.startCode) gcode.addGCode(settings.startCode, settings);
const defaultProfile = {
travelProfile: travel,
@@ -30,9 +29,10 @@ export default function slicesToGCode(slices, settings) {
let isFirstLayer = true;
for (let layer = 0; layer < slices.length; layer ++) {
const slice = slices[layer];
- const z = layer * layerHeight + zOffset;
+ const z = layer * layerHeight + Z_OFFSET;
if (layer === 1) {
+ gcode.updateLayerHeight(layerHeight, nozzleDiameter, filamentThickness);
gcode.turnFanOn();
isFirstLayer = false;
}
@@ -61,11 +61,11 @@ export default function slicesToGCode(slices, settings) {
const unRetract = isOuterShell;
const profile = isOuterShell ? profiles.outerShell : profiles.innerShell;
- pathToGCode(outline, combing && true, gcode, shell, false, unRetract, z, profile);
+ pathToGCode(outline, combing, gcode, shell, false, unRetract, z, profile);
}
- pathToGCode(outline, combing && true, gcode, part.outerFill, false, false, z, profiles.outerInfill);
- pathToGCode(outline, combing && true, gcode, part.innerFill, true, false, z, profiles.innerInfill);
+ pathToGCode(outline, combing, gcode, part.outerFill, false, false, z, profiles.outerInfill);
+ pathToGCode(outline, combing, gcode, part.innerFill, true, false, z, profiles.innerInfill);
} else {
const retract = !(slice.parts.length === 1 && typeof slice.support === 'undefined');
pathToGCode(null, false, gcode, part.shape, retract, retract, z, profiles.outerShell);
@@ -73,10 +73,12 @@ export default function slicesToGCode(slices, settings) {
}
if (typeof slice.support !== 'undefined') {
- pathToGCode(null, false, gcode, slice.support, true, true, z, profiles.support);
+ pathToGCode(slice.supportOutline, combing, gcode, slice.support, true, true, z, profiles.support);
}
}
+ if (settings.endCode) gcode.addGCode(settings.endCode, settings);
+
return gcode.getGCode();
}
@@ -93,7 +95,7 @@ function pathToGCode(outline, combing, gcode, shape, retract, unRetract, z, { li
if (i === 0) {
if (combing) {
- const combPath = comb(outline, gcode._nozzlePosition.divideScalar(PRECISION), point);
+ const combPath = comb(outline, divide(gcode._nozzlePosition, PRECISION), point);
for (let i = 0; i < combPath.length; i ++) {
const combPoint = combPath[i];
gcode.moveTo(combPoint.x, combPoint.y, z, travelProfile);
diff --git a/src/slicer.js b/src/slicer.js
index ead168b..0f77d18 100644
--- a/src/slicer.js
+++ b/src/slicer.js
@@ -1,11 +1,6 @@
-import { VertexColors } from 'three/src/constants.js';
-import { BufferAttribute } from 'three/src/core/BufferAttribute.js';
-import { LineBasicMaterial } from 'three/src/materials/LineBasicMaterial.js';
-import { LineSegments } from 'three/src/objects/LineSegments.js';
+import * as THREE from 'three';
import slice from './sliceActions/slice.js';
import SlicerWorker from './slicer.worker.js';
-import { FrontSide, DoubleSide } from 'three/src/constants.js';
-import { BufferGeometry } from 'three/src/core/BufferGeometry.js'
export function sliceMesh(settings, mesh, sync = false, constructLinePreview = false, onProgress) {
if (!mesh || !mesh.isMesh) {
@@ -21,26 +16,43 @@ export function sliceGeometry(settings, geometry, materials, matrix, sync = fals
if (!geometry) {
throw new Error('Missing required geometry argument');
} else if (geometry.isBufferGeometry) {
- geometry = new Geometry().fromBufferGeometry(geometry);
+ geometry = new THREE.Geometry().fromBufferGeometry(geometry);
} else if (geometry.isGeometry) {
geometry = geometry.clone();
} else {
throw new Error('Geometry is not an instance of BufferGeometry or Geometry');
}
- if (geometry.faces.length === 0) {
- throw new Error('Geometry does not contain any data');
- }
+ if (matrix && matrix.isMatrix4) geometry.applyMatrix(matrix);
- if (matrix && matrix.isMatrix4) {
- geometry.applyMatrix(matrix);
- }
+ const vertices = geometry.vertices.reduce((array, { x, y, z }, i) => {
+ const i3 = i * 3;
+ array[i3] = x;
+ array[i3 + 1] = y;
+ array[i3 + 2] = z;
+ return array;
+ }, new Float32Array(geometry.vertices.length * 3));
+ const faces = geometry.faces.reduce((array, { a, b, c }, i) => {
+ const i3 = i * 3;
+ array[i3] = a;
+ array[i3 + 1] = b;
+ array[i3 + 2] = c;
+ return array;
+ }, new Uint32Array(geometry.faces.length * 3));
+ const objectIndexes = geometry.faces.reduce((array, { materialIndex }, i) => {
+ array[i] = materialIndex;
+ return array;
+ }, new Uint8Array(geometry.faces.length));
+
+ if (faces.length === 0) throw new Error('Geometry does not contain any data');
+
+ geometry = { vertices, faces, objectIndexes };
const openObjectIndexes = materials instanceof Array ? materials.map(({ side }) => {
switch (side) {
- case FrontSide:
+ case THREE.FrontSide:
return false;
- case DoubleSide:
+ case THREE.DoubleSide:
return true;
default:
return false;
@@ -55,7 +67,9 @@ export function sliceGeometry(settings, geometry, materials, matrix, sync = fals
}
function sliceSync(settings, geometry, openObjectIndexes, constructLinePreview, onProgress) {
- return slice(settings, geometry, openObjectIndexes, constructLinePreview, onProgress);
+ const gcode = slice(settings, geometry, openObjectIndexes, constructLinePreview, onProgress);
+ if (gcode.linePreview) gcode.linePreview = constructLineGeometry(gcode.linePreview);
+ return gcode;
}
function sliceAsync(settings, geometry, openObjectIndexes, constructLinePreview, onProgress) {
@@ -63,10 +77,10 @@ function sliceAsync(settings, geometry, openObjectIndexes, constructLinePreview,
// create the slicer worker
const slicerWorker = new SlicerWorker();
- slicerWorker.onerror = error => {
+ slicerWorker.addEventListener('error', event => {
slicerWorker.terminate();
- reject(error);
- };
+ reject(event);
+ });
// listen to messages send from worker
slicerWorker.addEventListener('message', (event) => {
@@ -75,36 +89,36 @@ function sliceAsync(settings, geometry, openObjectIndexes, constructLinePreview,
case 'SLICE': {
slicerWorker.terminate();
- if (data.gcode.linePreview) {
- const geometry = new BufferGeometry();
+ const { gcode } = data;
+ if (gcode.linePreview) gcode.linePreview = constructLineGeometry(gcode.linePreview);
- const { position, color } = data.gcode.linePreview;
- geometry.addAttribute('position', new BufferAttribute(new Float32Array(position), 3));
- geometry.addAttribute('color', new BufferAttribute(new Float32Array(color), 3));
-
- const material = new LineBasicMaterial({ vertexColors: VertexColors });
- const linePreview = new LineSegments(geometry, material);
-
- data.gcode.linePreview = linePreview;
- }
-
- resolve(data.gcode);
+ resolve(gcode);
break;
}
case 'PROGRESS': {
- if (typeof onProgress !== 'undefined') {
- onProgress(data);
- }
+ if (typeof onProgress !== 'undefined') onProgress(data);
break;
}
}
});
- // send geometry and settings to worker to start the slicing progress
- geometry = geometry.toJSON();
+ const { vertices, faces, objectIndexes } = geometry;
+ const buffers = [vertices.buffer, faces.buffer, objectIndexes.buffer];
+
slicerWorker.postMessage({
message: 'SLICE',
data: { settings, geometry, openObjectIndexes, constructLinePreview }
- });
+ }, buffers);
});
}
+
+function constructLineGeometry(linePreview) {
+ const geometry = new THREE.BufferGeometry();
+
+ geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(linePreview.positions), 3));
+ geometry.addAttribute('color', new THREE.BufferAttribute(new Float32Array(linePreview.colors), 3));
+
+ const material = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors });
+ const mesh = new THREE.LineSegments(geometry, material);
+ return mesh;
+}
diff --git a/src/slicer.worker.js b/src/slicer.worker.js
index 0168b7b..a2fdcbc 100644
--- a/src/slicer.worker.js
+++ b/src/slicer.worker.js
@@ -1,7 +1,5 @@
import 'core-js'; // polyfills
import slice from './sliceActions/slice.js';
-import { Matrix4 } from 'three/src/math/Matrix4.js';
-import { JSONLoader } from 'three/src/loaders/JSONLoader.js';
const onProgress = progress => {
self.postMessage({
@@ -10,23 +8,18 @@ const onProgress = progress => {
});
}
-const loader = new JSONLoader();
-
-self.addEventListener('message', async (event) => {
+self.addEventListener('message', (event) => {
const { message, data } = event.data;
switch (message) {
case 'SLICE': {
- const { settings, geometry: JSONGeometry, constructLinePreview, openObjectIndexes } = data;
- const { geometry } = loader.parse(JSONGeometry.data);
+ const { settings, geometry, constructLinePreview, openObjectIndexes } = data;
const gcode = slice(settings, geometry, openObjectIndexes, constructLinePreview, onProgress);
const buffers = [];
if (gcode.linePreview) {
- const position = gcode.linePreview.geometry.getAttribute('position').array;
- const color = gcode.linePreview.geometry.getAttribute('color').array;
- buffers.push(position.buffer, color.buffer);
- gcode.linePreview = { position, color };
+ buffers.push(gcode.linePreview.positions.buffer);
+ buffers.push(gcode.linePreview.colors.buffer);
}
self.postMessage({
diff --git a/webpack.config.js b/webpack.config.js
index 8e59163..157a8d2 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,8 +1,9 @@
const path = require('path');
-// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
+const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const HTMLWebpackPlugin = require('html-webpack-plugin');
-const devMode = true;
+const devMode = process.env.NODE_ENV !== 'production';
+const analyzeBundle = process.env.ANALYZE_BUNDLE;
const babelLoader = {
loader: 'babel-loader',
@@ -34,6 +35,7 @@ module.exports = {
'clipper-lib': '@doodle3d/clipper-lib',
'clipper-js': '@doodle3d/clipper-js',
'doodle3d-core': `@doodle3d/doodle3d-core/${devMode ? 'module' : 'lib'}`,
+ 'doodle3d-api': `@doodle3d/doodle3d-api/${devMode ? 'module' : 'lib'}`,
'cal': '@doodle3d/cal'
}
},
@@ -51,7 +53,13 @@ module.exports = {
use: 'yml-loader'
}, {
test: /\.worker\.js$/,
- use: ['worker-loader', babelLoader]
+ use: [{
+ loader: 'worker-loader',
+ options: {
+ inline: false,
+ name: '[name].js'
+ }
+ }, babelLoader],
}, {
test: /\.(png|jpg|gif)$/,
use: ['url-loader?name=images/[name].[ext]']
@@ -61,15 +69,16 @@ module.exports = {
}
]
},
- plugins: [
+ plugins: analyzeBundle ? [new BundleAnalyzerPlugin()] : [
new HTMLWebpackPlugin({
- title: 'Doodle3D Slicer - Simple example',
+ favicon: 'favicon.ico',
+ title: 'Doodle3D Slicer',
template: require('html-webpack-template'),
inject: false,
appMountId: 'app'
- }),
+ })
],
- devtool: "source-map",
+ devtool: devMode ? 'source-map' : false,
devServer: {
contentBase: 'dist'
}