Merge branch 'develop'

This commit is contained in:
casperlamboo 2017-08-25 15:36:52 +02:00
commit 24a100fbb1
14 changed files with 331 additions and 51 deletions

View 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();
}

View File

@ -1,5 +1,7 @@
import * as THREE from 'three';
import { defaultSettings, sliceGeometry } from 'doodle3d-slicer';
import fileURL from '!url-loader!./models/combingtest.json';
import fileSaver from 'file-saver';
const settings = {
...defaultSettings.base,
@ -8,14 +10,20 @@ const settings = {
...defaultSettings.quality.high
};
const geometry = new THREE.TorusGeometry(20, 10, 30, 30).clone();
geometry.mergeVertices();
const jsonLoader = new THREE.JSONLoader();
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 percentage = `${(done / total * 100).toFixed()}%`
document.write(`<p>${action}, ${percentage}</p>`);
};
const onProgress = ({ progress: { done, total, action } }) => {
const percentage = `${(done / total * 100).toFixed()}%`
document.write(`<p>${action}, ${percentage}</p>`);
};
sliceGeometry(settings, geometry, null, false, onProgress).then(gcode => {
document.body.innerHTML = gcode.replace(/(?:\r\n|\r|\n)/g, '<br />');
const { filament, duration, gcode } = sliceGeometry(settings, geometry, null, true, onProgress);
// 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);
});

View 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]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -886,8 +886,7 @@
"big.js": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz",
"integrity": "sha1-TK2iGTZS6zyp7I5VyQFWacmAaXg=",
"dev": true
"integrity": "sha1-TK2iGTZS6zyp7I5VyQFWacmAaXg="
},
"binary-extensions": {
"version": "1.8.0",
@ -1589,8 +1588,7 @@
"emojis-list": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
"dev": true
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k="
},
"encodeurl": {
"version": "1.0.1",
@ -1897,6 +1895,11 @@
"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": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
@ -3508,8 +3511,7 @@
"json5": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"dev": true
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="
},
"jsonify": {
"version": "0.0.0",
@ -3564,7 +3566,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
"integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
"dev": true,
"requires": {
"big.js": "3.1.3",
"emojis-list": "2.1.0",
@ -3721,8 +3722,7 @@
"mime": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz",
"integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=",
"dev": true
"integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM="
},
"mime-db": {
"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": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz",

View File

@ -11,7 +11,9 @@
"license": "ISC",
"dependencies": {
"babel-polyfill": "^6.23.0",
"three": "^0.83.0"
"file-saver": "^1.3.3",
"three": "^0.83.0",
"url-loader": "^0.5.9"
},
"devDependencies": {
"babel-core": "^6.25.0",

View File

@ -8,6 +8,7 @@ filamentThickness: 2.85
temperature: 210
bedTemperature: 70
layerHeight: 0.15
combing: true
thickness:
top: 1.2
bottom: 1.2

View File

@ -1,4 +1,5 @@
import * as THREE from 'three';
import { PRECISION } from '../../constants.js';
const MOVE = 'G';
const M_COMMAND = 'M';
@ -17,6 +18,7 @@ export default class {
this._currentValues = {};
this._nozzlePosition = new THREE.Vector2(0, 0);
this._extruder = 0.0;
this._duration = 0.0;
this._isRetracted = false;
this._isFanOn = false;
}
@ -62,35 +64,37 @@ export default class {
}
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({
[MOVE]: 0,
[POSITION_X]: x.toFixed(3),
[POSITION_Y]: y.toFixed(3),
[POSITION_X]: newNozzlePosition.x.toFixed(3),
[POSITION_Y]: newNozzlePosition.y.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;
}
lineTo(x, y, z, { speed, flowRate }) {
const newNozzlePosition = new THREE.Vector2(x, y);
speed *= 60;
const newNozzlePosition = new THREE.Vector2(x, y).multiplyScalar(PRECISION);
const lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
this._extruder += this._nozzleToFilamentRatio * lineLength * flowRate;
this._duration += lineLength / speed;
this._addGCode({
[MOVE]: 1,
[POSITION_X]: x.toFixed(3),
[POSITION_Y]: y.toFixed(3),
[POSITION_X]: newNozzlePosition.x.toFixed(3),
[POSITION_Y]: newNozzlePosition.y.toFixed(3),
[POSITION_Z]: z.toFixed(3),
[SPEED]: speed.toFixed(3),
[SPEED]: (speed * 60).toFixed(3),
[EXTRUDER]: this._extruder.toFixed(3)
});
@ -99,17 +103,17 @@ export default class {
return this;
}
unRetract({ enabled, speed, minDistance }) {
unRetract({ enabled, speed, minDistance, amount }) {
if (this._isRetracted && enabled) {
this._isRetracted = false;
speed *= 60;
if (this._extruder > minDistance) {
this._duration += amount / speed;
this._addGCode({
[MOVE]: 0,
[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) {
this._isRetracted = true;
speed *= 60;
if (this._extruder > minDistance) {
this._duration += amount / speed;
this._addGCode({
[MOVE]: 0,
[EXTRUDER]: (this._extruder - amount).toFixed(3),
[SPEED]: speed.toFixed(3)
[SPEED]: (speed * 60).toFixed(3)
});
}
}
@ -136,6 +140,10 @@ export default class {
}
getGCode() {
return this._gcode;
return {
gcode: this._gcode,
duration: this._duration,
filament: this._extruder
};
}
}

View 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
};
}

View 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;
}
}

View File

@ -11,7 +11,7 @@ import shapesToSlices from './shapesToSlices.js';
import slicesToGCode from './slicesToGCode.js';
import detectOpenClosed from './detectOpenClosed.js';
import applyPrecision from './applyPrecision.js';
import removePrecision from './removePrecision.js';
// import removePrecision from './removePrecision.js';
export default function(settings, geometry, onProgress) {
const totalStages = 12;
@ -65,7 +65,7 @@ export default function(settings, geometry, onProgress) {
updateProgress('Optimizing paths');
optimizePaths(slices, settings);
removePrecision(slices);
// removePrecision(slices);
updateProgress('Constructing gcode');
const gcode = slicesToGCode(slices, settings);

View File

@ -1,4 +1,6 @@
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'];
@ -9,7 +11,8 @@ export default function slicesToGCode(slices, settings) {
nozzleDiameter,
travelSpeed,
retraction,
travel
travel,
combing
} = settings;
const filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI;
@ -42,11 +45,12 @@ export default function slicesToGCode(slices, settings) {
}, {});
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 ++) {
const part = slice.parts[i];
const outline = part.shell[0];
if (part.closed) {
for (let i = 0; i < part.shell.length; i ++) {
@ -55,26 +59,26 @@ export default function slicesToGCode(slices, settings) {
const unRetract = isOuterShell;
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(gcode, part.innerFill, true, false, z, profiles.innerInfill);
pathToGCode(outline, combing && true, gcode, part.outerFill, false, false, z, profiles.outerInfill);
pathToGCode(outline, combing && true, gcode, part.innerFill, true, false, z, profiles.innerInfill);
} else {
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') {
pathToGCode(gcode, slice.support, true, true, z, profiles.support);
pathToGCode(null, false, gcode, slice.support, true, true, z, profiles.support);
}
}
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 paths = shape.mapToLower();
@ -86,9 +90,15 @@ function pathToGCode(gcode, shape, retract, unRetract, z, { lineProfile, travelP
const point = line[i % line.length];
if (i === 0) {
// TODO
// moveTo should impliment combing
gcode.moveTo(point.x, point.y, z, travelProfile);
if (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);
}
if (unRetract) {
gcode.unRetract(retractionProfile);