move fdm print funcs to fdm mode. code style update
This commit is contained in:
parent
8eed635c3f
commit
0dd617feeb
10 changed files with 5055 additions and 5088 deletions
2
app.js
2
app.js
|
|
@ -410,6 +410,7 @@ const script = {
|
||||||
"geo/slicer",
|
"geo/slicer",
|
||||||
"geo/mesh",
|
"geo/mesh",
|
||||||
// "moto/broker",
|
// "moto/broker",
|
||||||
|
"kiri/consts",
|
||||||
"kiri/pack",
|
"kiri/pack",
|
||||||
"kiri/utils",
|
"kiri/utils",
|
||||||
"kiri/slice",
|
"kiri/slice",
|
||||||
|
|
@ -459,6 +460,7 @@ const script = {
|
||||||
"geo/polygon",
|
"geo/polygon",
|
||||||
"geo/gyroid",
|
"geo/gyroid",
|
||||||
"geo/slicer",
|
"geo/slicer",
|
||||||
|
"kiri/consts",
|
||||||
"kiri-mode/fdm/driver",
|
"kiri-mode/fdm/driver",
|
||||||
"kiri-mode/fdm/slice",
|
"kiri-mode/fdm/slice",
|
||||||
"kiri/utils",
|
"kiri/utils",
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -4,69 +4,66 @@
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
const KIRI = self.kiri,
|
const { kiri } = self;
|
||||||
BASE = self.base,
|
const FDM = kiri.driver.FDM = { getRangeParameters };
|
||||||
UTIL = BASE.util,
|
|
||||||
POLY = BASE.polygons,
|
|
||||||
FDM = KIRI.driver.FDM = {
|
|
||||||
// init, // src/mode/fdm/client.js
|
|
||||||
// slice, // src/mode/fdm/slice.js
|
|
||||||
// prepare, // src/mode/fdm/prepare.js
|
|
||||||
// export, // src/mode/fdm/export.js
|
|
||||||
getRangeParameters
|
|
||||||
};
|
|
||||||
|
|
||||||
function getRangeParameters(process, index) {
|
// shared by client and worker contexts
|
||||||
if (index === undefined || index === null || index < 0) {
|
function getRangeParameters(process, index) {
|
||||||
return process;
|
if (index === undefined || index === null || index < 0) {
|
||||||
}
|
return process;
|
||||||
let ranges = process.ranges;
|
}
|
||||||
if (!(ranges && ranges.length)) {
|
let ranges = process.ranges;
|
||||||
return process;
|
if (!(ranges && ranges.length)) {
|
||||||
}
|
return process;
|
||||||
let params = Object.clone(process);
|
}
|
||||||
for (let range of ranges) {
|
let params = Object.clone(process);
|
||||||
if (index >= range.lo && index <= range.hi) {
|
for (let range of ranges) {
|
||||||
for (let [key,value] of Object.entries(range.fields)) {
|
if (index >= range.lo && index <= range.hi) {
|
||||||
params[key] = value;
|
for (let [key, value] of Object.entries(range.fields)) {
|
||||||
params._range = true;
|
params[key] = value;
|
||||||
}
|
params._range = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return params;
|
|
||||||
}
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
// defer loading until KIRI.client and KIRI.worker exist
|
// defer loading until client and worker exist
|
||||||
KIRI.load(function(API) {
|
kiri.load(function(api) {
|
||||||
|
|
||||||
if (KIRI.client)
|
const { client, worker } = kiri;
|
||||||
// FDM.support_generate = KIRI.client.fdm_support_generate = function(ondone) {
|
|
||||||
|
if (client) {
|
||||||
FDM.support_generate = function(ondone) {
|
FDM.support_generate = function(ondone) {
|
||||||
KIRI.client.clear();
|
client.clear();
|
||||||
KIRI.client.sync();
|
client.sync();
|
||||||
let settings = API.conf.get();
|
const settings = api.conf.get();
|
||||||
let widgets = API.widgets.map();
|
const widgets = api.widgets.map();
|
||||||
KIRI.client.send("fdm_support_generate", { settings }, (gen) => {
|
client.send("fdm_support_generate", { settings }, (gen) => {
|
||||||
if (gen && gen.error) {
|
if (gen && gen.error) {
|
||||||
API.show.alert('support generation canceled');
|
api.show.alert('support generation canceled');
|
||||||
return ondone([]);
|
return ondone([]);
|
||||||
}
|
}
|
||||||
for (let g of gen) g.widget = widgets[g.id];
|
for (let g of gen) {
|
||||||
|
g.widget = widgets[g.id];
|
||||||
|
}
|
||||||
ondone(gen);
|
ondone(gen);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (KIRI.worker)
|
if (worker) {
|
||||||
KIRI.worker.fdm_support_generate = function(data, send) {
|
worker.fdm_support_generate = function(data, send) {
|
||||||
const { settings } = data;
|
const { settings } = data;
|
||||||
const widgets = Object.values(wcache);
|
const widgets = Object.values(wcache);
|
||||||
const fresh = widgets.filter(widget => FDM.supports(settings, widget));
|
const fresh = widgets.filter(widget => FDM.supports(settings, widget));
|
||||||
send.done(KIRI.codec.encode(fresh.map(widget => { return {
|
send.done(kiri.codec.encode(fresh.map(widget => { return {
|
||||||
id: widget.id,
|
id: widget.id,
|
||||||
supports: widget.supports,
|
supports: widget.supports,
|
||||||
} } )));
|
} } )));
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -4,317 +4,317 @@
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
if (self.kiri.fill) return;
|
if (self.kiri.fill) return;
|
||||||
|
|
||||||
const KIRI = self.kiri,
|
const KIRI = self.kiri,
|
||||||
BASE = self.base,
|
BASE = self.base,
|
||||||
UTIL = BASE.util,
|
UTIL = BASE.util,
|
||||||
ROUND = UTIL.round,
|
ROUND = UTIL.round,
|
||||||
DEG2RAD = Math.PI / 180,
|
DEG2RAD = Math.PI / 180,
|
||||||
FILL = self.kiri.fill = {
|
FILL = self.kiri.fill = {
|
||||||
hex: fillHexFull,
|
hex: fillHexFull,
|
||||||
grid: fillGrid,
|
grid: fillGrid,
|
||||||
gyroid: fillGyroid,
|
gyroid: fillGyroid,
|
||||||
triangle: fillTriangle,
|
triangle: fillTriangle,
|
||||||
linear: fillLinear,
|
linear: fillLinear,
|
||||||
cubic: fillCubic
|
cubic: fillCubic
|
||||||
},
|
},
|
||||||
CACHE = self.kiri.fill_fixed = {
|
CACHE = self.kiri.fill_fixed = {
|
||||||
hex: fillHexFull,
|
hex: fillHexFull,
|
||||||
grid: fillGrid,
|
grid: fillGrid,
|
||||||
triangle: fillTriangle
|
triangle: fillTriangle
|
||||||
};
|
};
|
||||||
|
|
||||||
function fillHexFull(target) {
|
function fillHexFull(target) {
|
||||||
fillHex(target, true);
|
fillHex(target, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* emitter creates a hex infill pattern and sends to target
|
* emitter creates a hex infill pattern and sends to target
|
||||||
*
|
*
|
||||||
* @param {Object} target
|
* @param {Object} target
|
||||||
* @param {boolean} full continuous walls
|
* @param {boolean} full continuous walls
|
||||||
*/
|
*/
|
||||||
function fillHex(target, full) {
|
function fillHex(target, full) {
|
||||||
// compute segment lengths (vert/horiz and 45)
|
// compute segment lengths (vert/horiz and 45)
|
||||||
let spacing = target.offset();
|
let spacing = target.offset();
|
||||||
// let vhlen = (1 / target.density()) * (target.lineWidth() + spacing);
|
// let vhlen = (1 / target.density()) * (target.lineWidth() + spacing);
|
||||||
let vhlen = (1 / target.density()) * target.lineWidth() * 0.5;
|
let vhlen = (1 / target.density()) * target.lineWidth() * 0.5;
|
||||||
let anxlen = ROUND(Math.cos(30 * DEG2RAD) * vhlen, 7);
|
let anxlen = ROUND(Math.cos(30 * DEG2RAD) * vhlen, 7);
|
||||||
let anylen = ROUND(Math.sin(30 * DEG2RAD) * vhlen, 7);
|
let anylen = ROUND(Math.sin(30 * DEG2RAD) * vhlen, 7);
|
||||||
let bounds = target.bounds();
|
let bounds = target.bounds();
|
||||||
let even = true;
|
let even = true;
|
||||||
let evenZ = target.zIndex() % 2 === 0;
|
let evenZ = target.zIndex() % 2 === 0;
|
||||||
let maxy = bounds.max.y + (vhlen + anylen * 2);
|
let maxy = bounds.max.y + (vhlen + anylen * 2);
|
||||||
let x, y;
|
let x, y;
|
||||||
|
|
||||||
if (full || evenZ) {
|
if (full || evenZ) {
|
||||||
x = bounds.min.x;
|
x = bounds.min.x;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (even && x > bounds.max.x) break;
|
if (even && x > bounds.max.x) break;
|
||||||
if (!even && x > bounds.max.x + anxlen + spacing) break;
|
if (!even && x > bounds.max.x + anxlen + spacing) break;
|
||||||
y = bounds.min.y;
|
y = bounds.min.y;
|
||||||
target.newline();
|
|
||||||
while (y <= maxy) {
|
|
||||||
target.emit(x,y);
|
|
||||||
y += vhlen;
|
|
||||||
target.emit(x,y);
|
|
||||||
if (even) x += anxlen; else x -= anxlen;
|
|
||||||
y += anylen;
|
|
||||||
target.emit(x,y);
|
|
||||||
y += vhlen;
|
|
||||||
target.emit(x,y);
|
|
||||||
if (even) x -= anxlen; else x += anxlen;
|
|
||||||
y += anylen;
|
|
||||||
}
|
|
||||||
x += spacing;
|
|
||||||
if (even) x += (anxlen * 2);
|
|
||||||
even = !even;
|
|
||||||
target.newline();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
y = bounds.min.y + vhlen;
|
|
||||||
for (;;) {
|
|
||||||
if (even && y > bounds.max.y) break;
|
|
||||||
if (!even && y > bounds.max.y + anylen) break;
|
|
||||||
x = bounds.min.x;
|
|
||||||
target.newline();
|
|
||||||
while (x < bounds.max.x) {
|
|
||||||
target.emit(x,y);
|
|
||||||
if (even) y += anylen; else y -= anylen;
|
|
||||||
x += anxlen;
|
|
||||||
target.emit(x,y);
|
|
||||||
x += spacing;
|
|
||||||
target.emit(x,y);
|
|
||||||
if (even) y -= anylen; else y += anylen;
|
|
||||||
x += anxlen;
|
|
||||||
target.emit(x,y);
|
|
||||||
x += spacing;
|
|
||||||
}
|
|
||||||
y += vhlen;
|
|
||||||
if (even) y += (anylen * 2);
|
|
||||||
even = !even;
|
|
||||||
target.newline();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fillGyroid(target) {
|
|
||||||
let bounds = target.bounds();
|
|
||||||
let height = target.zHeight();
|
|
||||||
let span_x = bounds.max.x - bounds.min.x;
|
|
||||||
let span_y = bounds.max.y - bounds.min.y;
|
|
||||||
let density = target.density();
|
|
||||||
let tile = 1 + (1 - density) * 15;
|
|
||||||
let tile_x = span_x / tile;
|
|
||||||
let tile_y = span_y / tile;
|
|
||||||
let tile_z = 1 / tile;
|
|
||||||
let gyroid = BASE.gyroid.slice(target.zValue() * tile_z, (1 - density) * 500);
|
|
||||||
|
|
||||||
// gyroid.polys.forEach(poly => {
|
|
||||||
// for (let tx=0; tx<=tile_x; tx++) {
|
|
||||||
// for (let ty=0; ty<=tile_y; ty++) {
|
|
||||||
// target.newline();
|
|
||||||
// let bx = tx * tile + bounds.min.x;
|
|
||||||
// let by = ty * tile + bounds.min.y;
|
|
||||||
// poly.forEach(point => {
|
|
||||||
// target.emit(bx + point.x * tile, by + point.y * tile);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
let polys = [];
|
|
||||||
for (let tx=0; tx<=tile_x; tx++) {
|
|
||||||
for (let ty=0; ty<=tile_y; ty++) {
|
|
||||||
for (let poly of gyroid.polys) {
|
|
||||||
target.newline();
|
|
||||||
let points = poly.map(el => {
|
|
||||||
return {
|
|
||||||
x: el.x * tile + tx * tile + bounds.min.x,
|
|
||||||
y: el.y * tile + ty * tile + bounds.min.y,
|
|
||||||
z: 0
|
|
||||||
}
|
|
||||||
});
|
|
||||||
polys.push(BASE.newPolygon().setOpen(true).addObj(points));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
polys = connectOpenPolys(polys);
|
|
||||||
for (let poly of polys.filter(p => p.perimeter() > 2)) {
|
|
||||||
target.newline();
|
target.newline();
|
||||||
for (let point of poly.points) {
|
while (y <= maxy) {
|
||||||
target.emit(point.x, point.y);
|
target.emit(x,y);
|
||||||
|
y += vhlen;
|
||||||
|
target.emit(x,y);
|
||||||
|
if (even) x += anxlen; else x -= anxlen;
|
||||||
|
y += anylen;
|
||||||
|
target.emit(x,y);
|
||||||
|
y += vhlen;
|
||||||
|
target.emit(x,y);
|
||||||
|
if (even) x -= anxlen; else x += anxlen;
|
||||||
|
y += anylen;
|
||||||
|
}
|
||||||
|
x += spacing;
|
||||||
|
if (even) x += (anxlen * 2);
|
||||||
|
even = !even;
|
||||||
|
target.newline();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
y = bounds.min.y + vhlen;
|
||||||
|
for (;;) {
|
||||||
|
if (even && y > bounds.max.y) break;
|
||||||
|
if (!even && y > bounds.max.y + anylen) break;
|
||||||
|
x = bounds.min.x;
|
||||||
|
target.newline();
|
||||||
|
while (x < bounds.max.x) {
|
||||||
|
target.emit(x,y);
|
||||||
|
if (even) y += anylen; else y -= anylen;
|
||||||
|
x += anxlen;
|
||||||
|
target.emit(x,y);
|
||||||
|
x += spacing;
|
||||||
|
target.emit(x,y);
|
||||||
|
if (even) y -= anylen; else y += anylen;
|
||||||
|
x += anxlen;
|
||||||
|
target.emit(x,y);
|
||||||
|
x += spacing;
|
||||||
|
}
|
||||||
|
y += vhlen;
|
||||||
|
if (even) y += (anylen * 2);
|
||||||
|
even = !even;
|
||||||
|
target.newline();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillGyroid(target) {
|
||||||
|
let bounds = target.bounds();
|
||||||
|
let height = target.zHeight();
|
||||||
|
let span_x = bounds.max.x - bounds.min.x;
|
||||||
|
let span_y = bounds.max.y - bounds.min.y;
|
||||||
|
let density = target.density();
|
||||||
|
let tile = 1 + (1 - density) * 15;
|
||||||
|
let tile_x = span_x / tile;
|
||||||
|
let tile_y = span_y / tile;
|
||||||
|
let tile_z = 1 / tile;
|
||||||
|
let gyroid = BASE.gyroid.slice(target.zValue() * tile_z, (1 - density) * 500);
|
||||||
|
|
||||||
|
// gyroid.polys.forEach(poly => {
|
||||||
|
// for (let tx=0; tx<=tile_x; tx++) {
|
||||||
|
// for (let ty=0; ty<=tile_y; ty++) {
|
||||||
|
// target.newline();
|
||||||
|
// let bx = tx * tile + bounds.min.x;
|
||||||
|
// let by = ty * tile + bounds.min.y;
|
||||||
|
// poly.forEach(point => {
|
||||||
|
// target.emit(bx + point.x * tile, by + point.y * tile);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
let polys = [];
|
||||||
|
for (let tx=0; tx<=tile_x; tx++) {
|
||||||
|
for (let ty=0; ty<=tile_y; ty++) {
|
||||||
|
for (let poly of gyroid.polys) {
|
||||||
|
target.newline();
|
||||||
|
let points = poly.map(el => {
|
||||||
|
return {
|
||||||
|
x: el.x * tile + tx * tile + bounds.min.x,
|
||||||
|
y: el.y * tile + ty * tile + bounds.min.y,
|
||||||
|
z: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
polys.push(BASE.newPolygon().setOpen(true).addObj(points));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
polys = connectOpenPolys(polys);
|
||||||
|
for (let poly of polys.filter(p => p.perimeter() > 2)) {
|
||||||
|
target.newline();
|
||||||
|
for (let point of poly.points) {
|
||||||
|
target.emit(point.x, point.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function connectOpenPolys(noff, dist = 0.1) {
|
function connectOpenPolys(noff, dist = 0.1) {
|
||||||
if (noff.length <= 1) {
|
if (noff.length <= 1) {
|
||||||
return noff;
|
|
||||||
}
|
|
||||||
let heal = 0;
|
|
||||||
// heal/rejoin open segments that have close endpoints
|
|
||||||
outer: for(;; heal++) {
|
|
||||||
let ntmp = noff, tlen = ntmp.length;
|
|
||||||
for (let i=0; i<tlen; i++) {
|
|
||||||
let s1 = ntmp[i];
|
|
||||||
if (!s1 || !s1.open) continue;
|
|
||||||
for (let j=i+1; j<tlen; j++) {
|
|
||||||
let s2 = ntmp[j];
|
|
||||||
if (!s2 || !s2.open) continue;
|
|
||||||
if (s1.last().distTo2D(s2.first()) <= dist) {
|
|
||||||
s1.addPoints(s2.points);
|
|
||||||
ntmp[j] = null;
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
if (s1.first().distTo2D(s2.last()) <= dist) {
|
|
||||||
s2.addPoints(s1.points);
|
|
||||||
ntmp[i] = null;
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
if (s1.first().distTo2D(s2.first()) <= dist) {
|
|
||||||
s1.reverse();
|
|
||||||
s1.addPoints(s2.points);
|
|
||||||
ntmp[j] = null;
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
if (s1.last().distTo2D(s2.last()) <= dist) {
|
|
||||||
s1.addPoints(s2.points.reverse());
|
|
||||||
ntmp[j] = null;
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (heal > 0) {
|
|
||||||
// cull nulls
|
|
||||||
noff = noff.filter(o => o);
|
|
||||||
}
|
|
||||||
return noff;
|
return noff;
|
||||||
}
|
}
|
||||||
|
let heal = 0;
|
||||||
function fillGrid(target) {
|
// heal/rejoin open segments that have close endpoints
|
||||||
let bounds = target.bounds();
|
outer: for(;; heal++) {
|
||||||
let height = target.zHeight();
|
let ntmp = noff, tlen = ntmp.length;
|
||||||
let span_x = bounds.max.x - bounds.min.x;
|
for (let i=0; i<tlen; i++) {
|
||||||
let span_y = bounds.max.y - bounds.min.y;
|
let s1 = ntmp[i];
|
||||||
let offset = target.offset() / 2;
|
if (!s1 || !s1.open) continue;
|
||||||
let tile = (1 / target.density()) * target.lineWidth();
|
for (let j=i+1; j<tlen; j++) {
|
||||||
let tile_x = tile + offset;
|
let s2 = ntmp[j];
|
||||||
let tile_xc = span_x / tile_x;
|
if (!s2 || !s2.open) continue;
|
||||||
let tile_yc = span_y / tile;
|
if (s1.last().distTo2D(s2.first()) <= dist) {
|
||||||
|
s1.addPoints(s2.points);
|
||||||
for (let tx=0; tx<=tile_xc; tx++) {
|
ntmp[j] = null;
|
||||||
target.newline();
|
continue outer;
|
||||||
for (let ty=0; ty<=tile_yc; ty++) {
|
}
|
||||||
let bx = tx * tile_x + bounds.min.x;
|
if (s1.first().distTo2D(s2.last()) <= dist) {
|
||||||
let by = ty * tile + bounds.min.y;
|
s2.addPoints(s1.points);
|
||||||
if ((tx + ty) % 2) {
|
ntmp[i] = null;
|
||||||
target.emit(bx, by);
|
continue outer;
|
||||||
target.emit(bx + tile_x - offset, by + tile);
|
}
|
||||||
} else {
|
if (s1.first().distTo2D(s2.first()) <= dist) {
|
||||||
target.emit(bx + tile_x - offset, by);
|
s1.reverse();
|
||||||
target.emit(bx, by + tile);
|
s1.addPoints(s2.points);
|
||||||
|
ntmp[j] = null;
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
if (s1.last().distTo2D(s2.last()) <= dist) {
|
||||||
|
s1.addPoints(s2.points.reverse());
|
||||||
|
ntmp[j] = null;
|
||||||
|
continue outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
if (heal > 0) {
|
||||||
function fillCubic(target) {
|
// cull nulls
|
||||||
let bounds = target.bounds();
|
noff = noff.filter(o => o);
|
||||||
let span = Math.max(
|
|
||||||
bounds.max.x - bounds.min.x,
|
|
||||||
bounds.max.y - bounds.min.y
|
|
||||||
);
|
|
||||||
let steps = Math.floor((span / target.lineWidth()) * target.density());
|
|
||||||
let step = span / steps;
|
|
||||||
let ztype = Math.floor(target.zIndex() / target.repeat()) % 3;
|
|
||||||
if (ztype === 1) {
|
|
||||||
for (let tx=bounds.min.x; tx<=bounds.max.x; tx += step) {
|
|
||||||
target.newline();
|
|
||||||
target.emit(tx, bounds.min.y);
|
|
||||||
target.emit(tx, bounds.max.y);
|
|
||||||
}
|
|
||||||
} else if (ztype === 0) {
|
|
||||||
for (let ty=bounds.min.y; ty<=bounds.max.y; ty += step) {
|
|
||||||
target.newline();
|
|
||||||
target.emit(bounds.min.x, ty);
|
|
||||||
target.emit(bounds.max.x, ty);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
step *- Math.sqrt(2);
|
|
||||||
for (let tx=bounds.min.x; tx<=bounds.max.x; tx += step) {
|
|
||||||
target.newline();
|
|
||||||
target.emit(tx, bounds.min.y);
|
|
||||||
target.emit(tx + 1000, bounds.max.y + 1000);
|
|
||||||
}
|
|
||||||
for (let ty=bounds.min.y; ty<=bounds.max.y; ty += step) {
|
|
||||||
target.newline();
|
|
||||||
target.emit(bounds.min.x, ty);
|
|
||||||
target.emit(bounds.min.x + 1000, ty + 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return noff;
|
||||||
|
}
|
||||||
|
|
||||||
function fillLinear(target) {
|
function fillGrid(target) {
|
||||||
let bounds = target.bounds();
|
let bounds = target.bounds();
|
||||||
let span = Math.max(
|
let height = target.zHeight();
|
||||||
bounds.max.x - bounds.min.x,
|
let span_x = bounds.max.x - bounds.min.x;
|
||||||
bounds.max.y - bounds.min.y
|
let span_y = bounds.max.y - bounds.min.y;
|
||||||
);
|
let offset = target.offset() / 2;
|
||||||
let steps = Math.floor((span / target.lineWidth()) * target.density());
|
let tile = (1 / target.density()) * target.lineWidth();
|
||||||
let step = span / steps;
|
let tile_x = tile + offset;
|
||||||
let ztype = Math.floor(target.zIndex() / target.repeat()) % 2;
|
let tile_xc = span_x / tile_x;
|
||||||
if (ztype === 1) {
|
let tile_yc = span_y / tile;
|
||||||
for (let tx=bounds.min.x; tx<=bounds.max.x; tx += step) {
|
|
||||||
target.newline();
|
|
||||||
target.emit(tx, bounds.min.y);
|
|
||||||
target.emit(tx, bounds.max.y);
|
|
||||||
}
|
|
||||||
} else if (ztype === 0) {
|
|
||||||
for (let ty=bounds.min.y; ty<=bounds.max.y; ty += step) {
|
|
||||||
target.newline();
|
|
||||||
target.emit(bounds.min.x, ty);
|
|
||||||
target.emit(bounds.max.x, ty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fillTriangle(target) {
|
for (let tx=0; tx<=tile_xc; tx++) {
|
||||||
let bounds = target.bounds();
|
target.newline();
|
||||||
let span_x = bounds.max.x - bounds.min.x;
|
for (let ty=0; ty<=tile_yc; ty++) {
|
||||||
let span_y = bounds.max.y - bounds.min.y;
|
|
||||||
let offset = target.offset();
|
|
||||||
let line_w = target.lineWidth() / 2;
|
|
||||||
let tile = (1 / target.density()) * (target.lineWidth() * 1.25);
|
|
||||||
let tile_x = tile + offset*2 + line_w;
|
|
||||||
let tile_xc = span_x / tile_x;
|
|
||||||
let tile_yc = span_y / tile;
|
|
||||||
|
|
||||||
for (let tx=0; tx<=tile_xc; tx++) {
|
|
||||||
target.newline();
|
|
||||||
for (let ty=0; ty<=tile_yc; ty++) {
|
|
||||||
let bx = tx * tile_x + bounds.min.x;
|
|
||||||
let by = ty * tile + bounds.min.y;
|
|
||||||
if ((tx + ty) % 2) {
|
|
||||||
target.emit(bx, by);
|
|
||||||
target.emit(bx + tile_x - offset - line_w, by + tile);
|
|
||||||
} else {
|
|
||||||
target.emit(bx + tile_x - offset - line_w, by);
|
|
||||||
target.emit(bx, by + tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let tx=0; tx<=tile_xc; tx++) {
|
|
||||||
let bx = tx * tile_x + bounds.min.x;
|
let bx = tx * tile_x + bounds.min.x;
|
||||||
let xp = bx + tile_x - line_w/2 - offset/2;
|
let by = ty * tile + bounds.min.y;
|
||||||
target.newline();
|
if ((tx + ty) % 2) {
|
||||||
target.emit(xp, bounds.min.y);
|
target.emit(bx, by);
|
||||||
target.emit(xp, bounds.max.y);
|
target.emit(bx + tile_x - offset, by + tile);
|
||||||
|
} else {
|
||||||
|
target.emit(bx + tile_x - offset, by);
|
||||||
|
target.emit(bx, by + tile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillCubic(target) {
|
||||||
|
let bounds = target.bounds();
|
||||||
|
let span = Math.max(
|
||||||
|
bounds.max.x - bounds.min.x,
|
||||||
|
bounds.max.y - bounds.min.y
|
||||||
|
);
|
||||||
|
let steps = Math.floor((span / target.lineWidth()) * target.density());
|
||||||
|
let step = span / steps;
|
||||||
|
let ztype = Math.floor(target.zIndex() / target.repeat()) % 3;
|
||||||
|
if (ztype === 1) {
|
||||||
|
for (let tx=bounds.min.x; tx<=bounds.max.x; tx += step) {
|
||||||
|
target.newline();
|
||||||
|
target.emit(tx, bounds.min.y);
|
||||||
|
target.emit(tx, bounds.max.y);
|
||||||
|
}
|
||||||
|
} else if (ztype === 0) {
|
||||||
|
for (let ty=bounds.min.y; ty<=bounds.max.y; ty += step) {
|
||||||
|
target.newline();
|
||||||
|
target.emit(bounds.min.x, ty);
|
||||||
|
target.emit(bounds.max.x, ty);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
step *- Math.sqrt(2);
|
||||||
|
for (let tx=bounds.min.x; tx<=bounds.max.x; tx += step) {
|
||||||
|
target.newline();
|
||||||
|
target.emit(tx, bounds.min.y);
|
||||||
|
target.emit(tx + 1000, bounds.max.y + 1000);
|
||||||
|
}
|
||||||
|
for (let ty=bounds.min.y; ty<=bounds.max.y; ty += step) {
|
||||||
|
target.newline();
|
||||||
|
target.emit(bounds.min.x, ty);
|
||||||
|
target.emit(bounds.min.x + 1000, ty + 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillLinear(target) {
|
||||||
|
let bounds = target.bounds();
|
||||||
|
let span = Math.max(
|
||||||
|
bounds.max.x - bounds.min.x,
|
||||||
|
bounds.max.y - bounds.min.y
|
||||||
|
);
|
||||||
|
let steps = Math.floor((span / target.lineWidth()) * target.density());
|
||||||
|
let step = span / steps;
|
||||||
|
let ztype = Math.floor(target.zIndex() / target.repeat()) % 2;
|
||||||
|
if (ztype === 1) {
|
||||||
|
for (let tx=bounds.min.x; tx<=bounds.max.x; tx += step) {
|
||||||
|
target.newline();
|
||||||
|
target.emit(tx, bounds.min.y);
|
||||||
|
target.emit(tx, bounds.max.y);
|
||||||
|
}
|
||||||
|
} else if (ztype === 0) {
|
||||||
|
for (let ty=bounds.min.y; ty<=bounds.max.y; ty += step) {
|
||||||
|
target.newline();
|
||||||
|
target.emit(bounds.min.x, ty);
|
||||||
|
target.emit(bounds.max.x, ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillTriangle(target) {
|
||||||
|
let bounds = target.bounds();
|
||||||
|
let span_x = bounds.max.x - bounds.min.x;
|
||||||
|
let span_y = bounds.max.y - bounds.min.y;
|
||||||
|
let offset = target.offset();
|
||||||
|
let line_w = target.lineWidth() / 2;
|
||||||
|
let tile = (1 / target.density()) * (target.lineWidth() * 1.25);
|
||||||
|
let tile_x = tile + offset*2 + line_w;
|
||||||
|
let tile_xc = span_x / tile_x;
|
||||||
|
let tile_yc = span_y / tile;
|
||||||
|
|
||||||
|
for (let tx=0; tx<=tile_xc; tx++) {
|
||||||
|
target.newline();
|
||||||
|
for (let ty=0; ty<=tile_yc; ty++) {
|
||||||
|
let bx = tx * tile_x + bounds.min.x;
|
||||||
|
let by = ty * tile + bounds.min.y;
|
||||||
|
if ((tx + ty) % 2) {
|
||||||
|
target.emit(bx, by);
|
||||||
|
target.emit(bx + tile_x - offset - line_w, by + tile);
|
||||||
|
} else {
|
||||||
|
target.emit(bx + tile_x - offset - line_w, by);
|
||||||
|
target.emit(bx, by + tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let tx=0; tx<=tile_xc; tx++) {
|
||||||
|
let bx = tx * tile_x + bounds.min.x;
|
||||||
|
let xp = bx + tile_x - line_w/2 - offset/2;
|
||||||
|
target.newline();
|
||||||
|
target.emit(xp, bounds.min.y);
|
||||||
|
target.emit(xp, bounds.max.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -131,6 +131,7 @@ kiri.consts = {
|
||||||
MODES,
|
MODES,
|
||||||
VIEWS,
|
VIEWS,
|
||||||
SEED,
|
SEED,
|
||||||
|
beltfact: Math.cos(Math.PI / 4)
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
const { feature, platform, selection, settings } = api;
|
const { feature, platform, selection, settings } = api;
|
||||||
const { COLOR, MODES, PMODES, VIEWS } = consts;
|
const { COLOR, MODES, PMODES, VIEWS } = consts;
|
||||||
|
|
||||||
const LANG = lang.current,
|
const LANG = lang.current,
|
||||||
WIN = self.window,
|
WIN = self.window,
|
||||||
DOC = self.document,
|
DOC = self.document,
|
||||||
LOC = self.location,
|
LOC = self.location,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
const { base, kiri } = self;
|
const { base, kiri } = self;
|
||||||
const { paths, util, newPoint, Polygon } = base;
|
const { paths, util, newPoint, Polygon } = base;
|
||||||
const { tip2tipEmit } = paths;
|
const { tip2tipEmit } = paths;
|
||||||
|
const { numOrDefault } = util;
|
||||||
|
const { beltfact } = kiri.consts;
|
||||||
|
|
||||||
kiri.newPrint = function(settings, widgets, id) {
|
kiri.newPrint = function(settings, widgets, id) {
|
||||||
return new Print(settings, widgets, id);
|
return new Print(settings, widgets, id);
|
||||||
|
|
@ -70,7 +72,7 @@ class Print {
|
||||||
const { process } = settings;
|
const { process } = settings;
|
||||||
|
|
||||||
let shortDist = process.outputShortDistance,
|
let shortDist = process.outputShortDistance,
|
||||||
shellMult = pref(options.extrude, process.outputShellMult),
|
shellMult = numOrDefault(options.extrude, process.outputShellMult),
|
||||||
printSpeed = options.rate || process.outputFeedrate,
|
printSpeed = options.rate || process.outputFeedrate,
|
||||||
moveSpeed = process.outputSeekrate,
|
moveSpeed = process.outputSeekrate,
|
||||||
minSpeed = process.outputMinSpeed,
|
minSpeed = process.outputMinSpeed,
|
||||||
|
|
@ -169,638 +171,6 @@ class Print {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fdm only
|
|
||||||
slicePrintPath(slice, startPoint, offset, output, opt = {}) {
|
|
||||||
const scope = this;
|
|
||||||
const { settings } = scope;
|
|
||||||
const { device } = settings;
|
|
||||||
|
|
||||||
// console.log({slicePrintPath: slice.index, ext:slice.extruder});
|
|
||||||
let i,
|
|
||||||
preout = [],
|
|
||||||
process = opt.params || settings.process,
|
|
||||||
extruder = slice.extruder || 0,
|
|
||||||
nozzleSize = device.extruders[extruder].extNozzle,
|
|
||||||
firstLayer = opt.first || false,
|
|
||||||
thinWall = nozzleSize * (opt.thinWall || 1.75),
|
|
||||||
retractDist = opt.retractOver || 2,
|
|
||||||
solidWidth = process.sliceFillWidth || 1,
|
|
||||||
fillMult = opt.mult || process.outputFillMult,
|
|
||||||
shellMult = opt.mult || process.outputShellMult || (process.laserSliceHeight >= 0 ? 1 : 0),
|
|
||||||
shellOrder = {"out-in":-1,"in-out":1}[process.sliceShellOrder] || -1,
|
|
||||||
sparseMult = process.outputSparseMult,
|
|
||||||
coastDist = process.outputCoastDist || 0,
|
|
||||||
finishSpeed = opt.speed || process.outputFinishrate,
|
|
||||||
firstShellSpeed = process.firstLayerRate,
|
|
||||||
firstFillSpeed = process.firstLayerFillRate,
|
|
||||||
firstPrintMult = process.firstLayerPrintMult,
|
|
||||||
printSpeed = opt.speed || (firstLayer ? firstShellSpeed : process.outputFeedrate),
|
|
||||||
fillSpeed = opt.speed || opt.fillSpeed || (firstLayer ? firstFillSpeed || firstShellSpeed : process.outputFeedrate),
|
|
||||||
infillSpeed = process.sliceFillRate || opt.infillSpeed || fillSpeed || printSpeed,
|
|
||||||
moveSpeed = process.outputSeekrate,
|
|
||||||
origin = startPoint.add(offset),
|
|
||||||
zhop = process.zHopDistance || 0,
|
|
||||||
antiBacklash = process.antiBacklash,
|
|
||||||
wipeDist = process.outputRetractWipe || 0,
|
|
||||||
isBelt = device.bedBelt,
|
|
||||||
beltFirst = process.outputBeltFirst || false,
|
|
||||||
startClone = startPoint.clone(),
|
|
||||||
seedPoint = opt.seedPoint || startPoint,
|
|
||||||
z = slice.z,
|
|
||||||
lastPoly;
|
|
||||||
|
|
||||||
// apply first layer extrusion multipliers
|
|
||||||
if (firstLayer) {
|
|
||||||
fillMult *= firstPrintMult;
|
|
||||||
shellMult *= firstPrintMult;
|
|
||||||
sparseMult *= firstPrintMult;
|
|
||||||
}
|
|
||||||
|
|
||||||
function retract() {
|
|
||||||
let array = preout.length ? preout : output;
|
|
||||||
if (array.length) {
|
|
||||||
let last = array.last();
|
|
||||||
last.retract = true;
|
|
||||||
if (wipeDist && lastPoly && last.point) {
|
|
||||||
let endpoint = last.point.followTo(lastPoly.center(true), wipeDist);
|
|
||||||
if (endpoint.inPolygon(lastPoly)) {
|
|
||||||
scope.addOutput(array, endpoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (opt.pretract) {
|
|
||||||
opt.pretract(wipeDist);
|
|
||||||
} else {
|
|
||||||
console.log('unable to retract. no preout or output');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function intersectsTop(p1, p2) {
|
|
||||||
if (slice.index < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let int = false;
|
|
||||||
slice.topPolysFlat().forEach((poly) => {
|
|
||||||
if (!int) poly.forEachSegment((s1, s2) => {
|
|
||||||
if (util.intersect(p1,p2,s1,s2,base.key.SEGINT)) {
|
|
||||||
return int = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// if intersecting, look for a route around
|
|
||||||
if (int && opt.routeAround) {
|
|
||||||
return !routeAround(p1, p2);
|
|
||||||
}
|
|
||||||
return int;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns true if routed around or no retract requried
|
|
||||||
function routeAround(p1, p2) {
|
|
||||||
const dbug = false;
|
|
||||||
if (dbug === slice.index) console.log(slice.index, {p1, p2, d: p1.distTo2D(p2)});
|
|
||||||
|
|
||||||
let ints = [];
|
|
||||||
let tops = slice.topRouteFlat();
|
|
||||||
for (let poly of tops) {
|
|
||||||
poly.forEachSegment((s1, s2) => {
|
|
||||||
let ip = util.intersect(p1,p2,s1,s2,base.key.SEGINT);
|
|
||||||
if (ip) {
|
|
||||||
ints.push({ip, poly});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// no intersections
|
|
||||||
if (ints.length === 0) {
|
|
||||||
if (dbug === slice.index) console.log(slice.index, 'no ints');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// odd # of intersections ?!? do retraction
|
|
||||||
if (ints.length && ints.length % 2 !== 0) {
|
|
||||||
if (dbug === slice.index) console.log(slice.index, {odd_intersects: ints});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort by distance
|
|
||||||
ints.sort((a, b) => {
|
|
||||||
return a.ip.dist - b.ip.dist;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (dbug === slice.index) console.log(slice.index, {ints});
|
|
||||||
|
|
||||||
// check pairs. eliminate too close points.
|
|
||||||
// pairs must intersect same poly or retract.
|
|
||||||
for (let i=0; i<ints.length; i += 2) {
|
|
||||||
let i1 = ints[i];
|
|
||||||
let i2 = ints[i+1];
|
|
||||||
// different poly. force retract
|
|
||||||
if (i1.poly !== i2.poly) {
|
|
||||||
if (dbug === slice.index) console.log(slice.index, {int_diff_poly: ints, i});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// mark invalid intersect pairs (low or zero dist, etc)
|
|
||||||
// TODO: only if this is the outer pair and there are closer inner pairs
|
|
||||||
if (i1.ip.distTo2D(i2.ip) < retractDist) {
|
|
||||||
if (dbug === slice.index) console.log(slice.index, {int_dist_too_small: i1.ip.distTo2D(i2.ip), retractDist});
|
|
||||||
ints[i] = undefined;
|
|
||||||
ints[i+1] = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// filter out invalid intersection pairs
|
|
||||||
ints = ints.filter(i => i);
|
|
||||||
|
|
||||||
if (ints.length > 2) {
|
|
||||||
if (dbug === slice.index) console.log(slice.index, {complex_route: ints.length});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ints.length === 2) {
|
|
||||||
// can route around intersected top polys
|
|
||||||
for (let i=0; i<ints.length; i += 2) {
|
|
||||||
let i1 = ints[0];
|
|
||||||
let i2 = ints[1];
|
|
||||||
|
|
||||||
// output first point
|
|
||||||
scope.addOutput(preout, i1.ip, 0, moveSpeed, extruder);
|
|
||||||
|
|
||||||
// create two loops around poly
|
|
||||||
// find shortest of two paths and emit poly points
|
|
||||||
let poly = i1.poly;
|
|
||||||
let isCW = poly.isClockwise();
|
|
||||||
let points = poly.points;
|
|
||||||
|
|
||||||
let p1p = isCW ? points : points.slice().reverse(); // CW
|
|
||||||
let p2p = isCW ? points.slice().reverse() : points; // CCW
|
|
||||||
|
|
||||||
let r1s = p1p.indexOf(isCW ? i1.ip.p2 : i1.ip.p1);
|
|
||||||
let r1e = p1p.indexOf(isCW ? i2.ip.p1 : i2.ip.p2);
|
|
||||||
|
|
||||||
let r1 = r1s === r1e ?
|
|
||||||
[ p1p[r1s] ] : r1s < r1e ?
|
|
||||||
[ ...p1p.slice(r1s,r1e+1) ] :
|
|
||||||
[ ...p1p.slice(r1s), ...p1p.slice(0,r1e+1) ];
|
|
||||||
|
|
||||||
let r1d = 0;
|
|
||||||
for (let i=1; i<r1.length; i++) {
|
|
||||||
r1d += r1[i-1].distTo2D(r1[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let r2s = p2p.indexOf(isCW ? i1.ip.p1 : i1.ip.p2);
|
|
||||||
let r2e = p2p.indexOf(isCW ? i2.ip.p2 : i2.ip.p1);
|
|
||||||
|
|
||||||
let r2 = r2s === r2e ?
|
|
||||||
[ p2p[r2s] ] : r2s < r2e ?
|
|
||||||
[ ...p2p.slice(r2s,r2e+1) ] :
|
|
||||||
[ ...p2p.slice(r2s), ...p2p.slice(0,r2e+1) ];
|
|
||||||
|
|
||||||
let r2d = 0;
|
|
||||||
for (let i=1; i<r2.length; i++) {
|
|
||||||
r2d += r2[i-1].distTo2D(r2[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let route = r1d <= r2d ? r1 : r2;
|
|
||||||
|
|
||||||
if (dbug === slice.index) console.log(slice.index, {
|
|
||||||
ints: ints.map(i=>i.ip.dist),
|
|
||||||
i1, i2, same: i1.poly === i2.poly,
|
|
||||||
route,
|
|
||||||
p1, p2, dist: p1.distTo2D(p2),
|
|
||||||
r1, r1d, r1s, r1e,
|
|
||||||
r2, r2d, r2s, r2e,
|
|
||||||
isCW});
|
|
||||||
|
|
||||||
for (let p of route) {
|
|
||||||
scope.addOutput(preout, p, 0, moveSpeed, extruder);
|
|
||||||
}
|
|
||||||
|
|
||||||
// output last point
|
|
||||||
scope.addOutput(preout, i2.ip, 0, moveSpeed, extruder);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function outputTraces(poly, opt = {}) {
|
|
||||||
if (!poly) return;
|
|
||||||
if (Array.isArray(poly)) {
|
|
||||||
if (opt.sort) {
|
|
||||||
let polys = poly.slice().sort((a,b) => {
|
|
||||||
return (a.perimeter() - b.perimeter()) * opt.sort;
|
|
||||||
});
|
|
||||||
let debug = polys.length > 3;
|
|
||||||
let last;
|
|
||||||
while (polys.length) {
|
|
||||||
let next;
|
|
||||||
for (let p of polys) {
|
|
||||||
if (!last) {
|
|
||||||
next = p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (opt.sort > 0) {
|
|
||||||
// in-out
|
|
||||||
if (last.isInside(p)) {
|
|
||||||
next = p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// out-in
|
|
||||||
if (p.isInside(last)) {
|
|
||||||
next = p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (next) {
|
|
||||||
last = next;
|
|
||||||
polys.remove(next);
|
|
||||||
outputTraces(next, opt);
|
|
||||||
} else {
|
|
||||||
last = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
outputOrderClosest(poly, function(next) {
|
|
||||||
outputTraces(next, opt);
|
|
||||||
}, null);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let finishShell = poly.depth === 0 && !firstLayer;
|
|
||||||
startPoint = scope.polyPrintPath(poly, startPoint, preout, {
|
|
||||||
tool: extruder,
|
|
||||||
rate: finishShell ? finishSpeed : printSpeed,
|
|
||||||
accel: finishShell,
|
|
||||||
wipe: process.outputWipeDistance || 0,
|
|
||||||
coast: firstLayer ? 0 : coastDist,
|
|
||||||
extrude: pref(opt.extrude, shellMult),
|
|
||||||
onfirst: function(firstPoint) {
|
|
||||||
let from = seedPoint || startPoint;
|
|
||||||
if (from.distTo2D(firstPoint) > retractDist) {
|
|
||||||
if (intersectsTop(from, firstPoint)) {
|
|
||||||
retract();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
seedPoint = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
lastPoly = slice.lastPoly = poly;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Polygon[]} polys
|
|
||||||
*/
|
|
||||||
function outputSparse(polys, extrude, speed) {
|
|
||||||
if (!polys) return;
|
|
||||||
let proxy = polys.map((poly) => {
|
|
||||||
return {poly: poly, first: poly.first(), last: poly.last()};
|
|
||||||
});
|
|
||||||
let lp = startPoint;
|
|
||||||
startPoint = tip2tipEmit(proxy, startPoint, (el, point, count) => {
|
|
||||||
let poly = el.poly;
|
|
||||||
if (poly.last() === point) {
|
|
||||||
poly.reverse();
|
|
||||||
}
|
|
||||||
poly.forEachPoint((p, i) => {
|
|
||||||
let dist = lp.distTo2D(p);
|
|
||||||
let rdst = dist > retractDist;
|
|
||||||
let itop = rdst && intersectsTop(lp,p);
|
|
||||||
let emit = extrude;
|
|
||||||
// retract if dist trigger and crosses a slice top polygon
|
|
||||||
if (i === 0) {
|
|
||||||
if (itop) {
|
|
||||||
retract();
|
|
||||||
emit = 0;
|
|
||||||
} else if (dist > nozzleSize) {
|
|
||||||
emit = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// let emit = i === 0 ? 0 : extrude;
|
|
||||||
// handle shallow cloned infill
|
|
||||||
if (poly.z !== undefined) {
|
|
||||||
p = p.clone().setZ(poly.z);
|
|
||||||
}
|
|
||||||
scope.addOutput(preout, p, emit, speed || printSpeed, extruder);
|
|
||||||
lp = p;
|
|
||||||
}, !poly.open);
|
|
||||||
return lp;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function outputThin(lines) {
|
|
||||||
if (!lines) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let points = lines.group(2).map(grp => {
|
|
||||||
let [ p1, p2 ] = grp;
|
|
||||||
return newPoint(
|
|
||||||
(p1.x + p2.x) / 2,
|
|
||||||
(p1.y + p2.y) / 2,
|
|
||||||
(p1.z + p2.z) / 2
|
|
||||||
)
|
|
||||||
});
|
|
||||||
let order = util.orderClosest(points, (p1, p2) => p1.distTo2D(p2));
|
|
||||||
if (order.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let first = points[0];
|
|
||||||
let last = first;
|
|
||||||
scope.addOutput(preout, first, 0, moveSpeed, extruder);
|
|
||||||
for (let p of order) {
|
|
||||||
let dist = last ? last.distTo2D(p) : 0;
|
|
||||||
if (dist > thinWall) {
|
|
||||||
retract();
|
|
||||||
scope.addOutput(preout, p, 0, moveSpeed, extruder);
|
|
||||||
}
|
|
||||||
scope.addOutput(preout, p, 1, fillSpeed, extruder);
|
|
||||||
last = p;
|
|
||||||
}
|
|
||||||
// close a circle
|
|
||||||
if (last && last.distTo2D(first) <= thinWall) {
|
|
||||||
scope.addOutput(preout, first, 1, fillSpeed, extruder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function outputFills(lines, opt = {}) {
|
|
||||||
if (!lines || lines.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let p, p1, p2, dist, len, found, group, mindist, t1, t2,
|
|
||||||
marked = 0,
|
|
||||||
start = 0,
|
|
||||||
skip = false,
|
|
||||||
lastIndex = -1,
|
|
||||||
flow = opt.flow || 1,
|
|
||||||
near = opt.near || false,
|
|
||||||
fast = opt.fast || false,
|
|
||||||
fill = (opt.fill >= 0 ? opt.fill : fillMult) * flow,
|
|
||||||
thinDist = near ? thinWall : thinWall;
|
|
||||||
|
|
||||||
while (lines && marked < lines.length) {
|
|
||||||
group = null;
|
|
||||||
found = false;
|
|
||||||
mindist = Infinity;
|
|
||||||
|
|
||||||
// use next nearest line strategy
|
|
||||||
if (near)
|
|
||||||
for (i=0; i<lines.length; i += 2) {
|
|
||||||
t1 = lines[i];
|
|
||||||
if (t1.del) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
t2 = lines[i+1];
|
|
||||||
let d1 = t1.distToSq2D(startPoint);
|
|
||||||
let d2 = t2.distToSq2D(startPoint);
|
|
||||||
if (d1 < mindist || d2 < mindist) {
|
|
||||||
if (d2 < d1) {
|
|
||||||
p2 = t1;
|
|
||||||
p1 = t2;
|
|
||||||
} else {
|
|
||||||
p1 = t1;
|
|
||||||
p2 = t2;
|
|
||||||
}
|
|
||||||
mindist = Math.min(d1, d2);
|
|
||||||
lastIndex = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use next index line strategy
|
|
||||||
// order all points by distance to last point
|
|
||||||
if (!near)
|
|
||||||
for (i=start; i<lines.length; i += 2) {
|
|
||||||
p = lines[i];
|
|
||||||
if (p.del) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (group === null && p.index > lastIndex) {
|
|
||||||
group = p.index;
|
|
||||||
}
|
|
||||||
if (group !== null) {
|
|
||||||
if (p.index !== group) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (p.index % 2 === 0) {
|
|
||||||
t1 = lines[i];
|
|
||||||
t2 = lines[i+1];
|
|
||||||
} else {
|
|
||||||
t2 = lines[i];
|
|
||||||
t1 = lines[i+1];
|
|
||||||
}
|
|
||||||
dist = Math.min(t1.distTo2D(startPoint), t2.distTo2D(startPoint));
|
|
||||||
if (dist < mindist) {
|
|
||||||
p1 = t1;
|
|
||||||
p2 = t2;
|
|
||||||
mindist = dist;
|
|
||||||
}
|
|
||||||
start = i;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// go back to start and try again
|
|
||||||
if (!near && !found) {
|
|
||||||
if (start === 0 && lastIndex === -1) {
|
|
||||||
console.log('infinite loop', lines, {
|
|
||||||
marked, i, group, start, lastIndex,
|
|
||||||
points: lines.map(p => p.index).join(', ')
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
start = 0;
|
|
||||||
lastIndex = -1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
dist = startPoint.distToSq2D(p1);
|
|
||||||
len = p1.distToSq2D(p2);
|
|
||||||
|
|
||||||
// go back to start when dist > retractDist
|
|
||||||
if (!near && !fast && !skip && dist > retractDist) {
|
|
||||||
skip = true;
|
|
||||||
start = 0;
|
|
||||||
lastIndex = -1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
skip = false;
|
|
||||||
|
|
||||||
// mark as used (temporarily)
|
|
||||||
p1.del = true;
|
|
||||||
p2.del = true;
|
|
||||||
marked += 2;
|
|
||||||
lastIndex = p1.index;
|
|
||||||
|
|
||||||
// if dist to new segment is less than thinWall
|
|
||||||
// and segment length is less than thinWall then
|
|
||||||
// just extrude to midpoint of next segment. this is
|
|
||||||
// to avoid shaking the printer to death.
|
|
||||||
if (dist <= thinDist && len <= thinDist) {
|
|
||||||
p2 = p1.midPointTo(p2);
|
|
||||||
// this.addOutput(preout, p2, fill * (dist / thinWall), fillSpeed, extruder);
|
|
||||||
scope.addOutput(preout, p2, fill, fillSpeed, extruder);
|
|
||||||
} else {
|
|
||||||
// retract if dist trigger or crosses a slice top polygon
|
|
||||||
if (!fast && dist > retractDist && (zhop || intersectsTop(startPoint, p1))) {
|
|
||||||
retract();
|
|
||||||
}
|
|
||||||
|
|
||||||
// anti-backlash on longer move
|
|
||||||
if (!fast && antiBacklash && dist > retractDist) {
|
|
||||||
scope.addOutput(preout, p1.add({x:antiBacklash,y:-antiBacklash,z:0}), 0, moveSpeed, extruder);
|
|
||||||
}
|
|
||||||
|
|
||||||
// bridge ends of fill when they're close together
|
|
||||||
if (dist < thinDist) {
|
|
||||||
scope.addOutput(preout, p1, fill, fillSpeed, extruder);
|
|
||||||
} else {
|
|
||||||
scope.addOutput(preout, p1, 0, moveSpeed, extruder);
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.addOutput(preout, p2, fill, fillSpeed, extruder);
|
|
||||||
}
|
|
||||||
|
|
||||||
startPoint = p2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear delete marks so we can re-print later
|
|
||||||
if (lines) lines.forEach(p => { p.del = false });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* given array of polygons, emit them in next closest order with
|
|
||||||
* the special exception that depth is considered into distance
|
|
||||||
* so that inner polygons are emitted first.
|
|
||||||
*
|
|
||||||
* @param {Array} array of Polygons or Polygon wrappers (tops)
|
|
||||||
* @param {Function} fn call to emit next candidate
|
|
||||||
* @param {Function} fnp convert 'next' object into a Polygon for closeness
|
|
||||||
*/
|
|
||||||
function outputOrderClosest(array, fn, fnp) {
|
|
||||||
if (array.length === 1) {
|
|
||||||
return fn(array[0]);
|
|
||||||
}
|
|
||||||
array = array.slice();
|
|
||||||
let closest, find, next, order, poly, lastDepth = 0;
|
|
||||||
for (;;) {
|
|
||||||
order = [];
|
|
||||||
closest = null;
|
|
||||||
for (i=0; i<array.length; i++) {
|
|
||||||
next = array[i];
|
|
||||||
if (!next) continue;
|
|
||||||
poly = fnp ? fnp(next) : next;
|
|
||||||
find = poly.findClosestPointTo(startPoint);
|
|
||||||
order.push({
|
|
||||||
i: i,
|
|
||||||
n: next,
|
|
||||||
d: find.distance - (poly.depth * thinWall),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (order.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
order.sort((a,b) => {
|
|
||||||
return a.d - b.d;
|
|
||||||
});
|
|
||||||
array[order[0].i] = null;
|
|
||||||
fn(order[0].n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let out = [];
|
|
||||||
if (slice.tops) {
|
|
||||||
out.appendAll(slice.tops);
|
|
||||||
};
|
|
||||||
if (opt.support && slice.supports) {
|
|
||||||
out.appendAll(slice.supports);
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastTop = null;
|
|
||||||
outputOrderClosest(out, function(next) {
|
|
||||||
if (next instanceof Polygon) {
|
|
||||||
scope.setType('support');
|
|
||||||
// support polygon
|
|
||||||
next.setZ(z);
|
|
||||||
outputTraces([next].appendAll(next.inner || []));
|
|
||||||
if (next.fill) {
|
|
||||||
next.fill.forEach(p => { p.z = z });
|
|
||||||
outputFills(next.fill, {fast: true});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
scope.setType('shells');
|
|
||||||
if (lastTop && lastTop !== next) {
|
|
||||||
retract();
|
|
||||||
}
|
|
||||||
|
|
||||||
// control of layer start point
|
|
||||||
switch (process.sliceLayerStart) {
|
|
||||||
case "center":
|
|
||||||
startPoint = newPoint(0,0,startPoint.z);
|
|
||||||
break;
|
|
||||||
case "origin":
|
|
||||||
startPoint = origin.clone();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// optimize start point on belt for tops touching belt
|
|
||||||
// and enforce optimal shell order (outer first)
|
|
||||||
if (isBelt && opt.onBelt) {
|
|
||||||
startPoint = startClone;
|
|
||||||
if (beltFirst) {
|
|
||||||
shellOrder = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// innermost shells
|
|
||||||
let inner = next.innerShells() || [];
|
|
||||||
|
|
||||||
// output inner polygons
|
|
||||||
if (shellOrder === 1) outputTraces(inner, { sort: shellOrder });
|
|
||||||
|
|
||||||
outputTraces(next.shells, { sort: shellOrder });
|
|
||||||
|
|
||||||
// output outer polygons
|
|
||||||
if (shellOrder === -1) outputTraces(inner, { sort: shellOrder });
|
|
||||||
|
|
||||||
// output thin fill
|
|
||||||
scope.setType('thin fill');
|
|
||||||
outputThin(next.thin_fill);
|
|
||||||
|
|
||||||
// then output solid and sparse fill
|
|
||||||
scope.setType('solid fill');
|
|
||||||
outputFills(next.fill_lines, {flow: solidWidth});
|
|
||||||
|
|
||||||
scope.setType('sparse infill');
|
|
||||||
outputSparse(next.fill_sparse, sparseMult, infillSpeed);
|
|
||||||
|
|
||||||
lastTop = next;
|
|
||||||
}
|
|
||||||
}, function(obj) {
|
|
||||||
// for tops
|
|
||||||
return obj instanceof Polygon ? obj : obj.poly;
|
|
||||||
});
|
|
||||||
|
|
||||||
// produce polishing paths when present
|
|
||||||
if (slice.tops.length && slice.tops[0].polish) {
|
|
||||||
let {x,y} = slice.tops[0].polish;
|
|
||||||
if (x) {
|
|
||||||
outputSparse(x, 0, process.polishSpeed);
|
|
||||||
}
|
|
||||||
if (y) {
|
|
||||||
outputSparse(y, 0, process.polishSpeed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// offset print points
|
|
||||||
for (i=0; i<preout.length; i++) {
|
|
||||||
preout[i].point = preout[i].point.add(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add offset points to total print
|
|
||||||
this.addPrintPoints(preout, output, origin, extruder);
|
|
||||||
|
|
||||||
return startPoint.add(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
parseSVG(code, offset) {
|
parseSVG(code, offset) {
|
||||||
let scope = this,
|
let scope = this,
|
||||||
svg = new DOMParser().parseFromString(code, 'text/xml'),
|
svg = new DOMParser().parseFromString(code, 'text/xml'),
|
||||||
|
|
@ -890,7 +260,6 @@ class Print {
|
||||||
|
|
||||||
const output = scope.output = [ seq ];
|
const output = scope.output = [ seq ];
|
||||||
const beltaxis = { X: "X", Y: "Z", Z: "Y", E: "E", F: "F" };
|
const beltaxis = { X: "X", Y: "Z", Z: "Y", E: "E", F: "F" };
|
||||||
const beltfact = Math.cos(Math.PI / 4);
|
|
||||||
|
|
||||||
function LOG() {
|
function LOG() {
|
||||||
console.log(...[...arguments].map(o => Object.clone(o)));
|
console.log(...[...arguments].map(o => Object.clone(o)));
|
||||||
|
|
@ -1181,10 +550,6 @@ class Output {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function pref(a,b) {
|
|
||||||
return a !== undefined ? a : b;
|
|
||||||
}
|
|
||||||
|
|
||||||
kiri.Print = Print;
|
kiri.Print = Print;
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue