mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-11-22 13:37:58 +01:00
Merge branch 'restructure' into develop
This commit is contained in:
commit
d016aa345b
95
config.js
95
config.js
@ -1,95 +0,0 @@
|
||||
System.config({
|
||||
baseURL: "/",
|
||||
defaultJSExtensions: true,
|
||||
transpiler: "babel",
|
||||
babelOptions: {
|
||||
"optional": [
|
||||
"runtime"
|
||||
]
|
||||
},
|
||||
paths: {
|
||||
"github:*": "jspm_packages/github/*",
|
||||
"npm:*": "jspm_packages/npm/*"
|
||||
},
|
||||
bundles: {
|
||||
"bundle.js": []
|
||||
},
|
||||
|
||||
map: {
|
||||
"babel": "npm:babel-core@5.8.21",
|
||||
"babel-runtime": "npm:babel-runtime@5.8.20",
|
||||
"clipper-lib": "npm:clipper-lib@1.0.0",
|
||||
"core-js": "npm:core-js@0.9.18",
|
||||
"json": "github:systemjs/plugin-json@0.1.0",
|
||||
"nodeca/js-yaml": "github:nodeca/js-yaml@3.3.1",
|
||||
"read-yaml": "npm:read-yaml@1.0.0",
|
||||
"systemjs/plugin-json": "github:systemjs/plugin-json@0.1.0",
|
||||
"three.js": "github:mrdoob/three.js@r72",
|
||||
"github:jspm/nodelibs-assert@0.1.0": {
|
||||
"assert": "npm:assert@1.3.0"
|
||||
},
|
||||
"github:jspm/nodelibs-path@0.1.0": {
|
||||
"path-browserify": "npm:path-browserify@0.0.0"
|
||||
},
|
||||
"github:jspm/nodelibs-process@0.1.1": {
|
||||
"process": "npm:process@0.10.1"
|
||||
},
|
||||
"github:jspm/nodelibs-util@0.1.0": {
|
||||
"util": "npm:util@0.10.3"
|
||||
},
|
||||
"npm:argparse@1.0.2": {
|
||||
"assert": "github:jspm/nodelibs-assert@0.1.0",
|
||||
"fs": "github:jspm/nodelibs-fs@0.1.2",
|
||||
"lodash": "npm:lodash@3.10.1",
|
||||
"path": "github:jspm/nodelibs-path@0.1.0",
|
||||
"process": "github:jspm/nodelibs-process@0.1.1",
|
||||
"sprintf-js": "npm:sprintf-js@1.0.3",
|
||||
"util": "github:jspm/nodelibs-util@0.1.0"
|
||||
},
|
||||
"npm:assert@1.3.0": {
|
||||
"util": "npm:util@0.10.3"
|
||||
},
|
||||
"npm:babel-runtime@5.8.20": {
|
||||
"process": "github:jspm/nodelibs-process@0.1.1"
|
||||
},
|
||||
"npm:clipper-lib@1.0.0": {
|
||||
"process": "github:jspm/nodelibs-process@0.1.1"
|
||||
},
|
||||
"npm:core-js@0.9.18": {
|
||||
"fs": "github:jspm/nodelibs-fs@0.1.2",
|
||||
"process": "github:jspm/nodelibs-process@0.1.1",
|
||||
"systemjs-json": "github:systemjs/plugin-json@0.1.0"
|
||||
},
|
||||
"npm:esprima@2.2.0": {
|
||||
"fs": "github:jspm/nodelibs-fs@0.1.2",
|
||||
"process": "github:jspm/nodelibs-process@0.1.1"
|
||||
},
|
||||
"npm:inherits@2.0.1": {
|
||||
"util": "github:jspm/nodelibs-util@0.1.0"
|
||||
},
|
||||
"npm:js-yaml@3.3.1": {
|
||||
"argparse": "npm:argparse@1.0.2",
|
||||
"esprima": "npm:esprima@2.2.0",
|
||||
"fs": "github:jspm/nodelibs-fs@0.1.2",
|
||||
"path": "github:jspm/nodelibs-path@0.1.0",
|
||||
"process": "github:jspm/nodelibs-process@0.1.1",
|
||||
"systemjs-json": "github:systemjs/plugin-json@0.1.0",
|
||||
"util": "github:jspm/nodelibs-util@0.1.0"
|
||||
},
|
||||
"npm:lodash@3.10.1": {
|
||||
"process": "github:jspm/nodelibs-process@0.1.1"
|
||||
},
|
||||
"npm:path-browserify@0.0.0": {
|
||||
"process": "github:jspm/nodelibs-process@0.1.1"
|
||||
},
|
||||
"npm:read-yaml@1.0.0": {
|
||||
"fs": "github:jspm/nodelibs-fs@0.1.2",
|
||||
"js-yaml": "npm:js-yaml@3.3.1",
|
||||
"xtend": "npm:xtend@4.0.0"
|
||||
},
|
||||
"npm:util@0.10.3": {
|
||||
"inherits": "npm:inherits@2.0.1",
|
||||
"process": "github:jspm/nodelibs-process@0.1.1"
|
||||
}
|
||||
}
|
||||
});
|
@ -1,19 +1,17 @@
|
||||
import THREE from 'three.js';
|
||||
import PRINTER_SETTINGS from 'settings/printer_settings.json!';
|
||||
import USER_SETTINGS from 'settings/user_settings.json!';
|
||||
import * as SLICER from 'src/index';
|
||||
|
||||
var settings = new SLICER.Settings();
|
||||
settings.updateConfig(PRINTER_SETTINGS["ultimaker2go"]);
|
||||
settings.updateConfig(USER_SETTINGS);
|
||||
const settings = new SLICER.Settings({
|
||||
...SLICER.printerSettings['ultimaker2go'],
|
||||
...SLICER.userSettings
|
||||
});
|
||||
|
||||
var geometry = new THREE.TorusGeometry(20, 10, 30, 30);
|
||||
const geometry = new THREE.TorusGeometry(20, 10, 30, 30);
|
||||
|
||||
var slicer = new SLICER.Slicer();
|
||||
//var slicer = new SLICER.SlicerWorker();
|
||||
const slicer = new SLICER.Slicer();
|
||||
|
||||
slicer.setGeometry(geometry.clone());
|
||||
slicer.onfinish = function (gCode) {
|
||||
document.getElementById('gcode').innerHTML = gCode.replace(/(?:\r\n|\r|\n)/g, '<br />');
|
||||
};
|
||||
slicer.setGeometry(geometry);
|
||||
slicer.addEventListener('finish', ({ gcode }) => {
|
||||
document.getElementById('gcode').innerHTML = gcode.replace(/(?:\r\n|\r|\n)/g, '<br />');
|
||||
});
|
||||
slicer.slice(settings);
|
||||
|
@ -11,7 +11,7 @@
|
||||
</style>
|
||||
|
||||
<script type="text/javascript" src="../jspm_packages/system.js"></script>
|
||||
<script type="text/javascript" src="../config.js"></script>
|
||||
<script type="text/javascript" src="../jspm.config.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
System.import('example/app.js');
|
||||
|
70
jspm.config.js
Normal file
70
jspm.config.js
Normal file
@ -0,0 +1,70 @@
|
||||
SystemJS.config({
|
||||
paths: {
|
||||
"github:": "jspm_packages/github/",
|
||||
"npm:": "jspm_packages/npm/",
|
||||
"slicer/": "src/"
|
||||
},
|
||||
browserConfig: {
|
||||
"baseURL": "/"
|
||||
},
|
||||
devConfig: {
|
||||
"map": {
|
||||
"babel-runtime": "npm:babel-runtime@5.8.38",
|
||||
"core-js": "npm:core-js@0.9.18",
|
||||
"process": "github:jspm/nodelibs-process@0.2.0-alpha",
|
||||
"fs": "github:jspm/nodelibs-fs@0.2.0-alpha",
|
||||
"plugin-babel": "npm:systemjs-plugin-babel@0.0.12"
|
||||
},
|
||||
"packages": {
|
||||
"npm:babel-runtime@5.8.38": {
|
||||
"map": {}
|
||||
},
|
||||
"npm:core-js@0.9.18": {
|
||||
"map": {
|
||||
"systemjs-json": "github:systemjs/plugin-json@0.1.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
transpiler: "plugin-babel",
|
||||
babelOptions: {
|
||||
"optional": [
|
||||
"runtime"
|
||||
]
|
||||
},
|
||||
packages: {
|
||||
"slicer": {
|
||||
"main": "index.js"
|
||||
}
|
||||
},
|
||||
bundles: {
|
||||
"bundle.js": []
|
||||
},
|
||||
map: {
|
||||
"babel": "npm:babel-core@5.8.38"
|
||||
}
|
||||
});
|
||||
|
||||
SystemJS.config({
|
||||
packageConfigPaths: [
|
||||
"npm:@*/*.json",
|
||||
"npm:*.json",
|
||||
"github:*/*.json"
|
||||
],
|
||||
map: {
|
||||
"json": "github:systemjs/plugin-json@0.1.2",
|
||||
"Doodle3D/clipper-js": "github:Doodle3D/clipper-js@master",
|
||||
"casperlamboo/EventDispatcher": "github:casperlamboo/EventDispatcher@master",
|
||||
"three.js": "github:mrdoob/three.js@r72"
|
||||
},
|
||||
packages: {
|
||||
"github:Doodle3D/clipper-js@master": {
|
||||
"map": {
|
||||
"clipper-lib": "npm:clipper-lib@1.0.0"
|
||||
}
|
||||
},
|
||||
"npm:clipper-lib@1.0.0": {
|
||||
"map": {}
|
||||
}
|
||||
}
|
||||
});
|
25
package.json
25
package.json
@ -1,20 +1,31 @@
|
||||
{
|
||||
"jspm": {
|
||||
"main": "index",
|
||||
"name": "slicer",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"lib": "src"
|
||||
},
|
||||
"dependencies": {
|
||||
"clipper-lib": "npm:clipper-lib@^1.0.0",
|
||||
"nodeca/js-yaml": "github:nodeca/js-yaml@^3.3.1",
|
||||
"read-yaml": "npm:read-yaml@^1.0.0",
|
||||
"systemjs/plugin-json": "github:systemjs/plugin-json@^0.1.0",
|
||||
"Doodle3D/clipper-js": "github:Doodle3D/clipper-js@master",
|
||||
"casperlamboo/EventDispatcher": "github:casperlamboo/EventDispatcher@master",
|
||||
"json": "github:systemjs/plugin-json@^0.1.2",
|
||||
"three.js": "github:mrdoob/three.js@r72"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel": "npm:babel-core@^5.1.13",
|
||||
"babel-runtime": "npm:babel-runtime@^5.1.13",
|
||||
"core-js": "npm:core-js@^0.9.4"
|
||||
"core-js": "npm:core-js@^0.9.4",
|
||||
"fs": "github:jspm/nodelibs-fs@^0.2.0-alpha",
|
||||
"plugin-babel": "npm:systemjs-plugin-babel@^0.0.12",
|
||||
"process": "github:jspm/nodelibs-process@^0.2.0-alpha"
|
||||
},
|
||||
"overrides": {
|
||||
"npm:babel-runtime@5.8.38": {
|
||||
"main": false,
|
||||
"dependencies": {},
|
||||
"optionalDependencies": {
|
||||
"core-js": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
src/constants.js
Normal file
2
src/constants.js
Normal file
@ -0,0 +1,2 @@
|
||||
export const CLEAN_DELTA = 0.01;
|
||||
export const PRECISION = 0.01;
|
155
src/gcode.js
155
src/gcode.js
@ -1,7 +1,16 @@
|
||||
import THREE from 'three.js';
|
||||
|
||||
const G_COMMAND = 'G';
|
||||
const M_COMMAND = 'M';
|
||||
const FAN_SPEED = 'S';
|
||||
const SPEED = 'F';
|
||||
const EXTRUDER = 'E';
|
||||
const POSITION_X = 'X';
|
||||
const POSITION_Y = 'Y';
|
||||
const POSITION_Z = 'Z';
|
||||
|
||||
export default class {
|
||||
constructor () {
|
||||
constructor(settings) {
|
||||
this.gcode = '';
|
||||
this.current = {};
|
||||
|
||||
@ -10,71 +19,73 @@ export default class {
|
||||
this.isRetracted = false;
|
||||
this.isFanOn = false;
|
||||
this._nozzlePosition = new THREE.Vector2(0, 0);
|
||||
|
||||
if (settings !== undefined) {
|
||||
this.setSettings(settings);
|
||||
}
|
||||
}
|
||||
|
||||
_addGCode (command) {
|
||||
var str = '';
|
||||
var first = true;
|
||||
_addGCode(command) {
|
||||
let str = '';
|
||||
let first = true;
|
||||
|
||||
for (var i in command) {
|
||||
for (const action in command) {
|
||||
const value = command[action];
|
||||
const currentValue = this.current[action];
|
||||
if (first) {
|
||||
str = i + command[i];
|
||||
str = action + value;
|
||||
|
||||
first = false;
|
||||
}
|
||||
else if (this.current[i] !== command[i]) {
|
||||
str += ' ' + i + command[i];
|
||||
} else if (currentValue !== value) {
|
||||
str += ` ${action}${value}`;
|
||||
|
||||
this.current[i] = command[i];
|
||||
this.current[action] = value;
|
||||
}
|
||||
}
|
||||
|
||||
this.gcode += str + '\n';
|
||||
this.gcode += `${str}\n`;
|
||||
}
|
||||
|
||||
setSettings (settings) {
|
||||
setSettings(settings) {
|
||||
this.settings = settings;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
turnFanOn (fanSpeed) {
|
||||
turnFanOn(fanSpeed) {
|
||||
this.isFanOn = true;
|
||||
|
||||
var gcode = {
|
||||
'M': 106
|
||||
}
|
||||
|
||||
if (fanSpeed !== undefined) {
|
||||
gcode['S'] = fanSpeed;
|
||||
}
|
||||
const gcode = { [M_COMMAND]: 106 }
|
||||
if (fanSpeed !== undefined) gcode[FAN_SPEED] = fanSpeed;
|
||||
|
||||
this._addGCode(gcode);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
turnFanOff () {
|
||||
turnFanOff() {
|
||||
this.isFanOn = false;
|
||||
|
||||
this._addGCode({
|
||||
'M': 107
|
||||
});
|
||||
this._addGCode({ [M_COMMAND]: 107 });
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
moveTo (x, y, layer) {
|
||||
var layerHeight = this.settings.config['layerHeight'];
|
||||
var travelSpeed = this.settings.config['travelSpeed'];
|
||||
moveTo(x, y, layer) {
|
||||
const {
|
||||
layerHeight,
|
||||
travelSpeed
|
||||
} = this.settings.config;
|
||||
|
||||
var z = (layer + 1) * layerHeight;
|
||||
var speed = travelSpeed * 60;
|
||||
const z = (layer + 1) * layerHeight;
|
||||
const speed = travelSpeed * 60;
|
||||
|
||||
this._addGCode({
|
||||
'G': 0,
|
||||
'X': x.toFixed(3), 'Y': y.toFixed(3), 'Z': z.toFixed(3),
|
||||
'F': speed.toFixed(3)
|
||||
[G_COMMAND]: 0,
|
||||
[POSITION_X]: x.toFixed(3),
|
||||
[POSITION_Y]: y.toFixed(3),
|
||||
[POSITION_Z]: z.toFixed(3),
|
||||
[SPEED]: speed.toFixed(3)
|
||||
});
|
||||
|
||||
this._nozzlePosition.set(x, y);
|
||||
@ -82,30 +93,38 @@ export default class {
|
||||
return this;
|
||||
}
|
||||
|
||||
lineTo (x, y, layer, type) {
|
||||
var newNozzlePosition = new THREE.Vector2(x, y);
|
||||
lineTo(x, y, layer, type) {
|
||||
const newNozzlePosition = new THREE.Vector2(x, y);
|
||||
|
||||
var layerHeight = this.settings.config['layerHeight'];
|
||||
var nozzleDiameter = this.settings.config['nozzleDiameter'];
|
||||
var filamentThickness = this.settings.config['filamentThickness'];
|
||||
var travelSpeed = this.settings.config['travelSpeed'];
|
||||
const {
|
||||
layerHeight,
|
||||
nozzleDiameter,
|
||||
filamentThickness,
|
||||
travelSpeed
|
||||
} = this.settings.config;
|
||||
|
||||
var profile = this.settings.config[(this.bottom ? 'bottom' : type)];
|
||||
const profile = this.settings.config[(this.bottom ? 'bottom' : type)];
|
||||
|
||||
var speed = profile['speed'] * 60;
|
||||
var flowRate = profile['flowRate'];
|
||||
var z = (layer + 1) * layerHeight;
|
||||
let {
|
||||
speed,
|
||||
flowRate
|
||||
} = profile;
|
||||
|
||||
var lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
|
||||
speed *= 60;
|
||||
const z = (layer + 1) * layerHeight;
|
||||
|
||||
var filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI;
|
||||
const lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
|
||||
|
||||
const filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI;
|
||||
this.extruder += lineLength * nozzleDiameter * layerHeight / filamentSurfaceArea * flowRate;
|
||||
|
||||
this._addGCode({
|
||||
'G': 1,
|
||||
'X': x.toFixed(3), 'Y': y.toFixed(3), 'Z': z.toFixed(3),
|
||||
'F': speed.toFixed(3),
|
||||
'E': this.extruder.toFixed(3)
|
||||
[G_COMMAND]: 1,
|
||||
[POSITION_X]: x.toFixed(3),
|
||||
[POSITION_Y]: y.toFixed(3),
|
||||
[POSITION_Z]: z.toFixed(3),
|
||||
[SPEED]: speed.toFixed(3),
|
||||
[EXTRUDER]: this.extruder.toFixed(3)
|
||||
});
|
||||
|
||||
this._nozzlePosition.copy(newNozzlePosition);
|
||||
@ -113,21 +132,23 @@ export default class {
|
||||
return this;
|
||||
}
|
||||
|
||||
unRetract () {
|
||||
var retractionEnabled = this.settings.config['retractionEnabled'];
|
||||
var retractionMinDistance = this.settings.config['retractionMinDistance'];
|
||||
var retractionSpeed = this.settings.config['retractionSpeed'];
|
||||
unRetract() {
|
||||
const {
|
||||
retractionEnabled,
|
||||
retractionMinDistance,
|
||||
retractionSpeed
|
||||
} = this.settings.config;
|
||||
|
||||
if (this.isRetracted && retractionEnabled) {
|
||||
this.isRetracted = false;
|
||||
|
||||
var speed = retractionSpeed * 60;
|
||||
const speed = retractionSpeed * 60;
|
||||
|
||||
if (this.extruder > retractionMinDistance) {
|
||||
this._addGCode({
|
||||
'G': 0,
|
||||
'E': this.extruder.toFixed(3),
|
||||
'F': speed.toFixed(3)
|
||||
[G_COMMAND]: 0,
|
||||
[EXTRUDER]: this.extruder.toFixed(3),
|
||||
[SPEED]: speed.toFixed(3)
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -135,22 +156,24 @@ export default class {
|
||||
return this;
|
||||
}
|
||||
|
||||
retract () {
|
||||
var retractionAmount = this.settings.config['retractionAmount'];
|
||||
var retractionEnabled = this.settings.config['retractionEnabled'];
|
||||
var retractionMinDistance = this.settings.config['retractionMinDistance'];
|
||||
var retractionSpeed = this.settings.config['retractionSpeed'];
|
||||
retract() {
|
||||
const {
|
||||
retractionAmount,
|
||||
retractionEnabled,
|
||||
retractionMinDistance,
|
||||
retractionSpeed
|
||||
} = this.settings.config;
|
||||
|
||||
if (!this.isRetracted && retractionEnabled) {
|
||||
this.isRetracted = true;
|
||||
|
||||
var speed = retractionSpeed * 60;
|
||||
const speed = retractionSpeed * 60;
|
||||
|
||||
if (this.extruder > retractionMinDistance && retractionEnabled) {
|
||||
this._addGCode({
|
||||
'G': 0,
|
||||
'E': (this.extruder - retractionAmount).toFixed(3),
|
||||
'F': speed.toFixed(3)
|
||||
[G_COMMAND]: 0,
|
||||
[EXTRUDER]: (this.extruder - retractionAmount).toFixed(3),
|
||||
[SPEED]: speed.toFixed(3)
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -158,7 +181,7 @@ export default class {
|
||||
return this;
|
||||
}
|
||||
|
||||
getGCode () {
|
||||
getGCode() {
|
||||
return this.settings.startCode() + this.gcode + this.settings.endCode();
|
||||
}
|
||||
}
|
||||
|
31
src/getFillTemplate.js
Normal file
31
src/getFillTemplate.js
Normal file
@ -0,0 +1,31 @@
|
||||
import Shape from 'Doodle3D/clipper-js';
|
||||
|
||||
export default function getFillTemplate(bounds, size, even, uneven) {
|
||||
const paths = [];
|
||||
|
||||
const left = Math.floor(bounds.left / size) * size;
|
||||
const right = Math.ceil(bounds.right / size) * size;
|
||||
const top = Math.floor(bounds.top / size) * size;
|
||||
const bottom = Math.ceil(bounds.bottom / size) * size;
|
||||
|
||||
const width = right - left;
|
||||
|
||||
if (even) {
|
||||
for (let y = top; y <= bottom + width; y += size) {
|
||||
paths.push([
|
||||
{ x: left, y },
|
||||
{ x: right, y: y - width }
|
||||
]);
|
||||
}
|
||||
}
|
||||
if (uneven) {
|
||||
for (let y = top - width; y <= bottom; y += size) {
|
||||
paths.push([
|
||||
{ x: left, y },
|
||||
{ x: right, y: y + width }
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return new Shape(paths, false, true);
|
||||
}
|
10
src/index.js
10
src/index.js
@ -1,10 +1,6 @@
|
||||
import Slicer from './slicer.js';
|
||||
import SlicerWorker from './slicerworker.js';
|
||||
import Settings from './settings.js';
|
||||
import ClipperLib from 'clipper-lib';
|
||||
import printerSettings from './settings/printer_settings.json!json';
|
||||
import userSettings from './settings/user_settings.json!json';
|
||||
|
||||
ClipperLib.Error = function (message) {
|
||||
console.error(message);
|
||||
};
|
||||
|
||||
export {Slicer, SlicerWorker, Settings};
|
||||
export { Slicer, Settings, printerSettings, userSettings };
|
||||
|
252
src/paths.js
252
src/paths.js
@ -1,252 +0,0 @@
|
||||
import ClipperLib from 'clipper-lib';
|
||||
import THREE from 'three.js';
|
||||
|
||||
export default class Paths extends Array {
|
||||
constructor (paths = [], closed = true) {
|
||||
super();
|
||||
|
||||
this.setPaths(paths);
|
||||
this.closed = closed;
|
||||
}
|
||||
|
||||
setPaths (paths) {
|
||||
for (var i = 0; i < paths.length; i ++) {
|
||||
var path = paths[i];
|
||||
|
||||
if (path.length > 0) {
|
||||
this.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
_clip (path, type) {
|
||||
var solution = new ClipperLib.PolyTree();
|
||||
|
||||
var clipper = new ClipperLib.Clipper();
|
||||
clipper.AddPaths(this, ClipperLib.PolyType.ptSubject, this.closed);
|
||||
clipper.AddPaths(path, ClipperLib.PolyType.ptClip, path.closed);
|
||||
clipper.Execute(type, solution);
|
||||
|
||||
if (this.closed) {
|
||||
var paths = ClipperLib.Clipper.ClosedPathsFromPolyTree(solution);
|
||||
}
|
||||
else {
|
||||
var paths = ClipperLib.Clipper.OpenPathsFromPolyTree(solution);
|
||||
}
|
||||
|
||||
return new Paths(paths, this.closed);
|
||||
}
|
||||
|
||||
union (path) {
|
||||
return this._clip(path, ClipperLib.ClipType.ctUnion);
|
||||
}
|
||||
|
||||
difference (path) {
|
||||
return this._clip(path, ClipperLib.ClipType.ctDifference);
|
||||
}
|
||||
|
||||
intersect (path) {
|
||||
return this._clip(path, ClipperLib.ClipType.ctIntersection);
|
||||
}
|
||||
|
||||
xor (path) {
|
||||
return this._clip(path, ClipperLib.ClipType.ctXor);
|
||||
}
|
||||
|
||||
offset (offset) {
|
||||
var solution = new ClipperLib.Paths();
|
||||
var co = new ClipperLib.ClipperOffset(1, 1);
|
||||
co.AddPaths(this, ClipperLib.JoinType.jtSquare, ClipperLib.EndType.etClosedPolygon);
|
||||
co.Execute(solution, offset);
|
||||
|
||||
return new Paths(solution);
|
||||
}
|
||||
|
||||
scaleUp (factor) {
|
||||
ClipperLib.JS.ScaleUpPaths(this, factor);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
scaleDown (factor) {
|
||||
ClipperLib.JS.ScaleDownPaths(this, factor);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
lastPoint () {
|
||||
if (this.length === 0) {
|
||||
return new THREE.Vector2();
|
||||
}
|
||||
|
||||
var lastPath = this[this.length - 1];
|
||||
var lastPoint = this.closed ? lastPath[0] : lastPath[lastPath.length - 1];
|
||||
return new THREE.Vector2(lastPoint.X, lastPoint.Y);
|
||||
}
|
||||
|
||||
optimizePath (start) {
|
||||
var optimizedPaths = new Paths([], this.closed);
|
||||
var donePaths = [];
|
||||
|
||||
while (optimizedPaths.length !== this.length) {
|
||||
var minLength = false;
|
||||
var reverse;
|
||||
var minPath;
|
||||
var offset;
|
||||
var pathIndex;
|
||||
|
||||
for (var i = 0; i < this.length; i += 1) {
|
||||
var path = this[i];
|
||||
|
||||
if (donePaths.indexOf(i) === -1) {
|
||||
|
||||
if (this.closed) {
|
||||
for (var j = 0; j < path.length; j += 1) {
|
||||
var point = new THREE.Vector2(path[j].X, path[j].Y);
|
||||
var length = point.sub(start).length();
|
||||
if (minLength === false || length < minLength) {
|
||||
minPath = path;
|
||||
minLength = length;
|
||||
offset = j;
|
||||
pathIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
var startPoint = new THREE.Vector2(path[0].X, path[0].Y);
|
||||
var length = startPoint.sub(start).length();
|
||||
if (minLength === false || length < minLength) {
|
||||
minPath = path;
|
||||
minLength = length;
|
||||
reverse = false;
|
||||
pathIndex = i;
|
||||
}
|
||||
var endPoint = new THREE.Vector2(path[path.length - 1].X, path[path.length - 1].Y);
|
||||
var length = endPoint.sub(start).length();
|
||||
if (length < minLength) {
|
||||
minPath = path;
|
||||
minLength = length;
|
||||
reverse = true;
|
||||
pathIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.closed) {
|
||||
minPath = minPath.concat(minPath.splice(0, offset));
|
||||
var point = minPath[0];
|
||||
}
|
||||
else {
|
||||
if (reverse) {
|
||||
minPath.reverse();
|
||||
}
|
||||
var point = minPath[minPath.length - 1];
|
||||
}
|
||||
donePaths.push(pathIndex);
|
||||
start = new THREE.Vector2(point.X, point.Y);
|
||||
|
||||
optimizedPaths.push(minPath);
|
||||
}
|
||||
|
||||
return optimizedPaths;
|
||||
}
|
||||
|
||||
areas () {
|
||||
var areas = [];
|
||||
|
||||
for (var i = 0; i < this.length; i ++) {
|
||||
var shape = this[i];
|
||||
var area = Math.abs(ClipperLib.Clipper.Area(shape));
|
||||
areas.push(area);
|
||||
}
|
||||
|
||||
return areas;
|
||||
}
|
||||
|
||||
area () {
|
||||
var areas = this.areas();
|
||||
var totalArea = 0;
|
||||
|
||||
for (var i = 0; i < areas.length; i ++) {
|
||||
var area = areas[i];
|
||||
totalArea += area;
|
||||
}
|
||||
|
||||
return totalArea;
|
||||
}
|
||||
|
||||
tresholdArea (minArea) {
|
||||
// code not tested yet
|
||||
for (var i = 0; i < this.length; i ++) {
|
||||
var shape = this[i];
|
||||
var area = ClipperLib.Clipper.Area(shape);
|
||||
|
||||
if (area < minArea) {
|
||||
this.splice(i, 1);
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
join (path) {
|
||||
for (var i = 0; i < path.length; i += 1) {
|
||||
this.push(path[i]);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
clone () {
|
||||
return new Paths(ClipperLib.JS.Clone(this), this.closed);
|
||||
}
|
||||
|
||||
bounds () {
|
||||
return ClipperLib.Clipper.GetBounds(this);
|
||||
}
|
||||
|
||||
clean (cleanDelta) {
|
||||
return new Paths(ClipperLib.Clipper.CleanPolygons(this, cleanDelta), this.closed);
|
||||
}
|
||||
|
||||
isHole () {
|
||||
return !ClipperLib.Clipper.Orientation(this[0]);
|
||||
}
|
||||
|
||||
pointCollision (point) {
|
||||
var collision = ClipperLib.Clipper.PointInPolygon(point, this[0]);
|
||||
return ClipperLib.Clipper.PointInPolygon(point, this[0]);
|
||||
}
|
||||
|
||||
boundSize () {
|
||||
var bounds = this.bounds();
|
||||
|
||||
var width = bounds.right - bounds.left;
|
||||
var height = bounds.bottom - bounds.top;
|
||||
|
||||
return width * height;
|
||||
}
|
||||
|
||||
draw (context, color) {
|
||||
context.strokeStyle = color;
|
||||
for (var i = 0; i < this.length; i += 1) {
|
||||
var shape = this[i];
|
||||
|
||||
// var point = shape[0];
|
||||
// context.fillText(i, point.X*2, point.Y*2);
|
||||
|
||||
context.beginPath();
|
||||
for (var j = 0; j < shape.length; j += 1) {
|
||||
var point = shape[j % shape.length];
|
||||
|
||||
context.lineTo(point.X * 2, point.Y * 2);
|
||||
}
|
||||
if (this.closed) {
|
||||
context.closePath();
|
||||
}
|
||||
context.stroke();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,54 +1,49 @@
|
||||
export default class {
|
||||
constructor () {
|
||||
this.config = {};
|
||||
constructor(config = {}) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
updateConfig (config) {
|
||||
for (var i in config) {
|
||||
this.config[i] = config[i];
|
||||
}
|
||||
updateConfig(config) {
|
||||
this.config = { ...this.config, ...config };
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
startCode () {
|
||||
var gcode = this.config["startCode"];
|
||||
|
||||
gcode = this._subsituteVariables(gcode);
|
||||
|
||||
startCode() {
|
||||
const { startCode } = this.config;
|
||||
const gcode = this._subsituteVariables(startCode);
|
||||
return gcode;
|
||||
}
|
||||
|
||||
endCode () {
|
||||
var gcode = this.config["endCode"];
|
||||
|
||||
gcode = this._subsituteVariables(gcode);
|
||||
|
||||
endCode() {
|
||||
const { endCode } = this.config;
|
||||
const gcode = this._subsituteVariables(endCode);
|
||||
return gcode;
|
||||
}
|
||||
_subsituteVariables(gcode) {
|
||||
let {
|
||||
temperature,
|
||||
bedTemperature,
|
||||
heatTemperature,
|
||||
heatBedTemperature,
|
||||
travelSpeed,
|
||||
printerType,
|
||||
heatedbed
|
||||
} = this.config;
|
||||
|
||||
_subsituteVariables (gcode) {
|
||||
var temperature = this.config["temperature"];
|
||||
var bedTemperature = this.config["bedTemperature"];
|
||||
var preheatTemperature = this.config["heatupTemperature"];
|
||||
var preheatBedTemperature = this.config["heatupBedTemperature"];
|
||||
var travelSpeed = this.config["travelSpeed"] * 60;
|
||||
var printerType = this.config["type"];
|
||||
var heatedbed = this.config["heatedbed"];
|
||||
travelSpeed *= 60;
|
||||
|
||||
switch (printerType) {
|
||||
case "makerbot_replicator2": printerType = "r2"; break;
|
||||
case "makerbot_replicator2x": printerType = "r2x"; break;
|
||||
case "makerbot_thingomatic": printerType = "t6"; break;
|
||||
case "makerbot_generic": printerType = "r2"; break;
|
||||
case "_3Dison_plus": printerType = "r2"; break;
|
||||
case 'makerbot_replicator2': printerType = 'r2'; break;
|
||||
case 'makerbot_replicator2x': printerType = 'r2x'; break;
|
||||
case 'makerbot_thingomatic': printerType = 't6'; break;
|
||||
case 'makerbot_generic': printerType = 'r2'; break;
|
||||
case '_3Dison_plus': printerType = 'r2'; break;
|
||||
}
|
||||
var heatedBedReplacement = heatedbed ? "" : ";";
|
||||
|
||||
const heatedBedReplacement = heatedbed ? '' : ';';
|
||||
|
||||
gcode = gcode.replace(/{printingTemp}/gi, temperature);
|
||||
gcode = gcode.replace(/{printingBedTemp}/gi, bedTemperature);
|
||||
gcode = gcode.replace(/{preheatTemp}/gi, preheatTemperature);
|
||||
gcode = gcode.replace(/{preheatBedTemp}/gi, preheatBedTemperature);
|
||||
gcode = gcode.replace(/{preheatTemp}/gi, heatTemperature);
|
||||
gcode = gcode.replace(/{preheatBedTemp}/gi, heatBedTemperature);
|
||||
gcode = gcode.replace(/{printerType}/gi, printerType);
|
||||
gcode = gcode.replace(/{travelSpeed}/gi, travelSpeed);
|
||||
gcode = gcode.replace(/{if heatedBed}/gi, heatedBedReplacement);
|
||||
|
131
src/slice.js
131
src/slice.js
@ -1,134 +1,31 @@
|
||||
import Paths from './paths.js';
|
||||
import Shape from 'Doodle3D/clipper-js';
|
||||
|
||||
export default class {
|
||||
constructor () {
|
||||
constructor() {
|
||||
this.parts = [];
|
||||
}
|
||||
getOutline() {
|
||||
const outLines = new Shape([], true);
|
||||
|
||||
removeSelfIntersect () {
|
||||
for (var i = 0; i < this.parts.length; i ++) {
|
||||
var part1 = this.parts[i].intersect;
|
||||
for (let i = 0; i < this.parts.length; i ++) {
|
||||
const part = this.parts[i];
|
||||
|
||||
if (!part1.closed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var j = i + 1; j < this.parts.length; j ++) {
|
||||
var part2 = this.parts[j].intersect;
|
||||
|
||||
if (!part2.closed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (part2.intersect(part1).length > 0) {
|
||||
part1 = this.parts[i].intersect = part1.union(part2);
|
||||
|
||||
this.parts.splice(j, 1);
|
||||
j --;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
optimizePaths (start) {
|
||||
if (this.brim !== undefined && this.brim.length > 0) {
|
||||
this.brim = this.brim.optimizePath(start);
|
||||
start = this.brim.lastPoint();
|
||||
}
|
||||
|
||||
var parts = [];
|
||||
|
||||
while (this.parts.length > 0) {
|
||||
|
||||
var closestDistance = Infinity;
|
||||
var closestPart;
|
||||
|
||||
for (var i = 0; i < this.parts.length; i ++) {
|
||||
var part = this.parts[i];
|
||||
if (part.intersect.closed) {
|
||||
var bounds = part.outerLine.bounds();
|
||||
}
|
||||
else {
|
||||
var bounds = part.intersect.bounds();
|
||||
}
|
||||
|
||||
var top = bounds.top - start.y;
|
||||
var bottom = start.y - bounds.bottom;
|
||||
var left = bounds.left - start.x;
|
||||
var right = start.x - bounds.right;
|
||||
|
||||
var distance = Math.max(top, bottom, left, right);
|
||||
|
||||
if (distance < closestDistance) {
|
||||
closestDistance = distance;
|
||||
closestPart = i;
|
||||
}
|
||||
}
|
||||
|
||||
var part = this.parts.splice(closestPart, 1)[0];
|
||||
parts.push(part);
|
||||
|
||||
if (part.intersect.closed) {
|
||||
if (part.outerLine.length > 0) {
|
||||
part.outerLine = part.outerLine.optimizePath(start);
|
||||
start = part.outerLine.lastPoint();
|
||||
}
|
||||
|
||||
for (var j = 0; j < part.innerLines.length; j ++) {
|
||||
var innerLine = part.innerLines[j];
|
||||
if (innerLine.length > 0) {
|
||||
part.innerLines[j] = innerLine.optimizePath(start);
|
||||
start = part.innerLines[j].lastPoint();
|
||||
}
|
||||
}
|
||||
|
||||
if (part.fill.length > 0) {
|
||||
part.fill = part.fill.optimizePath(start);
|
||||
start = part.fill.lastPoint();
|
||||
}
|
||||
}
|
||||
else {
|
||||
part.intersect.optimizePath(start);
|
||||
start = part.intersect.lastPoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.parts = parts;
|
||||
|
||||
if (this.support !== undefined && this.support.length > 0) {
|
||||
this.support = this.support.optimizePath(start);
|
||||
start = this.support.lastPoint();
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
getOutline () {
|
||||
var outLines = new Paths([], true);
|
||||
|
||||
for (var i = 0; i < this.parts.length; i ++) {
|
||||
var part = this.parts[i];
|
||||
|
||||
if (part.intersect.closed) {
|
||||
if (part.shape.closed) {
|
||||
outLines.join(this.parts[i].outerLine);
|
||||
}
|
||||
}
|
||||
|
||||
return outLines;
|
||||
}
|
||||
add(shape) {
|
||||
const part = { shape };
|
||||
|
||||
add (intersect) {
|
||||
var parts = {
|
||||
intersect
|
||||
};
|
||||
|
||||
if (intersect.closed) {
|
||||
parts.innerLines = [];
|
||||
parts.outerLine = new Paths([], true);
|
||||
parts.fill = new Paths([], false);
|
||||
if (shape.closed) {
|
||||
part.innerLines = [];
|
||||
part.outerLine = new Shape([], true);
|
||||
part.fill = new Shape([], false);
|
||||
}
|
||||
|
||||
this.parts.push(parts);
|
||||
this.parts.push(part);
|
||||
}
|
||||
}
|
19
src/sliceActions/addBrim.js
Normal file
19
src/sliceActions/addBrim.js
Normal file
@ -0,0 +1,19 @@
|
||||
import THREE from 'three.js';
|
||||
import { PRECISION } from '../constants.js';
|
||||
|
||||
const offsetOptions = {
|
||||
jointType: 'jtSquare',
|
||||
endType: 'etClosedPolygon',
|
||||
miterLimit: 2.0,
|
||||
roundPrecision: 0.25
|
||||
};
|
||||
|
||||
export default function addBrim(slices, settings) {
|
||||
console.log('add brim');
|
||||
|
||||
let { brimOffset } = settings.config;
|
||||
brimOffset /= PRECISION;
|
||||
|
||||
const fistLayer = slices[0];
|
||||
fistLayer.brim = fistLayer.getOutline().offset(brimOffset, offsetOptions);
|
||||
}
|
22
src/sliceActions/applyPrecision.js
Normal file
22
src/sliceActions/applyPrecision.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { PRECISION } from '../constants.js'
|
||||
|
||||
export default function applyPrecision(shapes) {
|
||||
for (let i = 0; i < shapes.length; i ++) {
|
||||
const { closedShapes, openShapes } = shapes[i];
|
||||
|
||||
scaleUpShape(closedShapes);
|
||||
scaleUpShape(openShapes);
|
||||
}
|
||||
}
|
||||
|
||||
function scaleUpShape(shape) {
|
||||
for (let i = 0; i < shape.length; i ++) {
|
||||
const path = shape[i];
|
||||
|
||||
for (let i = 0; i < path.length; i ++) {
|
||||
const point = path[i];
|
||||
|
||||
point.copy(point.divideScalar(PRECISION));
|
||||
}
|
||||
}
|
||||
}
|
47
src/sliceActions/calculateLayersIntersections.js
Normal file
47
src/sliceActions/calculateLayersIntersections.js
Normal file
@ -0,0 +1,47 @@
|
||||
import THREE from 'three.js';
|
||||
|
||||
export default function calculateLayersIntersections(lines, settings) {
|
||||
console.log('calculating layer intersections');
|
||||
|
||||
const { layerHeight, dimensionsZ } = settings.config;
|
||||
|
||||
const numLayers = Math.floor(dimensionsZ / layerHeight);
|
||||
|
||||
const layerIntersectionIndexes = [];
|
||||
const layerIntersectionPoints = [];
|
||||
for (let layer = 0; layer < numLayers; layer ++) {
|
||||
layerIntersectionIndexes[layer] = [];
|
||||
layerIntersectionPoints[layer] = [];
|
||||
}
|
||||
|
||||
for (let lineIndex = 0; lineIndex < lines.length; lineIndex ++) {
|
||||
const line = lines[lineIndex].line;
|
||||
|
||||
const min = Math.ceil(Math.min(line.start.y, line.end.y) / layerHeight);
|
||||
const max = Math.floor(Math.max(line.start.y, line.end.y) / layerHeight);
|
||||
|
||||
for (let layerIndex = min; layerIndex <= max; layerIndex ++) {
|
||||
if (layerIndex >= 0 && layerIndex < numLayers) {
|
||||
|
||||
layerIntersectionIndexes[layerIndex].push(lineIndex);
|
||||
|
||||
const y = layerIndex * layerHeight;
|
||||
let x, z;
|
||||
|
||||
if (line.start.y === line.end.y) {
|
||||
x = line.start.x;
|
||||
z = line.start.z;
|
||||
}
|
||||
else {
|
||||
const alpha = (y - line.start.y) / (line.end.y - line.start.y);
|
||||
x = line.end.x * alpha + line.start.x * (1 - alpha);
|
||||
z = line.end.z * alpha + line.start.z * (1 - alpha);
|
||||
}
|
||||
|
||||
layerIntersectionPoints[layerIndex][lineIndex] = new THREE.Vector2(z, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { layerIntersectionIndexes, layerIntersectionPoints };
|
||||
}
|
49
src/sliceActions/createLines.js
Normal file
49
src/sliceActions/createLines.js
Normal file
@ -0,0 +1,49 @@
|
||||
import THREE from 'three.js';
|
||||
|
||||
function addLine(geometry, lineLookup, lines, a, b) {
|
||||
const index = lines.length;
|
||||
lineLookup[`${a}_${b}`] = index;
|
||||
|
||||
lines.push({
|
||||
line: new THREE.Line3(geometry.vertices[a], geometry.vertices[b]),
|
||||
connects: [],
|
||||
normals: []
|
||||
});
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
export default function createLines(geometry, settings) {
|
||||
console.log('constructing unique lines from geometry');
|
||||
|
||||
const lines = [];
|
||||
const lineLookup = {};
|
||||
|
||||
for (let i = 0; i < geometry.faces.length; i ++) {
|
||||
const face = geometry.faces[i];
|
||||
if (face.normal.y !== 1 && face.normal.y !== -1) {
|
||||
const normal = new THREE.Vector2(face.normal.z, face.normal.x).normalize();
|
||||
|
||||
const lookupA = lineLookup[`${face.b}_${face.a}`];
|
||||
const lookupB = lineLookup[`${face.c}_${face.b}`];
|
||||
const lookupC = lineLookup[`${face.a}_${face.c}`];
|
||||
|
||||
// only add unique lines
|
||||
// returns index of said line
|
||||
const indexA = lookupA !== undefined ? lookupA : addLine(geometry, lineLookup, lines, face.a, face.b);
|
||||
const indexB = lookupB !== undefined ? lookupB : addLine(geometry, lineLookup, lines, face.b, face.c);
|
||||
const indexC = lookupC !== undefined ? lookupC : addLine(geometry, lineLookup, lines, face.c, face.a);
|
||||
|
||||
// set connecting lines (based on face)
|
||||
lines[indexA].connects.push(indexB, indexC);
|
||||
lines[indexB].connects.push(indexC, indexA);
|
||||
lines[indexC].connects.push(indexA, indexB);
|
||||
|
||||
lines[indexA].normals.push(normal);
|
||||
lines[indexB].normals.push(normal);
|
||||
lines[indexC].normals.push(normal);
|
||||
}
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
81
src/sliceActions/generateInfills.js
Normal file
81
src/sliceActions/generateInfills.js
Normal file
@ -0,0 +1,81 @@
|
||||
import { PRECISION } from '../constants.js'
|
||||
import getFillTemplate from '../getFillTemplate.js';
|
||||
import Shape from 'Doodle3D/clipper-js';
|
||||
|
||||
export default function generateInfills(slices, settings) {
|
||||
console.log('generating infills');
|
||||
|
||||
let {
|
||||
layerHeight,
|
||||
fillGridSize,
|
||||
bottomThickness,
|
||||
topThickness,
|
||||
nozzleDiameter,
|
||||
infillOverlap
|
||||
} = settings.config;
|
||||
|
||||
fillGridSize /= PRECISION;
|
||||
nozzleDiameter /= PRECISION;
|
||||
infillOverlap /= PRECISION;
|
||||
|
||||
const bottomSkinCount = Math.ceil(bottomThickness/layerHeight);
|
||||
const topSkinCount = Math.ceil(topThickness/layerHeight);
|
||||
const nozzleRadius = nozzleDiameter / 2;
|
||||
const hightemplateSize = Math.sqrt(2 * Math.pow(nozzleDiameter, 2));
|
||||
|
||||
for (let layer = 0; layer < slices.length; layer ++) {
|
||||
const slice = slices[layer];
|
||||
|
||||
let surroundingLayer;
|
||||
if (layer - bottomSkinCount >= 0 && layer + topSkinCount < slices.length) {
|
||||
const downSkin = slices[layer - bottomSkinCount].getOutline();
|
||||
const upSkin = slices[layer + topSkinCount].getOutline();
|
||||
surroundingLayer = upSkin.intersect(downSkin);
|
||||
}
|
||||
|
||||
for (let i = 0; i < slice.parts.length; i ++) {
|
||||
const part = slice.parts[i];
|
||||
|
||||
if (!part.shape.closed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const outerLine = part.outerLine;
|
||||
|
||||
if (outerLine.paths.length > 0) {
|
||||
const inset = (part.innerLines.length > 0) ? part.innerLines[part.innerLines.length - 1] : outerLine;
|
||||
|
||||
const fillArea = inset.offset(-nozzleRadius);
|
||||
let lowFillArea;
|
||||
let highFillArea;
|
||||
if (surroundingLayer) {
|
||||
highFillArea = fillArea.difference(surroundingLayer);
|
||||
|
||||
if (infillOverlap > 0) {
|
||||
highFillArea = highFillArea.offset(infillOverlap);
|
||||
}
|
||||
|
||||
highFillArea = highFillArea.intersect(fillArea);
|
||||
lowFillArea = fillArea.difference(highFillArea);
|
||||
} else {
|
||||
highFillArea = fillArea;
|
||||
}
|
||||
|
||||
if (lowFillArea && lowFillArea.paths.length > 0) {
|
||||
const bounds = lowFillArea.shapeBounds();
|
||||
const lowFillTemplate = getFillTemplate(bounds, fillGridSize, true, true);
|
||||
|
||||
part.fill.join(lowFillTemplate.intersect(lowFillArea));
|
||||
}
|
||||
|
||||
if (highFillArea.paths.length > 0) {
|
||||
const bounds = highFillArea.shapeBounds();
|
||||
const even = (layer % 2 === 0);
|
||||
const highFillTemplate = getFillTemplate(bounds, hightemplateSize, even, !even);
|
||||
|
||||
part.fill.join(highFillTemplate.intersect(highFillArea));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
48
src/sliceActions/generateInnerLines.js
Normal file
48
src/sliceActions/generateInnerLines.js
Normal file
@ -0,0 +1,48 @@
|
||||
import { PRECISION } from '../constants.js'
|
||||
|
||||
const offsetOptions = {
|
||||
jointType: 'jtSquare',
|
||||
endType: 'etClosedPolygon',
|
||||
miterLimit: 2.0,
|
||||
roundPrecision: 0.25
|
||||
};
|
||||
|
||||
export default function generateInnerLines(slices, settings) {
|
||||
console.log('generating outer lines and inner lines');
|
||||
|
||||
// need to scale up everything because of clipper rounding errors
|
||||
let { layerHeight, nozzleDiameter, shellThickness } = settings.config;
|
||||
nozzleDiameter /= PRECISION;
|
||||
shellThickness /= PRECISION;
|
||||
const nozzleRadius = nozzleDiameter / 2;
|
||||
const shells = Math.round(shellThickness / nozzleDiameter);
|
||||
|
||||
for (let layer = 0; layer < slices.length; layer ++) {
|
||||
const slice = slices[layer];
|
||||
|
||||
for (let i = 0; i < slice.parts.length; i ++) {
|
||||
const part = slice.parts[i];
|
||||
|
||||
if (!part.shape.closed) continue;
|
||||
|
||||
const outerLine = part.shape.offset(-nozzleRadius, offsetOptions);
|
||||
|
||||
if (outerLine.paths.length > 0) {
|
||||
part.outerLine.join(outerLine);
|
||||
|
||||
for (let shell = 1; shell < shells; shell += 1) {
|
||||
const offset = shell * nozzleDiameter;
|
||||
|
||||
const innerLine = outerLine.offset(-offset, offsetOptions);
|
||||
|
||||
if (innerLine.paths.length > 0) {
|
||||
part.innerLines.push(innerLine);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
77
src/sliceActions/generateSupport.js
Normal file
77
src/sliceActions/generateSupport.js
Normal file
@ -0,0 +1,77 @@
|
||||
import getFillTemplate from '../getFillTemplate.js';
|
||||
import Shape from 'Doodle3D/clipper-js';
|
||||
import { PRECISION } from '../constants.js';
|
||||
|
||||
export default function generateSupport(slices, settings) {
|
||||
console.log('generating support');
|
||||
|
||||
if (!settings.config.supportEnabled) return;
|
||||
|
||||
let {
|
||||
layerHeight,
|
||||
supportGridSize,
|
||||
supportAcceptanceMargin,
|
||||
supportPlateSize: plateSize,
|
||||
supportDistanceY,
|
||||
nozzleDiameter
|
||||
} = settings.config;
|
||||
|
||||
supportGridSize /= PRECISION;
|
||||
supportMargin /= PRECISION;
|
||||
plateSize /= PRECISION;
|
||||
nozzleDiameter /= PRECISION;
|
||||
var supportDistanceLayers = Math.max(Math.ceil(supportDistanceY / layerHeight), 1);
|
||||
|
||||
var supportAreas = new Shape([], true);
|
||||
|
||||
for (var layer = slices.length - 1 - supportDistanceLayers; layer >= 0; layer --) {
|
||||
var currentSlice = slices[layer];
|
||||
|
||||
if (supportAreas.length > 0) {
|
||||
|
||||
if (layer >= supportDistanceLayers) {
|
||||
var sliceSkin = slices[layer - supportDistanceLayers].getOutline();
|
||||
sliceSkin = sliceSkin;
|
||||
|
||||
var supportAreasSlimmed = supportAreas.difference(sliceSkin.offset(supportMargin));
|
||||
if (supportAreasSlimmed.area() < 100.0) {
|
||||
supportAreas = supportAreas.difference(sliceSkin);
|
||||
}
|
||||
else {
|
||||
supportAreas = supportAreasSlimmed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
var supportSkin = slices[layer + supportDistanceLayers - 1].getOutline();
|
||||
|
||||
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(supportAcceptanceMargin);
|
||||
}
|
||||
|
||||
var overlap = supportSkin.offset(supportAcceptanceMargin).intersect(outerLine);
|
||||
var overhang = outerLine.difference(overlap);
|
||||
|
||||
if (overlap.length === 0 || overhang.length > 0) {
|
||||
supportAreas = supportAreas.join(overhang);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
121
src/sliceActions/intersectionsToShapes.js
Normal file
121
src/sliceActions/intersectionsToShapes.js
Normal file
@ -0,0 +1,121 @@
|
||||
import THREE from 'three.js';
|
||||
import Shape from 'Doodle3D/clipper-js';
|
||||
|
||||
export default function intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings) {
|
||||
console.log('generating slices');
|
||||
|
||||
const layers = [];
|
||||
|
||||
for (let layer = 1; layer < layerIntersectionIndexes.length; layer ++) {
|
||||
const intersectionIndexes = layerIntersectionIndexes[layer];
|
||||
const intersectionPoints = layerIntersectionPoints[layer];
|
||||
|
||||
if (intersectionIndexes.length === 0) continue;
|
||||
|
||||
const closedShapes = [];
|
||||
const openShapes = [];
|
||||
for (let i = 0; i < intersectionIndexes.length; i ++) {
|
||||
let index = intersectionIndexes[i];
|
||||
|
||||
if (intersectionPoints[index] === undefined) continue;
|
||||
|
||||
const shape = [];
|
||||
|
||||
const firstPoints = [index];
|
||||
let isFirstPoint = true;
|
||||
let closed = false;
|
||||
|
||||
while (index !== -1) {
|
||||
const intersection = intersectionPoints[index];
|
||||
// uppercase X and Y because clipper vector
|
||||
shape.push(intersection);
|
||||
|
||||
delete intersectionPoints[index];
|
||||
|
||||
const connects = lines[index].connects;
|
||||
const faceNormals = lines[index].normals;
|
||||
|
||||
for (let i = 0; i < connects.length; i ++) {
|
||||
index = connects[i];
|
||||
|
||||
if (firstPoints.indexOf(index) !== -1 && shape.length > 2) {
|
||||
closed = true;
|
||||
index = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if index has an intersection or is already used
|
||||
if (intersectionPoints[index] !== undefined) {
|
||||
const faceNormal = faceNormals[Math.floor(i / 2)];
|
||||
|
||||
const a = new THREE.Vector2(intersection.x, intersection.y);
|
||||
const b = new THREE.Vector2(intersectionPoints[index].x, intersectionPoints[index].y);
|
||||
|
||||
// can't calculate normal between points if distance is smaller as 0.0001
|
||||
if ((faceNormal.x === 0 && faceNormal.y === 0) || a.distanceTo(b) < 0.0001) {
|
||||
if (isFirstPoint) {
|
||||
firstPoints.push(index);
|
||||
}
|
||||
|
||||
delete intersectionPoints[index];
|
||||
|
||||
connects.push(...lines[index].connects);
|
||||
faceNormals.push(...lines[index].normals);
|
||||
index = -1;
|
||||
} else {
|
||||
// make sure the path goes the right direction
|
||||
// THREE.Vector2.normal is not yet implimented
|
||||
// const normal = a.sub(b).normal().normalize();
|
||||
const normal = a.sub(b);
|
||||
normal.set(-normal.y, normal.x).normalize();
|
||||
|
||||
if (normal.dot(faceNormal) > 0) {
|
||||
break;
|
||||
} else {
|
||||
index = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
index = -1;
|
||||
}
|
||||
}
|
||||
isFirstPoint = false;
|
||||
}
|
||||
|
||||
if (!closed) {
|
||||
index = firstPoints[0];
|
||||
|
||||
while (index !== -1) {
|
||||
if (firstPoints.indexOf(index) === -1) {
|
||||
const intersection = intersectionPoints[index];
|
||||
shape.unshift(intersection);
|
||||
|
||||
delete intersectionPoints[index];
|
||||
}
|
||||
|
||||
const connects = lines[index].connects;
|
||||
|
||||
for (let i = 0; i < connects.length; i ++) {
|
||||
index = connects[i];
|
||||
|
||||
if (intersectionPoints[index] !== undefined) {
|
||||
break;
|
||||
} else {
|
||||
index = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closed) {
|
||||
closedShapes.push(shape);
|
||||
} else {
|
||||
openShapes.push(shape);
|
||||
}
|
||||
}
|
||||
|
||||
layers.push({ closedShapes, openShapes });
|
||||
}
|
||||
|
||||
return layers;
|
||||
}
|
152
src/sliceActions/optimizePaths.js
Normal file
152
src/sliceActions/optimizePaths.js
Normal file
@ -0,0 +1,152 @@
|
||||
import THREE from 'three.js';
|
||||
import Shape from 'Doodle3D/clipper-js';
|
||||
|
||||
export default function optimizePaths(slices, settings) {
|
||||
console.log('optimize paths');
|
||||
|
||||
const start = new THREE.Vector2(0, 0);
|
||||
|
||||
for (let layer = 0; layer < slices.length; layer ++) {
|
||||
const slice = slices[layer];
|
||||
|
||||
if (slice.brim !== undefined && slice.brim.paths.length > 0) {
|
||||
slice.brim = optimizeShape(slice.brim, start);
|
||||
start.copy(slice.brim.lastPoint());
|
||||
}
|
||||
|
||||
const parts = [];
|
||||
|
||||
while (slice.parts.length > 0) {
|
||||
let closestDistance = Infinity;
|
||||
let closestPart;
|
||||
|
||||
for (let i = 0; i < slice.parts.length; i ++) {
|
||||
const part = slice.parts[i];
|
||||
|
||||
let bounds;
|
||||
if (part.shape.closed) {
|
||||
bounds = part.outerLine.shapeBounds();
|
||||
} else {
|
||||
bounds = part.shape.shapeBounds();
|
||||
}
|
||||
|
||||
const top = bounds.top - start.y;
|
||||
const bottom = start.y - bounds.bottom;
|
||||
const left = bounds.left - start.x;
|
||||
const right = start.x - bounds.right;
|
||||
|
||||
const distance = Math.max(top, bottom, left, right);
|
||||
|
||||
if (distance < closestDistance) {
|
||||
closestDistance = distance;
|
||||
closestPart = i;
|
||||
}
|
||||
}
|
||||
|
||||
const part = slice.parts.splice(closestPart, 1)[0];
|
||||
parts.push(part);
|
||||
|
||||
if (part.shape.closed) {
|
||||
if (part.outerLine.paths.length > 0) {
|
||||
part.outerLine = optimizeShape(part.outerLine, start);
|
||||
start.copy(part.outerLine.lastPoint());
|
||||
}
|
||||
|
||||
for (let i = 0; i < part.innerLines.length; i ++) {
|
||||
const innerLine = part.innerLines[i];
|
||||
|
||||
if (innerLine.paths.length > 0) {
|
||||
part.innerLines[i] = optimizeShape(innerLine, start);
|
||||
start.copy(part.innerLines[i].lastPoint());
|
||||
}
|
||||
}
|
||||
|
||||
if (part.fill.paths.length > 0) {
|
||||
part.fill = optimizeShape(part.fill, start);
|
||||
start.copy(part.fill.lastPoint());
|
||||
}
|
||||
} else {
|
||||
part.shape = optimizeShape(part.shape, start);
|
||||
start.copy(part.shape.lastPoint());
|
||||
}
|
||||
}
|
||||
|
||||
slice.parts = parts;
|
||||
|
||||
if (slice.support !== undefined && slice.support.length > 0) {
|
||||
slice.support = optimizeShape(slice.support, start);
|
||||
start.copy(slice.support.lastPoint());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function optimizeShape(shape, start) {
|
||||
start = start.clone();
|
||||
|
||||
const inputPaths = shape.mapToLower();
|
||||
const optimizedPaths = [];
|
||||
const donePaths = [];
|
||||
|
||||
while (optimizedPaths.length !== inputPaths.length) {
|
||||
let minLength = false;
|
||||
let reverse;
|
||||
let minPath;
|
||||
let offset;
|
||||
let pathIndex;
|
||||
|
||||
for (let i = 0; i < inputPaths.length; i ++) {
|
||||
if (donePaths.indexOf(i) !== -1) continue;
|
||||
|
||||
const path = inputPaths[i];
|
||||
|
||||
if (shape.closed) {
|
||||
for (let j = 0; j < path.length; j += 1) {
|
||||
const point = new THREE.Vector2().copy(path[j]);
|
||||
const length = point.sub(start).length();
|
||||
if (minLength === false || length < minLength) {
|
||||
minPath = path;
|
||||
minLength = length;
|
||||
offset = j;
|
||||
pathIndex = i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const startPoint = new THREE.Vector2().copy(path[0]);
|
||||
const lengthToStart = startPoint.sub(start).length();
|
||||
if (minLength === false || lengthToStart < minLength) {
|
||||
minPath = path;
|
||||
minLength = lengthToStart;
|
||||
reverse = false;
|
||||
pathIndex = i;
|
||||
}
|
||||
|
||||
const endPoint = new THREE.Vector2().copy(path[path.length - 1]);
|
||||
const lengthToEnd = endPoint.sub(start).length();
|
||||
if (lengthToEnd < minLength) {
|
||||
minPath = path;
|
||||
minLength = lengthToEnd;
|
||||
reverse = true;
|
||||
pathIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let point;
|
||||
if (shape.closed) {
|
||||
minPath = minPath.concat(minPath.splice(0, offset));
|
||||
point = minPath[0];
|
||||
} else {
|
||||
if (reverse) {
|
||||
minPath.reverse();
|
||||
}
|
||||
point = minPath[minPath.length - 1];
|
||||
}
|
||||
|
||||
donePaths.push(pathIndex);
|
||||
start.copy(point);
|
||||
|
||||
optimizedPaths.push(minPath);
|
||||
}
|
||||
|
||||
return new Shape(optimizedPaths, shape.closed, true);
|
||||
}
|
32
src/sliceActions/removePrecision.js
Normal file
32
src/sliceActions/removePrecision.js
Normal file
@ -0,0 +1,32 @@
|
||||
import THREE from 'three.js';
|
||||
import { PRECISION } from '../constants.js';
|
||||
|
||||
export default function removePrecision(slices) {
|
||||
console.log('remove precision');
|
||||
|
||||
const start = new THREE.Vector2(0, 0);
|
||||
|
||||
for (let layer = 0; layer < slices.length; layer ++) {
|
||||
const slice = slices[layer];
|
||||
|
||||
for (let i = 0; i < slice.parts.length; i ++) {
|
||||
const part = slice.parts[i];
|
||||
|
||||
if (part.shape.closed) {
|
||||
part.outerLine.scaleDown(1 / PRECISION);
|
||||
for (let i = 0; i < part.innerLines.length; i ++) {
|
||||
const innerLine = part.innerLines[i];
|
||||
innerLine.scaleDown(1 / PRECISION);
|
||||
}
|
||||
part.fill.scaleDown(1 / PRECISION);
|
||||
}
|
||||
}
|
||||
|
||||
if (slice.support !== undefined) {
|
||||
slice.support.scaleDown(1 / PRECISION);
|
||||
}
|
||||
if (slice.brim !== undefined) {
|
||||
slice.brim.scaleDown(1 / PRECISION);
|
||||
}
|
||||
}
|
||||
}
|
40
src/sliceActions/shapesToSlices.js
Normal file
40
src/sliceActions/shapesToSlices.js
Normal file
@ -0,0 +1,40 @@
|
||||
import Shape from 'Doodle3D/clipper-js';
|
||||
import Slice from '../slice.js';
|
||||
|
||||
import { CLEAN_DELTA } from '../constants.js';
|
||||
|
||||
export default function shapesToSlices(shapes, settings) {
|
||||
const sliceLayers = [];
|
||||
|
||||
for (let layer = 0; layer < shapes.length; layer ++) {
|
||||
let { closedShapes, openShapes } = shapes[layer];
|
||||
|
||||
closedShapes = new Shape(closedShapes, true, true)
|
||||
.clean(CLEAN_DELTA)
|
||||
.fixOrientation()
|
||||
.removeOverlap()
|
||||
.seperateShapes();
|
||||
|
||||
openShapes = new Shape(openShapes, false, true)
|
||||
.clean(CLEAN_DELTA);
|
||||
|
||||
const slice = new Slice();
|
||||
|
||||
for (let i = 0; i < closedShapes.length; i ++) {
|
||||
const closedShape = closedShapes[i];
|
||||
slice.add(closedShape);
|
||||
|
||||
// if (openShapes.path.length > 0) {
|
||||
// openShapes = openShapes.difference(closedShape);
|
||||
// }
|
||||
}
|
||||
|
||||
if (openShapes.paths.length > 0) {
|
||||
slice.add(openShapes);
|
||||
}
|
||||
|
||||
sliceLayers.push(slice);
|
||||
}
|
||||
|
||||
return sliceLayers;
|
||||
}
|
71
src/sliceActions/slicesToGCode.js
Normal file
71
src/sliceActions/slicesToGCode.js
Normal file
@ -0,0 +1,71 @@
|
||||
import GCode from '../gcode.js';
|
||||
|
||||
export default function slicesToGCode(slices, settings) {
|
||||
console.log('slices to gcode');
|
||||
|
||||
const gcode = new GCode(settings);
|
||||
|
||||
for (let layer = 0; layer < slices.length; layer ++) {
|
||||
const slice = slices[layer];
|
||||
|
||||
if (layer === 1) {
|
||||
gcode.turnFanOn();
|
||||
gcode.bottom = false;
|
||||
}
|
||||
|
||||
if (slice.brim !== undefined) {
|
||||
pathToGCode(gcode, slice.brim, true, true, layer, 'brim');
|
||||
}
|
||||
|
||||
for (let i = 0; i < slice.parts.length; i ++) {
|
||||
const part = slice.parts[i];
|
||||
|
||||
if (part.shape.closed) {
|
||||
pathToGCode(gcode, part.outerLine, false, true, layer, 'outerLine');
|
||||
|
||||
for (let i = 0; i < part.innerLines.length; i ++) {
|
||||
const innerLine = part.innerLines[i];
|
||||
pathToGCode(gcode, innerLine, false, false, layer, 'innerLine');
|
||||
}
|
||||
|
||||
pathToGCode(gcode, part.fill, true, false, layer, 'fill');
|
||||
} else {
|
||||
const retract = !(slice.parts.length === 1 && slice.support === undefined);
|
||||
pathToGCode(gcode, part.shape, retract, retract, layer, 'outerLine');
|
||||
}
|
||||
}
|
||||
|
||||
if (slice.support !== undefined) {
|
||||
pathToGCode(gcode, slice.support, true, true, layer, 'support');
|
||||
}
|
||||
}
|
||||
|
||||
return gcode.getGCode();
|
||||
}
|
||||
|
||||
function pathToGCode(gcode, shape, retract, unRetract, layer, type) {
|
||||
for (let i = 0; i < shape.paths.length; i ++) {
|
||||
const line = shape.paths[i];
|
||||
|
||||
const length = shape.closed ? (line.length + 1) : line.length;
|
||||
for (let i = 0; i < length; i ++) {
|
||||
const point = line[i % line.length];
|
||||
|
||||
if (i === 0) {
|
||||
// TODO
|
||||
// moveTo should impliment combing
|
||||
gcode.moveTo(point.X, point.Y, layer);
|
||||
|
||||
if (unRetract) {
|
||||
gcode.unRetract();
|
||||
}
|
||||
} else {
|
||||
gcode.lineTo(point.X, point.Y, layer, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (retract) {
|
||||
gcode.retract();
|
||||
}
|
||||
}
|
760
src/slicer.js
760
src/slicer.js
@ -1,41 +1,33 @@
|
||||
import THREE from 'three.js';
|
||||
import Paths from './paths.js';
|
||||
import Slice from './slice.js';
|
||||
import GCode from './gcode.js';
|
||||
import EventDispatcher from 'casperlamboo/EventDispatcher';
|
||||
import calculateLayersIntersections from './sliceActions/calculateLayersIntersections.js';
|
||||
import createLines from './sliceActions/createLines.js';
|
||||
import generateInfills from './sliceActions/generateInfills.js';
|
||||
import generateInnerLines from './sliceActions/generateInnerLines.js';
|
||||
import generateSupport from './sliceActions/generateSupport.js';
|
||||
import intersectionsToShapes from './sliceActions/intersectionsToShapes.js';
|
||||
import addBrim from './sliceActions/addBrim.js';
|
||||
import optimizePaths from './sliceActions/optimizePaths.js';
|
||||
import shapesToSlices from './sliceActions/shapesToSlices.js';
|
||||
import slicesToGCode from './sliceActions/slicesToGCode.js';
|
||||
import applyPrecision from './sliceActions/applyPrecision.js';
|
||||
import removePrecision from './sliceActions/removePrecision.js';
|
||||
|
||||
export default class {
|
||||
constructor () {
|
||||
this.progress = {
|
||||
createdLines: false,
|
||||
calculatedLayerIntersections: false,
|
||||
sliced: false,
|
||||
generatedSlices: false,
|
||||
generatedInnerLines: false,
|
||||
generatedInfills: false,
|
||||
generatedSupport: false,
|
||||
optimizedPaths: false,
|
||||
generatedGCode: false
|
||||
};
|
||||
}
|
||||
|
||||
setMesh (mesh) {
|
||||
export default class extends EventDispatcher {
|
||||
setMesh(mesh) {
|
||||
mesh.updateMatrix();
|
||||
|
||||
this.setGeometry(mesh.geometry, mesh.matrix);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
setGeometry (geometry, matrix) {
|
||||
if (geometry.type === 'BufferGeometry') {
|
||||
setGeometry(geometry, matrix) {
|
||||
if (geometry instanceof THREE.BufferGeometry) {
|
||||
geometry = new THREE.Geometry().fromBufferGeometry(geometry);
|
||||
}
|
||||
else if (geometry.type.endsWith('Geometry')) {
|
||||
} else if (geometry instanceof THREE.Geometry) {
|
||||
geometry = geometry.clone();
|
||||
}
|
||||
else {
|
||||
console.warn('Geometry is not an instance of BufferGeometry or Geometry');
|
||||
return;
|
||||
} else {
|
||||
throw 'Geometry is not an instance of BufferGeometry or Geometry';
|
||||
}
|
||||
|
||||
if (matrix instanceof THREE.Matrix4) {
|
||||
@ -49,711 +41,31 @@ export default class {
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
slice (settings) {
|
||||
var supportEnabled = settings.config['supportEnabled'];
|
||||
|
||||
slice(settings) {
|
||||
// get unique lines from geometry;
|
||||
var lines = this._createLines(settings);
|
||||
const lines = createLines(this.geometry, settings);
|
||||
|
||||
var {layerIntersectionIndexes, layerIntersectionPoints} = this._calculateLayersIntersections(lines, settings);
|
||||
|
||||
var shapes = this._intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings);
|
||||
|
||||
var slices = this._shapesToSlices(shapes, settings);
|
||||
|
||||
this._generateInnerLines(slices, settings);
|
||||
|
||||
this._generateInfills(slices, settings);
|
||||
|
||||
if (supportEnabled) {
|
||||
this._generateSupport(slices, settings);
|
||||
}
|
||||
|
||||
this._optimizePaths(slices, settings);
|
||||
|
||||
var gcode = this._slicesToGCode(slices, settings);
|
||||
|
||||
if (this.onfinish !== undefined) {
|
||||
this.onfinish(gcode);
|
||||
}
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
_createLines (settings) {
|
||||
console.log('constructing unique lines from geometry');
|
||||
|
||||
var lines = [];
|
||||
var lineLookup = {};
|
||||
|
||||
var addLine = (a, b) => {
|
||||
var index = lineLookup[b + '_' + a];
|
||||
|
||||
if (index === undefined) {
|
||||
index = lines.length;
|
||||
lineLookup[a + '_' + b] = index;
|
||||
|
||||
lines.push({
|
||||
line: new THREE.Line3(this.geometry.vertices[a], this.geometry.vertices[b]),
|
||||
connects: [],
|
||||
normals: []
|
||||
});
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.geometry.faces.length; i ++) {
|
||||
var face = this.geometry.faces[i];
|
||||
if (face.normal.y !== 1 && face.normal.y !== -1) {
|
||||
var normal = new THREE.Vector2(face.normal.z, face.normal.x).normalize();
|
||||
|
||||
// check for only adding unique lines
|
||||
// returns index of said line
|
||||
var a = addLine(face.a, face.b);
|
||||
var b = addLine(face.b, face.c);
|
||||
var c = addLine(face.c, face.a);
|
||||
|
||||
// set connecting lines (based on face)
|
||||
lines[a].connects.push(b, c);
|
||||
lines[b].connects.push(c, a);
|
||||
lines[c].connects.push(a, b);
|
||||
|
||||
lines[a].normals.push(normal);
|
||||
lines[b].normals.push(normal);
|
||||
lines[c].normals.push(normal);
|
||||
}
|
||||
}
|
||||
|
||||
this.progress.createdLines = true;
|
||||
this._updateProgress(settings);
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
_calculateLayersIntersections (lines, settings) {
|
||||
console.log('calculating layer intersections');
|
||||
|
||||
var layerHeight = settings.config["layerHeight"];
|
||||
var height = settings.config["dimensionsZ"];
|
||||
|
||||
var numLayers = Math.floor(height / layerHeight);
|
||||
|
||||
var layerIntersectionIndexes = [];
|
||||
var layerIntersectionPoints = [];
|
||||
for (var layer = 0; layer < numLayers; layer ++) {
|
||||
layerIntersectionIndexes[layer] = [];
|
||||
layerIntersectionPoints[layer] = [];
|
||||
}
|
||||
|
||||
for (var lineIndex = 0; lineIndex < lines.length; lineIndex ++) {
|
||||
var line = lines[lineIndex].line;
|
||||
|
||||
var min = Math.ceil(Math.min(line.start.y, line.end.y) / layerHeight);
|
||||
var max = Math.floor(Math.max(line.start.y, line.end.y) / layerHeight);
|
||||
|
||||
for (var layerIndex = min; layerIndex <= max; layerIndex ++) {
|
||||
if (layerIndex >= 0 && layerIndex < numLayers) {
|
||||
|
||||
layerIntersectionIndexes[layerIndex].push(lineIndex);
|
||||
|
||||
var y = layerIndex * layerHeight;
|
||||
|
||||
if (line.start.y === line.end.y) {
|
||||
var x = line.start.x;
|
||||
var z = line.start.z;
|
||||
}
|
||||
else {
|
||||
var alpha = (y - line.start.y) / (line.end.y - line.start.y);
|
||||
var x = line.end.x * alpha + line.start.x * (1 - alpha);
|
||||
var z = line.end.z * alpha + line.start.z * (1 - alpha);
|
||||
}
|
||||
|
||||
layerIntersectionPoints[layerIndex][lineIndex] = new THREE.Vector2(z, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.progress.calculatedLayerIntersections = true;
|
||||
this._updateProgress(settings);
|
||||
|
||||
return {
|
||||
const {
|
||||
layerIntersectionIndexes,
|
||||
layerIntersectionPoints
|
||||
};
|
||||
}
|
||||
} = calculateLayersIntersections(lines, settings);
|
||||
|
||||
_intersectionsToShapes (layerIntersectionIndexes, layerIntersectionPoints, lines, settings) {
|
||||
console.log("generating slices");
|
||||
const shapes = intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings);
|
||||
|
||||
var shapes = [];
|
||||
applyPrecision(shapes);
|
||||
|
||||
for (var layer = 1; layer < layerIntersectionIndexes.length; layer ++) {
|
||||
var intersectionIndexes = layerIntersectionIndexes[layer];
|
||||
var intersectionPoints = layerIntersectionPoints[layer];
|
||||
const slices = shapesToSlices(shapes, settings);
|
||||
|
||||
if (intersectionIndexes.length === 0) {
|
||||
continue;
|
||||
}
|
||||
generateInnerLines(slices, settings);
|
||||
generateInfills(slices, settings);
|
||||
generateSupport(slices, settings);
|
||||
addBrim(slices, settings);
|
||||
optimizePaths(slices, settings);
|
||||
removePrecision(slices);
|
||||
|
||||
var shapeParts = [];
|
||||
for (var i = 0; i < intersectionIndexes.length; i ++) {
|
||||
var index = intersectionIndexes[i];
|
||||
const gcode = slicesToGCode(slices, settings);
|
||||
|
||||
if (intersectionPoints[index] === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var firstPoints = [index];
|
||||
var isFirstPoint = true;
|
||||
var closed = false;
|
||||
|
||||
var shape = [];
|
||||
|
||||
while (index !== -1) {
|
||||
var intersection = intersectionPoints[index];
|
||||
// uppercase X and Y because clipper vector
|
||||
shape.push({X: intersection.x, Y: intersection.y});
|
||||
|
||||
delete intersectionPoints[index];
|
||||
|
||||
var connects = lines[index].connects;
|
||||
var faceNormals = lines[index].normals;
|
||||
|
||||
for (var j = 0; j < connects.length; j ++) {
|
||||
var index = connects[j];
|
||||
|
||||
if (firstPoints.indexOf(index) !== -1 && shape.length > 2) {
|
||||
closed = true;
|
||||
index = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if index has an intersection or is already used
|
||||
if (intersectionPoints[index] !== undefined) {
|
||||
var faceNormal = faceNormals[Math.floor(j / 2)];
|
||||
|
||||
var a = new THREE.Vector2(intersection.x, intersection.y);
|
||||
var b = new THREE.Vector2(intersectionPoints[index].x, intersectionPoints[index].y);
|
||||
|
||||
// can't calculate normal between points if distance is smaller as 0.0001
|
||||
if ((faceNormal.x === 0 && faceNormal.y === 0) || a.distanceTo(b) < 0.0001) {
|
||||
if (isFirstPoint) {
|
||||
firstPoints.push(index);
|
||||
}
|
||||
|
||||
delete intersectionPoints[index];
|
||||
|
||||
connects = connects.concat(lines[index].connects);
|
||||
faceNormals = faceNormals.concat(lines[index].normals);
|
||||
index = -1;
|
||||
}
|
||||
else {
|
||||
// make sure the path goes the right direction
|
||||
// THREE.Vector2.normal is not yet implimented
|
||||
// var normal = a.sub(b).normal().normalize();
|
||||
var normal = a.sub(b);
|
||||
normal.set(-normal.y, normal.x).normalize();
|
||||
|
||||
if (normal.dot(faceNormal) > 0) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
index = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
index = -1;
|
||||
}
|
||||
}
|
||||
isFirstPoint = false;
|
||||
}
|
||||
|
||||
if (!closed) {
|
||||
var index = firstPoints[0];
|
||||
|
||||
while (index !== -1) {
|
||||
if (firstPoints.indexOf(index) === -1) {
|
||||
var intersection = intersectionPoints[index];
|
||||
shape.unshift({X: intersection.x, Y: intersection.y});
|
||||
|
||||
delete intersectionPoints[index];
|
||||
}
|
||||
|
||||
var connects = lines[index].connects;
|
||||
|
||||
for (var i = 0; i < connects.length; i ++) {
|
||||
var index = connects[i];
|
||||
|
||||
if (intersectionPoints[index] !== undefined) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
index = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var part = new Paths([shape], closed).clean(0.01);
|
||||
if (part.length > 0) {
|
||||
shapeParts.push(part);
|
||||
}
|
||||
}
|
||||
|
||||
shapes.push(shapeParts);
|
||||
}
|
||||
|
||||
this.progress.sliced = true;
|
||||
this._updateProgress(settings);
|
||||
|
||||
return shapes;
|
||||
}
|
||||
|
||||
_shapesToSlices (shapes, settings) {
|
||||
var slices = [];
|
||||
|
||||
for (var layer = 0; layer < shapes.length; layer ++) {
|
||||
var shapeParts = shapes[layer];
|
||||
|
||||
var slice = new Slice();
|
||||
|
||||
var holes = [];
|
||||
var outlines = [];
|
||||
|
||||
for (var i = 0; i < shapeParts.length; i ++) {
|
||||
var shape = shapeParts[i];
|
||||
|
||||
if (!shape.closed) {
|
||||
slice.add(shape);
|
||||
}
|
||||
else if (shape.isHole()) {
|
||||
holes.push(shape);
|
||||
}
|
||||
else {
|
||||
slice.add(shape);
|
||||
outlines.push(shape);
|
||||
}
|
||||
}
|
||||
|
||||
outlines.sort((a, b) => {
|
||||
return a.boundSize() - b.boundSize();
|
||||
});
|
||||
|
||||
console.log('test');
|
||||
|
||||
if (holes.length > outlines.length) {
|
||||
[holes, outlines] = [outlines, holes];
|
||||
}
|
||||
else if (holes.length === outlines.length) {
|
||||
holes.sort((a, b) => {
|
||||
return a.boundSize() - b.boundSize();
|
||||
});
|
||||
|
||||
if (holes[0].boundSize > outlines[0].boundSize()) {
|
||||
[holes, outlines] = [outlines, holes];
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < holes.length; i ++) {
|
||||
var hole = holes[i];
|
||||
|
||||
for (var j = 0; j < outlines.length; j ++) {
|
||||
var outline = outlines[j];
|
||||
|
||||
if (outline.pointCollision(hole[0][0])) {
|
||||
outline.join(hole);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slice.removeSelfIntersect();
|
||||
|
||||
slices.push(slice);
|
||||
}
|
||||
|
||||
this.progress.generatedSlices = true;
|
||||
this._updateProgress(settings);
|
||||
|
||||
return slices;
|
||||
}
|
||||
|
||||
_generateInnerLines (slices, settings) {
|
||||
console.log("generating outer lines and inner lines");
|
||||
|
||||
// need to scale up everything because of clipper rounding errors
|
||||
var scale = 100;
|
||||
|
||||
var layerHeight = settings.config["layerHeight"];
|
||||
var nozzleDiameter = settings.config["nozzleDiameter"] * scale;
|
||||
var shellThickness = settings.config["shellThickness"] * scale;
|
||||
var nozzleRadius = nozzleDiameter / 2;
|
||||
var shells = Math.round(shellThickness / nozzleDiameter);
|
||||
|
||||
for (var layer = 0; layer < slices.length; layer ++) {
|
||||
var slice = slices[layer];
|
||||
|
||||
for (var i = 0; i < slice.parts.length; i ++) {
|
||||
var part = slice.parts[i];
|
||||
|
||||
if (!part.intersect.closed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// var outerLine = part.intersect.clone().scaleUp(scale).offset(-nozzleRadius);
|
||||
var outerLine = part.intersect.scaleUp(scale).offset(-nozzleRadius);
|
||||
|
||||
if (outerLine.length > 0) {
|
||||
part.outerLine = outerLine;
|
||||
|
||||
for (var shell = 1; shell < shells; shell += 1) {
|
||||
var offset = shell * nozzleDiameter;
|
||||
|
||||
var innerLine = outerLine.offset(-offset);
|
||||
|
||||
if (innerLine.length > 0) {
|
||||
part.innerLines.push(innerLine);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.progress.generatedInnerLines = true;
|
||||
this._updateProgress(settings);
|
||||
}
|
||||
|
||||
_generateInfills (slices, settings) {
|
||||
console.log("generating infills");
|
||||
|
||||
// need to scale up everything because of clipper rounding errors
|
||||
var scale = 100;
|
||||
|
||||
var layerHeight = settings.config["layerHeight"];
|
||||
var fillGridSize = settings.config["fillGridSize"] * scale;
|
||||
var bottomThickness = settings.config["bottomThickness"];
|
||||
var topThickness = settings.config["topThickness"];
|
||||
var nozzleDiameter = settings.config["nozzleDiameter"] * scale;
|
||||
var infillOverlap = settings.config["infillOverlap"] * scale;
|
||||
|
||||
var bottomSkinCount = Math.ceil(bottomThickness/layerHeight);
|
||||
var topSkinCount = Math.ceil(topThickness/layerHeight);
|
||||
var nozzleRadius = nozzleDiameter / 2;
|
||||
var hightemplateSize = Math.sqrt(2 * Math.pow(nozzleDiameter, 2));
|
||||
|
||||
for (var layer = 0; layer < slices.length; layer ++) {
|
||||
var slice = slices[layer];
|
||||
|
||||
if (layer - bottomSkinCount >= 0 && layer + topSkinCount < slices.length) {
|
||||
var downSkin = slices[layer - bottomSkinCount].getOutline();
|
||||
var upSkin = slices[layer + topSkinCount].getOutline();
|
||||
var surroundingLayer = upSkin.intersect(downSkin);
|
||||
}
|
||||
else {
|
||||
var surroundingLayer = false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < slice.parts.length; i ++) {
|
||||
var part = slice.parts[i];
|
||||
|
||||
if (!part.intersect.closed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var outerLine = part.outerLine;
|
||||
|
||||
if (outerLine.length > 0) {
|
||||
var inset = (part.innerLines.length > 0) ? part.innerLines[part.innerLines.length - 1] : outerLine;
|
||||
|
||||
var fillArea = inset.offset(-nozzleRadius);
|
||||
var lowFillArea = false;
|
||||
if (surroundingLayer) {
|
||||
var highFillArea = fillArea.difference(surroundingLayer);
|
||||
|
||||
if (infillOverlap > 0) {
|
||||
highFillArea = highFillArea.offset(infillOverlap);
|
||||
}
|
||||
|
||||
highFillArea = highFillArea.intersect(fillArea);
|
||||
|
||||
var lowFillArea = fillArea.difference(highFillArea);
|
||||
}
|
||||
else {
|
||||
var highFillArea = fillArea;
|
||||
}
|
||||
|
||||
var fill = new Paths([], false);
|
||||
|
||||
if (lowFillArea && lowFillArea.length > 0) {
|
||||
var bounds = lowFillArea.bounds();
|
||||
var lowFillTemplate = this._getFillTemplate(bounds, fillGridSize, true, true);
|
||||
|
||||
part.fill.join(lowFillTemplate.intersect(lowFillArea));
|
||||
}
|
||||
|
||||
if (highFillArea.length > 0) {
|
||||
var bounds = highFillArea.bounds();
|
||||
var even = (layer % 2 === 0);
|
||||
var highFillTemplate = this._getFillTemplate(bounds, hightemplateSize, even, !even);
|
||||
|
||||
part.fill.join(highFillTemplate.intersect(highFillArea));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.progress.generatedInfills = true;
|
||||
this._updateProgress(settings);
|
||||
}
|
||||
|
||||
_generateSupport (slices, settings) {
|
||||
console.log("generating support");
|
||||
|
||||
// need to scale up everything because of clipper rounding errors
|
||||
var scale = 100;
|
||||
|
||||
var layerHeight = settings.config["layerHeight"];
|
||||
var supportGridSize = settings.config["supportGridSize"] * scale;
|
||||
var supportAcceptanceMargin = settings.config["supportAcceptanceMargin"] * scale;
|
||||
var supportMargin = settings.config["supportMargin"] * scale;
|
||||
var plateSize = settings.config["supportPlateSize"] * scale;
|
||||
var supportDistanceY = settings.config["supportDistanceY"];
|
||||
var supportDistanceLayers = Math.max(Math.ceil(supportDistanceY / layerHeight), 1);
|
||||
var nozzleDiameter = settings.config["nozzleDiameter"] * scale;
|
||||
|
||||
var supportAreas = new Paths([], true);
|
||||
|
||||
for (var layer = slices.length - 1 - supportDistanceLayers; layer >= 0; layer --) {
|
||||
var currentSlice = slices[layer];
|
||||
|
||||
if (supportAreas.length > 0) {
|
||||
|
||||
if (layer >= supportDistanceLayers) {
|
||||
var sliceSkin = slices[layer - supportDistanceLayers].getOutline();
|
||||
sliceSkin = sliceSkin;
|
||||
|
||||
var supportAreasSlimmed = supportAreas.difference(sliceSkin.offset(supportMargin));
|
||||
if (supportAreasSlimmed.area() < 100.0) {
|
||||
supportAreas = supportAreas.difference(sliceSkin);
|
||||
}
|
||||
else {
|
||||
supportAreas = supportAreasSlimmed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var supportTemplate = this._getFillTemplate(supportAreas.bounds(), supportGridSize, true, true);
|
||||
var supportFill = supportTemplate.intersect(supportAreas);
|
||||
if (supportFill.length === 0) {
|
||||
currentSlice.support = supportAreas.clone();
|
||||
}
|
||||
else {
|
||||
currentSlice.support = supportFill;
|
||||
}
|
||||
}
|
||||
|
||||
var supportSkin = slices[layer + supportDistanceLayers - 1].getOutline();
|
||||
|
||||
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(supportAcceptanceMargin);
|
||||
}
|
||||
|
||||
var overlap = supportSkin.offset(supportAcceptanceMargin).intersect(outerLine);
|
||||
var overhang = outerLine.difference(overlap);
|
||||
|
||||
if (overlap.length === 0 || overhang.length > 0) {
|
||||
supportAreas = supportAreas.join(overhang);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.progress.generatedSupport = true;
|
||||
this._updateProgress(settings);
|
||||
}
|
||||
|
||||
_optimizePaths (slices, settings) {
|
||||
console.log("opimize paths");
|
||||
|
||||
// need to scale up everything because of clipper rounding errors
|
||||
var scale = 100;
|
||||
|
||||
var brimOffset = settings.config["brimOffset"] * scale;
|
||||
|
||||
var start = new THREE.Vector2(0, 0);
|
||||
|
||||
for (var layer = 0; layer < slices.length; layer ++) {
|
||||
var slice = slices[layer];
|
||||
|
||||
if (layer === 0) {
|
||||
slice.brim = slice.getOutline().offset(brimOffset);
|
||||
}
|
||||
|
||||
start = slice.optimizePaths(start);
|
||||
|
||||
for (var i = 0; i < slice.parts.length; i ++) {
|
||||
var part = slice.parts[i];
|
||||
|
||||
if (part.intersect.closed) {
|
||||
part.outerLine.scaleDown(scale);
|
||||
for (var j = 0; j < part.innerLines.length; j ++) {
|
||||
var innerLine = part.innerLines[j];
|
||||
innerLine.scaleDown(scale);
|
||||
}
|
||||
part.fill.scaleDown(scale);
|
||||
}
|
||||
}
|
||||
|
||||
if (slice.support !== undefined) {
|
||||
slice.support.scaleDown(scale);
|
||||
}
|
||||
if (slice.brim !== undefined) {
|
||||
slice.brim.scaleDown(scale);
|
||||
}
|
||||
}
|
||||
|
||||
this.progress.optimizedPaths = true;
|
||||
this._updateProgress(settings);
|
||||
}
|
||||
|
||||
_getFillTemplate (bounds, size, even, uneven) {
|
||||
var paths = new Paths([], false);
|
||||
|
||||
var left = Math.floor(bounds.left / size) * size;
|
||||
var right = Math.ceil(bounds.right / size) * size;
|
||||
var top = Math.floor(bounds.top / size) * size;
|
||||
var bottom = Math.ceil(bounds.bottom / size) * size;
|
||||
|
||||
var width = right - left;
|
||||
|
||||
if (even) {
|
||||
for (var y = top; y <= bottom + width; y += size) {
|
||||
paths.push([
|
||||
{X: left, Y: y},
|
||||
{X: right, Y: y - width}
|
||||
]);
|
||||
}
|
||||
}
|
||||
if (uneven) {
|
||||
for (var y = top - width; y <= bottom; y += size) {
|
||||
paths.push([
|
||||
{X: left, Y: y},
|
||||
{X: right, Y: y + width}
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
_slicesToGCode (slices, settings) {
|
||||
var gcode = new GCode().setSettings(settings);
|
||||
|
||||
function pathToGCode (path, retract, unRetract, type) {
|
||||
|
||||
for (var i = 0; i < path.length; i ++) {
|
||||
var shape = path[i];
|
||||
|
||||
var length = path.closed ? (shape.length + 1) : shape.length;
|
||||
|
||||
for (var j = 0; j < length; j ++) {
|
||||
var point = shape[j % shape.length];
|
||||
|
||||
if (j === 0) {
|
||||
// TODO
|
||||
// moveTo should impliment combing
|
||||
gcode.moveTo(point.X, point.Y, layer);
|
||||
|
||||
if (unRetract) {
|
||||
gcode.unRetract();
|
||||
}
|
||||
}
|
||||
else {
|
||||
gcode.lineTo(point.X, point.Y, layer, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (retract) {
|
||||
gcode.retract();
|
||||
}
|
||||
}
|
||||
|
||||
for (var layer = 0; layer < slices.length; layer ++) {
|
||||
var slice = slices[layer];
|
||||
|
||||
if (layer === 1) {
|
||||
gcode.turnFanOn();
|
||||
gcode.bottom = false;
|
||||
}
|
||||
|
||||
if (slice.brim !== undefined) {
|
||||
pathToGCode(slice.brim, true, true, "brim");
|
||||
}
|
||||
|
||||
for (var i = 0; i < slice.parts.length; i ++) {
|
||||
var part = slice.parts[i];
|
||||
|
||||
if (part.intersect.closed) {
|
||||
pathToGCode(part.outerLine, false, true, "outerLine");
|
||||
|
||||
for (var j = 0; j < part.innerLines.length; j ++) {
|
||||
var innerLine = part.innerLines[j];
|
||||
pathToGCode(innerLine, false, false, "innerLine");
|
||||
}
|
||||
|
||||
pathToGCode(part.fill, true, false, "fill");
|
||||
}
|
||||
else {
|
||||
var retract = !(slice.parts.length === 1 && slice.support === undefined);
|
||||
pathToGCode(part.intersect, retract, retract, "outerLine");
|
||||
}
|
||||
}
|
||||
|
||||
if (slice.support !== undefined) {
|
||||
pathToGCode(slice.support, true, true, "support");
|
||||
}
|
||||
}
|
||||
|
||||
this.progress.generatedGCode = true;
|
||||
this._updateProgress(settings);
|
||||
|
||||
return gcode.getGCode();
|
||||
}
|
||||
|
||||
_updateProgress (settings) {
|
||||
if (this.onprogress !== undefined) {
|
||||
var supportEnabled = settings.config["supportEnabled"];
|
||||
|
||||
var progress = {};
|
||||
|
||||
var procent = 0;
|
||||
var length = 0;
|
||||
for (var i in this.progress) {
|
||||
if (!(!supportEnabled && i === "generatedSupport")) {
|
||||
progress[i] = this.progress[i];
|
||||
if (progress[i]) {
|
||||
procent += 1;
|
||||
}
|
||||
length += 1;
|
||||
}
|
||||
}
|
||||
|
||||
progress.procent = procent / length;
|
||||
|
||||
this.onprogress(progress);
|
||||
}
|
||||
this.dispatchEvent({ type: 'finish', gcode });
|
||||
return gcode;
|
||||
}
|
||||
}
|
||||
|
@ -1,98 +0,0 @@
|
||||
import THREE from 'three.js';
|
||||
import Settings from './settings.js';
|
||||
|
||||
export default class {
|
||||
constructor () {
|
||||
this.worker = new Worker('./worker.js');
|
||||
|
||||
this.worker.addEventListener('message', (event) => {
|
||||
switch (event.data['cmd']) {
|
||||
case 'PROGRESS':
|
||||
|
||||
if (this.onprogress !== undefined) {
|
||||
var progress = event.data['progress'];
|
||||
|
||||
this.onprogress(progress);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'GCODE':
|
||||
if (this.onfinish !== undefined) {
|
||||
var reader = new FileReader();
|
||||
reader.addEventListener("loadend", () => {
|
||||
var gcode = reader.result;
|
||||
this.onfinish(gcode);
|
||||
});
|
||||
reader.readAsBinaryString(event.data['gcode']);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}, false);
|
||||
|
||||
this.worker.onerror = function (error) {
|
||||
console.warn(error);
|
||||
};
|
||||
}
|
||||
|
||||
setMesh (mesh) {
|
||||
mesh.updateMatrix();
|
||||
|
||||
this.setGeometry(mesh.geometry, mesh.matrix);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
setGeometry (geometry, matrix) {
|
||||
if (geometry.type === 'Geometry') {
|
||||
geometry = new THREE.BufferGeometry().fromGeometry(geometry);
|
||||
}
|
||||
else if (geometry.type === 'BufferGeometry') {
|
||||
geometry = geometry.clone();
|
||||
}
|
||||
else {
|
||||
console.warn('Geometry is not an instance of BufferGeometry or Geometry');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(matrix instanceof THREE.Matrix4)) {
|
||||
matrix = new THREE.Matrix4();
|
||||
}
|
||||
|
||||
var buffers = [];
|
||||
for (var i = 0; i < geometry.attributesKeys.length; i ++) {
|
||||
var key = geometry.attributesKeys[i];
|
||||
buffers.push(geometry.attributes[key].array.buffer);
|
||||
}
|
||||
|
||||
delete geometry.boundingBox;
|
||||
delete geometry.boundingSphere;
|
||||
|
||||
this.worker.postMessage({
|
||||
'cmd': 'SET_MESH',
|
||||
'geometry': {
|
||||
'attributes': geometry.attributes,
|
||||
'attributesKeys': geometry.attributesKeys
|
||||
},
|
||||
'matrix': matrix.toArray()
|
||||
}, buffers);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
slice (settings) {
|
||||
this.worker.postMessage({
|
||||
'cmd': 'SLICE',
|
||||
'settings': settings.config
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
close () {
|
||||
this.worker.postMessage({
|
||||
'cmd': 'CLOSE'
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
importScripts('../jspm_packages/system.js');
|
||||
importScripts('../config.js');
|
||||
|
||||
var Slicer, Settings, THREE;
|
||||
|
||||
function init () {
|
||||
var slicer = new Slicer();
|
||||
slicer.onProgress = function (progress) {
|
||||
self.postMessage({
|
||||
'cmd': 'PROGRESS',
|
||||
'progress': progress
|
||||
});
|
||||
};
|
||||
|
||||
self.addEventListener('message', function (event) {
|
||||
switch (event.data['cmd']) {
|
||||
case 'SET_MESH':
|
||||
var geometry = new THREE.Geometry().fromBufferGeometry(event.data['geometry']);
|
||||
var matrix = new THREE.Matrix4().fromArray(event.data['matrix']);
|
||||
|
||||
slicer.setGeometry(geometry, matrix);
|
||||
break;
|
||||
|
||||
case 'SLICE':
|
||||
var settings = new Settings().updateConfig(event.data['settings']);
|
||||
|
||||
var gcode = slicer.slice(settings);
|
||||
var blob = new Blob([gcode], {type: 'text/plain'});
|
||||
|
||||
self.postMessage({
|
||||
'cmd': 'GCODE',
|
||||
'gcode': blob
|
||||
});
|
||||
|
||||
//self.close();
|
||||
break;
|
||||
|
||||
case 'CLOSE':
|
||||
self.close();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
System.import('./slicer'),
|
||||
System.import('./settings'),
|
||||
System.import('three.js')
|
||||
]).then(function(modules) {
|
||||
Slicer = modules[0].default;
|
||||
Settings = modules[1].default;
|
||||
THREE = modules[2];
|
||||
|
||||
init();
|
||||
});
|
Loading…
Reference in New Issue
Block a user