mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-11-22 21:47:59 +01:00
Merge branch 'develop'
This commit is contained in:
commit
24a100fbb1
80
simpleExample/coming_test.js
Normal file
80
simpleExample/coming_test.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import Shape from 'clipper-js';
|
||||||
|
import comb from '../src/sliceActions/helpers/comb.js';
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
document.body.appendChild(canvas);
|
||||||
|
canvas.width = 720;
|
||||||
|
canvas.height = 480;
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
|
||||||
|
const outline = new Shape([[
|
||||||
|
{ x: 100, y: 100 },
|
||||||
|
{ x: 400, y: 100 },
|
||||||
|
{ x: 400, y: 150 },
|
||||||
|
{ x: 200, y: 150 },
|
||||||
|
{ x: 200, y: 200 },
|
||||||
|
{ x: 400, y: 200 },
|
||||||
|
{ x: 400, y: 250 },
|
||||||
|
{ x: 200, y: 250 },
|
||||||
|
{ x: 200, y: 300 },
|
||||||
|
{ x: 400, y: 300 },
|
||||||
|
{ x: 400, y: 400 },
|
||||||
|
{ x: 100, y: 400 }
|
||||||
|
], [
|
||||||
|
{ x: 130, y: 310 },
|
||||||
|
{ x: 130, y: 370 },
|
||||||
|
{ x: 360, y: 370 },
|
||||||
|
{ x: 360, y: 360 },
|
||||||
|
{ x: 150, y: 360 },
|
||||||
|
{ x: 150, y: 350 },
|
||||||
|
{ x: 360, y: 350 },
|
||||||
|
{ x: 360, y: 340 },
|
||||||
|
{ x: 150, y: 340 },
|
||||||
|
{ x: 150, y: 330 },
|
||||||
|
{ x: 360, y: 330 },
|
||||||
|
{ x: 360, y: 310 }
|
||||||
|
]], true, true, false);
|
||||||
|
|
||||||
|
const start = { x: 380, y: 120 };
|
||||||
|
const end = { x: 200, y: 380 };
|
||||||
|
|
||||||
|
let combPath = comb(outline, start, end);
|
||||||
|
|
||||||
|
canvas.onmousemove = (event) => {
|
||||||
|
start.x = event.x;
|
||||||
|
start.y = event.y;
|
||||||
|
|
||||||
|
combPath = comb(outline, start, end);
|
||||||
|
draw();
|
||||||
|
};
|
||||||
|
|
||||||
|
draw();
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
context.strokeStyle = 'black';
|
||||||
|
for (const path of outline.mapToLower()) {
|
||||||
|
context.beginPath();
|
||||||
|
for (const point of path) {
|
||||||
|
context.lineTo(point.x, point.y);
|
||||||
|
}
|
||||||
|
context.closePath();
|
||||||
|
context.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
context.strokeStyle = 'red';
|
||||||
|
context.beginPath();
|
||||||
|
for (const point of combPath) {
|
||||||
|
context.lineTo(point.x, point.y);
|
||||||
|
}
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(start.x, start.y, 3, 0, Math.PI * 2.0, false);
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(end.x, end.y, 3, 0, Math.PI * 2.0, false);
|
||||||
|
context.stroke();
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { defaultSettings, sliceGeometry } from 'doodle3d-slicer';
|
import { defaultSettings, sliceGeometry } from 'doodle3d-slicer';
|
||||||
|
import fileURL from '!url-loader!./models/combingtest.json';
|
||||||
|
import fileSaver from 'file-saver';
|
||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
...defaultSettings.base,
|
...defaultSettings.base,
|
||||||
@ -8,14 +10,20 @@ const settings = {
|
|||||||
...defaultSettings.quality.high
|
...defaultSettings.quality.high
|
||||||
};
|
};
|
||||||
|
|
||||||
const geometry = new THREE.TorusGeometry(20, 10, 30, 30).clone();
|
const jsonLoader = new THREE.JSONLoader();
|
||||||
geometry.mergeVertices();
|
jsonLoader.load(fileURL, geometry => {
|
||||||
|
geometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / -2));
|
||||||
|
geometry.applyMatrix(new THREE.Matrix4().setPosition(new THREE.Vector3(50, -0.0, 50)));
|
||||||
|
|
||||||
const onProgress = ({ progress: { done, total, action } }) => {
|
const onProgress = ({ progress: { done, total, action } }) => {
|
||||||
const percentage = `${(done / total * 100).toFixed()}%`
|
const percentage = `${(done / total * 100).toFixed()}%`
|
||||||
document.write(`<p>${action}, ${percentage}</p>`);
|
document.write(`<p>${action}, ${percentage}</p>`);
|
||||||
};
|
};
|
||||||
|
|
||||||
sliceGeometry(settings, geometry, null, false, onProgress).then(gcode => {
|
const { filament, duration, gcode } = sliceGeometry(settings, geometry, null, true, onProgress);
|
||||||
document.body.innerHTML = gcode.replace(/(?:\r\n|\r|\n)/g, '<br />');
|
// console.log('filament: ', filament);
|
||||||
|
// console.log('duration: ', duration);
|
||||||
|
// document.body.innerHTML = gcode.replace(/(?:\r\n|\r|\n)/g, '<br />');
|
||||||
|
const file = new File([gcode], 'gcode.gcode', { type: 'text/plain' });
|
||||||
|
fileSaver.saveAs(file);
|
||||||
});
|
});
|
||||||
|
1
simpleExample/models/Doodle.json
Normal file
1
simpleExample/models/Doodle.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"vertices":[47.12482452392578,42.35624313354492,-2.593571992041396e-15,-7.293127536773682,29.7335205078125,-1.820653115308456e-15,-4.207573413848877,-19.915847778320312,1.219494014914264e-15,54.97896194458008,-14.586255073547363,8.931505683412107e-16,53.85694122314453,-42.07573699951172,2.576395944332666e-15,-35.624122619628906,-25.245441436767578,1.5458375198830296e-15,-38.99018096923828,51.6129035949707,-3.1603789970181283e-15,47.12482452392578,61.71107864379883,-3.7787139115382e-15,47.12482452392578,42.35624313354492,19.999999999999996,-7.293127536773682,29.7335205078125,19.999999999999996,-4.207573413848877,-19.915847778320312,20,54.97896194458008,-14.586255073547362,20,53.85694122314453,-42.07573699951172,20.000000000000004,-35.624122619628906,-25.245441436767578,20,-38.99018096923828,51.6129035949707,19.999999999999996,47.12482452392578,61.71107864379883,19.999999999999996],"normals":[0,-6.123234262925839e-17,-1,0,6.123234262925839e-17,1,0.22595957126202507,-0.9741366804278966,5.964867098368936e-17,0.22595959003447755,-0.9741366760734609,5.964867071705706e-17,0.9980744575480833,0.062027229424660574,-3.798072564474434e-18,0.9980744575147102,0.06202722996166134,-3.798072597356249e-18,-0.08968451372047671,0.995970224453885,-6.098559003229967e-17,-0.0896845139633491,0.9959702244320148,-6.098559003096051e-17,0.9991680515439634,-0.04078240765133586,2.4972023585526864e-18,0.9991680513997995,-0.040782411183346925,2.4972025748259983e-18,-0.18484654771165182,-0.9827673955718536,6.017714989051966e-17,-0.1848465343760269,-0.9827673980801217,6.017715004410679e-17,-0.9990423495327988,-0.04375367230285246,2.679139853736555e-18,-0.9990423494957035,-0.04375367314986121,2.6791399056008846e-18,-0.11646581097902166,0.993194701391927,-6.081563825319445e-17,-0.11646581460851489,0.9931947009663187,-6.081563822713346e-17,1,0,0],"faces":[50,7,0,1,0,0,0,0,0,50,2,3,4,0,0,0,0,0,50,6,7,1,0,0,0,0,0,50,2,4,5,0,0,0,0,0,50,5,6,1,0,0,0,0,0,50,1,2,5,0,0,0,0,0,50,13,10,9,0,1,1,1,1,50,9,14,13,0,1,1,1,1,50,13,12,10,0,1,1,1,1,50,9,15,14,0,1,1,1,1,50,12,11,10,0,1,1,1,1,50,9,8,15,0,1,1,1,1,50,0,8,1,0,2,3,3,3,50,8,9,1,0,2,3,3,3,50,1,9,2,0,4,5,5,5,50,9,10,2,0,4,5,5,5,50,2,10,3,0,6,7,7,7,50,10,11,3,0,6,7,7,7,50,3,11,4,0,8,9,9,9,50,11,12,4,0,8,9,9,9,50,4,12,5,0,10,11,11,11,50,12,13,5,0,10,11,11,11,50,5,13,6,0,12,13,13,13,50,13,14,6,0,12,13,13,13,50,6,14,7,0,14,15,15,15,50,14,15,7,0,14,15,15,15,50,7,15,0,0,16,16,16,16,50,15,8,0,0,16,16,16,16]}
|
1
simpleExample/models/combingtest.json
Normal file
1
simpleExample/models/combingtest.json
Normal file
File diff suppressed because one or more lines are too long
1
simpleExample/models/error.json
Normal file
1
simpleExample/models/error.json
Normal file
File diff suppressed because one or more lines are too long
1
simpleExample/models/shape.json
Normal file
1
simpleExample/models/shape.json
Normal file
File diff suppressed because one or more lines are too long
27
simpleExample/package-lock.json
generated
27
simpleExample/package-lock.json
generated
@ -886,8 +886,7 @@
|
|||||||
"big.js": {
|
"big.js": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz",
|
||||||
"integrity": "sha1-TK2iGTZS6zyp7I5VyQFWacmAaXg=",
|
"integrity": "sha1-TK2iGTZS6zyp7I5VyQFWacmAaXg="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"binary-extensions": {
|
"binary-extensions": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
@ -1589,8 +1588,7 @@
|
|||||||
"emojis-list": {
|
"emojis-list": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
|
||||||
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
|
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"encodeurl": {
|
"encodeurl": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@ -1897,6 +1895,11 @@
|
|||||||
"websocket-driver": "0.6.5"
|
"websocket-driver": "0.6.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"file-saver": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.3.tgz",
|
||||||
|
"integrity": "sha1-zdTETTqiZOrC9o7BZbx5HDSvEjI="
|
||||||
|
},
|
||||||
"filename-regex": {
|
"filename-regex": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
|
||||||
@ -3508,8 +3511,7 @@
|
|||||||
"json5": {
|
"json5": {
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
|
||||||
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
|
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"jsonify": {
|
"jsonify": {
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
@ -3564,7 +3566,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
|
||||||
"integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
|
"integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"big.js": "3.1.3",
|
"big.js": "3.1.3",
|
||||||
"emojis-list": "2.1.0",
|
"emojis-list": "2.1.0",
|
||||||
@ -3721,8 +3722,7 @@
|
|||||||
"mime": {
|
"mime": {
|
||||||
"version": "1.3.4",
|
"version": "1.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz",
|
||||||
"integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=",
|
"integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"mime-db": {
|
"mime-db": {
|
||||||
"version": "1.27.0",
|
"version": "1.27.0",
|
||||||
@ -5065,6 +5065,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"url-loader": {
|
||||||
|
"version": "0.5.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.9.tgz",
|
||||||
|
"integrity": "sha512-B7QYFyvv+fOBqBVeefsxv6koWWtjmHaMFT6KZWti4KRw8YUD/hOU+3AECvXuzyVawIBx3z7zQRejXCDSO5kk1Q==",
|
||||||
|
"requires": {
|
||||||
|
"loader-utils": "1.1.0",
|
||||||
|
"mime": "1.3.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"url-parse": {
|
"url-parse": {
|
||||||
"version": "1.1.9",
|
"version": "1.1.9",
|
||||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz",
|
||||||
|
@ -11,7 +11,9 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-polyfill": "^6.23.0",
|
"babel-polyfill": "^6.23.0",
|
||||||
"three": "^0.83.0"
|
"file-saver": "^1.3.3",
|
||||||
|
"three": "^0.83.0",
|
||||||
|
"url-loader": "^0.5.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-core": "^6.25.0",
|
"babel-core": "^6.25.0",
|
||||||
|
@ -8,6 +8,7 @@ filamentThickness: 2.85
|
|||||||
temperature: 210
|
temperature: 210
|
||||||
bedTemperature: 70
|
bedTemperature: 70
|
||||||
layerHeight: 0.15
|
layerHeight: 0.15
|
||||||
|
combing: true
|
||||||
thickness:
|
thickness:
|
||||||
top: 1.2
|
top: 1.2
|
||||||
bottom: 1.2
|
bottom: 1.2
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
|
import { PRECISION } from '../../constants.js';
|
||||||
|
|
||||||
const MOVE = 'G';
|
const MOVE = 'G';
|
||||||
const M_COMMAND = 'M';
|
const M_COMMAND = 'M';
|
||||||
@ -17,6 +18,7 @@ export default class {
|
|||||||
this._currentValues = {};
|
this._currentValues = {};
|
||||||
this._nozzlePosition = new THREE.Vector2(0, 0);
|
this._nozzlePosition = new THREE.Vector2(0, 0);
|
||||||
this._extruder = 0.0;
|
this._extruder = 0.0;
|
||||||
|
this._duration = 0.0;
|
||||||
this._isRetracted = false;
|
this._isRetracted = false;
|
||||||
this._isFanOn = false;
|
this._isFanOn = false;
|
||||||
}
|
}
|
||||||
@ -62,35 +64,37 @@ export default class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
moveTo(x, y, z, { speed }) {
|
moveTo(x, y, z, { speed }) {
|
||||||
speed *= 60;
|
const newNozzlePosition = new THREE.Vector2(x, y).multiplyScalar(PRECISION);
|
||||||
|
const lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
|
||||||
|
|
||||||
|
this._duration += lineLength / speed;
|
||||||
|
|
||||||
this._addGCode({
|
this._addGCode({
|
||||||
[MOVE]: 0,
|
[MOVE]: 0,
|
||||||
[POSITION_X]: x.toFixed(3),
|
[POSITION_X]: newNozzlePosition.x.toFixed(3),
|
||||||
[POSITION_Y]: y.toFixed(3),
|
[POSITION_Y]: newNozzlePosition.y.toFixed(3),
|
||||||
[POSITION_Z]: z.toFixed(3),
|
[POSITION_Z]: z.toFixed(3),
|
||||||
[SPEED]: speed.toFixed(3)
|
[SPEED]: (speed * 60).toFixed(3)
|
||||||
});
|
});
|
||||||
|
|
||||||
this._nozzlePosition.set(x, y);
|
this._nozzlePosition.copy(newNozzlePosition);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
lineTo(x, y, z, { speed, flowRate }) {
|
lineTo(x, y, z, { speed, flowRate }) {
|
||||||
const newNozzlePosition = new THREE.Vector2(x, y);
|
const newNozzlePosition = new THREE.Vector2(x, y).multiplyScalar(PRECISION);
|
||||||
|
|
||||||
speed *= 60;
|
|
||||||
|
|
||||||
const lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
|
const lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
|
||||||
|
|
||||||
this._extruder += this._nozzleToFilamentRatio * lineLength * flowRate;
|
this._extruder += this._nozzleToFilamentRatio * lineLength * flowRate;
|
||||||
|
this._duration += lineLength / speed;
|
||||||
|
|
||||||
this._addGCode({
|
this._addGCode({
|
||||||
[MOVE]: 1,
|
[MOVE]: 1,
|
||||||
[POSITION_X]: x.toFixed(3),
|
[POSITION_X]: newNozzlePosition.x.toFixed(3),
|
||||||
[POSITION_Y]: y.toFixed(3),
|
[POSITION_Y]: newNozzlePosition.y.toFixed(3),
|
||||||
[POSITION_Z]: z.toFixed(3),
|
[POSITION_Z]: z.toFixed(3),
|
||||||
[SPEED]: speed.toFixed(3),
|
[SPEED]: (speed * 60).toFixed(3),
|
||||||
[EXTRUDER]: this._extruder.toFixed(3)
|
[EXTRUDER]: this._extruder.toFixed(3)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -99,17 +103,17 @@ export default class {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
unRetract({ enabled, speed, minDistance }) {
|
unRetract({ enabled, speed, minDistance, amount }) {
|
||||||
if (this._isRetracted && enabled) {
|
if (this._isRetracted && enabled) {
|
||||||
this._isRetracted = false;
|
this._isRetracted = false;
|
||||||
|
|
||||||
speed *= 60;
|
|
||||||
|
|
||||||
if (this._extruder > minDistance) {
|
if (this._extruder > minDistance) {
|
||||||
|
this._duration += amount / speed;
|
||||||
|
|
||||||
this._addGCode({
|
this._addGCode({
|
||||||
[MOVE]: 0,
|
[MOVE]: 0,
|
||||||
[EXTRUDER]: this._extruder.toFixed(3),
|
[EXTRUDER]: this._extruder.toFixed(3),
|
||||||
[SPEED]: speed.toFixed(3)
|
[SPEED]: (speed * 60).toFixed(3)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,13 +125,13 @@ export default class {
|
|||||||
if (!this._isRetracted && enabled) {
|
if (!this._isRetracted && enabled) {
|
||||||
this._isRetracted = true;
|
this._isRetracted = true;
|
||||||
|
|
||||||
speed *= 60;
|
|
||||||
|
|
||||||
if (this._extruder > minDistance) {
|
if (this._extruder > minDistance) {
|
||||||
|
this._duration += amount / speed;
|
||||||
|
|
||||||
this._addGCode({
|
this._addGCode({
|
||||||
[MOVE]: 0,
|
[MOVE]: 0,
|
||||||
[EXTRUDER]: (this._extruder - amount).toFixed(3),
|
[EXTRUDER]: (this._extruder - amount).toFixed(3),
|
||||||
[SPEED]: speed.toFixed(3)
|
[SPEED]: (speed * 60).toFixed(3)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,6 +140,10 @@ export default class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getGCode() {
|
getGCode() {
|
||||||
return this._gcode;
|
return {
|
||||||
|
gcode: this._gcode,
|
||||||
|
duration: this._duration,
|
||||||
|
filament: this._extruder
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
src/sliceActions/helpers/VectorUtils.js
Normal file
27
src/sliceActions/helpers/VectorUtils.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
export const subtract = (a, b) => ({
|
||||||
|
x: a.x - b.x,
|
||||||
|
y: a.y - b.y
|
||||||
|
});
|
||||||
|
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 normal = (a) => ({
|
||||||
|
x: -a.y,
|
||||||
|
y: a.x
|
||||||
|
});
|
||||||
|
export const dot = (a, b) => a.x * b.x + a.y * b.y;
|
||||||
|
export const length = (a) => Math.sqrt(a.x * a.x + a.y * a.y);
|
||||||
|
export const distanceTo = (a, b) => length(subtract(a, b));
|
||||||
|
export const normalize = (a) => {
|
||||||
|
const l = length(a);
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: a.x / l,
|
||||||
|
y: a.y / l
|
||||||
|
};
|
||||||
|
}
|
131
src/sliceActions/helpers/comb.js
Normal file
131
src/sliceActions/helpers/comb.js
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import Shape from 'clipper-js';
|
||||||
|
import { subtract, add, scale, normalize, dot, length, distanceTo } from './VectorUtils.js';
|
||||||
|
import { PRECISION } from '../../constants.js';
|
||||||
|
|
||||||
|
const TOLERANCE = 5 / PRECISION;
|
||||||
|
|
||||||
|
export default function comb(outline, start, end) {
|
||||||
|
if (distanceTo(start, end) < TOLERANCE) {
|
||||||
|
return [start, end];
|
||||||
|
}
|
||||||
|
|
||||||
|
let combPath = new Shape([[start, end]], false, true, false);
|
||||||
|
|
||||||
|
for (let i = 0; i < outline.paths.length; i ++) {
|
||||||
|
let outlinePart = new Shape([outline.paths[i]], true, false, false, true);
|
||||||
|
|
||||||
|
let snappedCombPaths = outlinePart.orientation(0) ? combPath.intersect(outlinePart) : combPath.difference(outlinePart);
|
||||||
|
|
||||||
|
snappedCombPaths = snappedCombPaths.mapToLower();
|
||||||
|
outlinePart = outlinePart.mapToLower()[0];
|
||||||
|
|
||||||
|
if (distanceTo(start, outlinePart[outlinePart.length - 1]) < distanceTo(start, outlinePart[0])) {
|
||||||
|
outlinePart = outlinePart.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
const distanceMap = new WeakMap();
|
||||||
|
|
||||||
|
for (let i = 0; i < snappedCombPaths.length; i ++) {
|
||||||
|
const snappedCombPath = snappedCombPaths[i];
|
||||||
|
|
||||||
|
const distanceStart = distanceTo(start, snappedCombPath[0]);
|
||||||
|
const distanceEnd = distanceTo(start, snappedCombPath[snappedCombPath.length - 1]);
|
||||||
|
|
||||||
|
if (distanceStart < distanceEnd) {
|
||||||
|
distanceMap.set(snappedCombPath, distanceStart);
|
||||||
|
} else {
|
||||||
|
snappedCombPath.reverse();
|
||||||
|
distanceMap.set(snappedCombPath, distanceEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snappedCombPaths.sort((a, b) => distanceMap.get(a) - distanceMap.get(b));
|
||||||
|
|
||||||
|
const firstPath = snappedCombPaths[0];
|
||||||
|
const lastPath = snappedCombPaths[snappedCombPaths.length - 1];
|
||||||
|
|
||||||
|
if (snappedCombPaths.length === 0) {
|
||||||
|
snappedCombPaths.push([start], [end]);
|
||||||
|
} else if (distanceTo(firstPath[0], start) > 1.0) {
|
||||||
|
snappedCombPaths.unshift([start]);
|
||||||
|
} else if (distanceTo(lastPath[lastPath.length - 1], end) > 1.0) {
|
||||||
|
snappedCombPaths.push([end]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snappedCombPaths.length === 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startPath = snappedCombPaths[0];
|
||||||
|
const startPoint = startPath[startPath.length - 1];
|
||||||
|
|
||||||
|
const endPath = snappedCombPaths[snappedCombPaths.length - 1];
|
||||||
|
const endPoint = endPath[0];
|
||||||
|
|
||||||
|
const lineIndexStart = findClosestLineOnPath(outlinePart, startPoint);
|
||||||
|
const lineIndexEnd = findClosestLineOnPath(outlinePart, endPoint);
|
||||||
|
|
||||||
|
const path = [];
|
||||||
|
if (lineIndexEnd === lineIndexStart) {
|
||||||
|
continue;
|
||||||
|
} else if (lineIndexEnd > lineIndexStart) {
|
||||||
|
if (lineIndexStart + outlinePart.length - lineIndexEnd < lineIndexEnd - lineIndexStart) {
|
||||||
|
for (let i = lineIndexStart + outlinePart.length; i > lineIndexEnd; i --) {
|
||||||
|
path.push(outlinePart[i % outlinePart.length]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = lineIndexStart; i < lineIndexEnd; i ++) {
|
||||||
|
path.push(outlinePart[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (lineIndexEnd + outlinePart.length - lineIndexStart < lineIndexStart - lineIndexEnd) {
|
||||||
|
for (let i = lineIndexStart; i < lineIndexEnd + outlinePart.length; i ++) {
|
||||||
|
path.push(outlinePart[(i + 1) % outlinePart.length]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = lineIndexStart; i > lineIndexEnd; i --) {
|
||||||
|
path.push(outlinePart[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
combPath = new Shape([[...startPath, ...path, ...endPath]], false, true, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return combPath.mapToLower()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function findClosestLineOnPath(path, point) {
|
||||||
|
let distance = Infinity;
|
||||||
|
let lineIndex;
|
||||||
|
|
||||||
|
for (let i = 0; i < path.length; i ++) {
|
||||||
|
const pointA = path[i];
|
||||||
|
const pointB = path[(i + 1) % path.length];
|
||||||
|
|
||||||
|
const tempClosestPoint = findClosestPointOnLine(pointA, pointB, point);
|
||||||
|
const tempDistance = distanceTo(tempClosestPoint, point);
|
||||||
|
|
||||||
|
if (tempDistance < distance) {
|
||||||
|
distance = tempDistance;
|
||||||
|
lineIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lineIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findClosestPointOnLine(a, b, c) {
|
||||||
|
const b_ = subtract(b, a);
|
||||||
|
const c_ = subtract(c, a);
|
||||||
|
|
||||||
|
const lambda = dot(normalize(b_), c_) / length(b_);
|
||||||
|
|
||||||
|
if (lambda >= 1) {
|
||||||
|
return b;
|
||||||
|
} else if (lambda > 0) {
|
||||||
|
return add(a, scale(b_, lambda));
|
||||||
|
} else {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ import shapesToSlices from './shapesToSlices.js';
|
|||||||
import slicesToGCode from './slicesToGCode.js';
|
import slicesToGCode from './slicesToGCode.js';
|
||||||
import detectOpenClosed from './detectOpenClosed.js';
|
import detectOpenClosed from './detectOpenClosed.js';
|
||||||
import applyPrecision from './applyPrecision.js';
|
import applyPrecision from './applyPrecision.js';
|
||||||
import removePrecision from './removePrecision.js';
|
// import removePrecision from './removePrecision.js';
|
||||||
|
|
||||||
export default function(settings, geometry, onProgress) {
|
export default function(settings, geometry, onProgress) {
|
||||||
const totalStages = 12;
|
const totalStages = 12;
|
||||||
@ -65,7 +65,7 @@ export default function(settings, geometry, onProgress) {
|
|||||||
updateProgress('Optimizing paths');
|
updateProgress('Optimizing paths');
|
||||||
optimizePaths(slices, settings);
|
optimizePaths(slices, settings);
|
||||||
|
|
||||||
removePrecision(slices);
|
// removePrecision(slices);
|
||||||
|
|
||||||
updateProgress('Constructing gcode');
|
updateProgress('Constructing gcode');
|
||||||
const gcode = slicesToGCode(slices, settings);
|
const gcode = slicesToGCode(slices, settings);
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import GCode from './helpers/GCode.js';
|
import GCode from './helpers/GCode.js';
|
||||||
|
import comb from './helpers/comb.js';
|
||||||
|
import { PRECISION } from '../constants.js';
|
||||||
|
|
||||||
const PROFILE_TYPES = ['support', 'innerShell', 'outerShell', 'innerInfill', 'outerInfill', 'brim'];
|
const PROFILE_TYPES = ['support', 'innerShell', 'outerShell', 'innerInfill', 'outerInfill', 'brim'];
|
||||||
|
|
||||||
@ -9,7 +11,8 @@ export default function slicesToGCode(slices, settings) {
|
|||||||
nozzleDiameter,
|
nozzleDiameter,
|
||||||
travelSpeed,
|
travelSpeed,
|
||||||
retraction,
|
retraction,
|
||||||
travel
|
travel,
|
||||||
|
combing
|
||||||
} = settings;
|
} = settings;
|
||||||
|
|
||||||
const filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI;
|
const filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI;
|
||||||
@ -42,11 +45,12 @@ export default function slicesToGCode(slices, settings) {
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
if (typeof slice.brim !== 'undefined') {
|
if (typeof slice.brim !== 'undefined') {
|
||||||
pathToGCode(gcode, slice.brim, true, true, z, profiles.brim);
|
pathToGCode(null, false, gcode, slice.brim, true, true, z, profiles.brim);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < slice.parts.length; i ++) {
|
for (let i = 0; i < slice.parts.length; i ++) {
|
||||||
const part = slice.parts[i];
|
const part = slice.parts[i];
|
||||||
|
const outline = part.shell[0];
|
||||||
|
|
||||||
if (part.closed) {
|
if (part.closed) {
|
||||||
for (let i = 0; i < part.shell.length; i ++) {
|
for (let i = 0; i < part.shell.length; i ++) {
|
||||||
@ -55,26 +59,26 @@ export default function slicesToGCode(slices, settings) {
|
|||||||
|
|
||||||
const unRetract = isOuterShell;
|
const unRetract = isOuterShell;
|
||||||
const profile = isOuterShell ? profiles.outerShell : profiles.innerShell;
|
const profile = isOuterShell ? profiles.outerShell : profiles.innerShell;
|
||||||
pathToGCode(gcode, shell, false, unRetract, z, profile);
|
pathToGCode(outline, combing && true, gcode, shell, false, unRetract, z, profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
pathToGCode(gcode, part.outerFill, false, false, z, profiles.outerInfill);
|
pathToGCode(outline, combing && true, gcode, part.outerFill, false, false, z, profiles.outerInfill);
|
||||||
pathToGCode(gcode, part.innerFill, true, false, z, profiles.innerInfill);
|
pathToGCode(outline, combing && true, gcode, part.innerFill, true, false, z, profiles.innerInfill);
|
||||||
} else {
|
} else {
|
||||||
const retract = !(slice.parts.length === 1 && typeof slice.support === 'undefined');
|
const retract = !(slice.parts.length === 1 && typeof slice.support === 'undefined');
|
||||||
pathToGCode(gcode, part.shape, retract, retract, z, profiles.outerShell);
|
pathToGCode(null, false, gcode, part.shape, retract, retract, z, profiles.outerShell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof slice.support !== 'undefined') {
|
if (typeof slice.support !== 'undefined') {
|
||||||
pathToGCode(gcode, slice.support, true, true, z, profiles.support);
|
pathToGCode(null, false, gcode, slice.support, true, true, z, profiles.support);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return gcode.getGCode();
|
return gcode.getGCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
function pathToGCode(gcode, shape, retract, unRetract, z, { lineProfile, travelProfile, retractionProfile }) {
|
function pathToGCode(outline, combing, gcode, shape, retract, unRetract, z, { lineProfile, travelProfile, retractionProfile }) {
|
||||||
const { closed } = shape;
|
const { closed } = shape;
|
||||||
const paths = shape.mapToLower();
|
const paths = shape.mapToLower();
|
||||||
|
|
||||||
@ -86,9 +90,15 @@ function pathToGCode(gcode, shape, retract, unRetract, z, { lineProfile, travelP
|
|||||||
const point = line[i % line.length];
|
const point = line[i % line.length];
|
||||||
|
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
// TODO
|
if (combing) {
|
||||||
// moveTo should impliment combing
|
const combPath = comb(outline, gcode._nozzlePosition.divideScalar(PRECISION), point);
|
||||||
|
for (let i = 0; i < combPath.length; i ++) {
|
||||||
|
const combPoint = combPath[i];
|
||||||
|
gcode.moveTo(combPoint.x, combPoint.y, z, travelProfile);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
gcode.moveTo(point.x, point.y, z, travelProfile);
|
gcode.moveTo(point.x, point.y, z, travelProfile);
|
||||||
|
}
|
||||||
|
|
||||||
if (unRetract) {
|
if (unRetract) {
|
||||||
gcode.unRetract(retractionProfile);
|
gcode.unRetract(retractionProfile);
|
||||||
|
Loading…
Reference in New Issue
Block a user