checkpoint new support / rotation work

This commit is contained in:
Stewart Allen 2021-01-24 00:13:09 -05:00
commit dba922753c
9 changed files with 250 additions and 158 deletions

View file

@ -51,8 +51,9 @@
# FDM - BELT # FDM - BELT
* `B` layer retract broken * `B` layer retract broken?
* `B` finish supports for belt printing. clip below belt. add progress. * `B` infill / thin-fill is generating tons of small linear segments
* `B` finish supports for belt printing. add progress.
# CAM # CAM

View file

@ -70,7 +70,10 @@
// return cached or refreshed (when update = true) bounding box // return cached or refreshed (when update = true) bounding box
GP.getBoundingBox = function(update) { GP.getBoundingBox = function(update) {
if (update || !this.boundingBox) this.computeBoundingBox(); if (update || !this.boundingBox) {
this.boundingBox = null;
this.computeBoundingBox();
}
this.boundingBox.dim = this.boundingBox.max.clone().sub(this.boundingBox.min); this.boundingBox.dim = this.boundingBox.max.clone().sub(this.boundingBox.min);
return this.boundingBox; return this.boundingBox;
}; };

View file

@ -123,17 +123,18 @@ KIRI.work = {
}, },
// widget sync // widget sync
sync: function(widget) { sync: function() {
let widgets = widget ? [ widget ] : KIRI.api.widgets.all(); let widgets = KIRI.api.widgets.all();
widgets.forEach(widget => { widgets.forEach(widget => {
if (widget.modified || !syncd[widget.id]) { if (widget.modified || !syncd[widget.id]) {
syncd[widget.id] = true; syncd[widget.id] = true;
let vertices = widget.getGeoVertices().buffer.slice(0); let vertices = widget.getGeoVertices().buffer.slice(0);
send("sync", { send("sync", {
id: widget.id, id: widget.id,
group: widget.group.id,
track: widget.track,
vertices: vertices, vertices: vertices,
position: widget.mesh.position, position: widget.mesh.position,
tracking: widget.track
}, done => { }, done => {
widget.modified = false; widget.modified = false;
}, [vertices]); }, [vertices]);
@ -141,8 +142,31 @@ KIRI.work = {
}); });
}, },
rotate: function(settings, callback) {
send("rotate", { settings }, reply => {
if (reply.group) {
for (let widget of KIRI.Widget.Groups.forid(reply.group)) {
widget.belt = reply.belt;
}
} else if (callback) {
callback();
}
});
},
unrotate: function(settings, callback) {
send("unrotate", { settings }, reply => {
if (reply.group) {
for (let widget of KIRI.Widget.Groups.forid(reply.group)) {
widget.rotinfo = reply.rotinfo;
}
} else if (callback) {
callback();
}
});
},
slice: function(settings, widget, callback) { slice: function(settings, widget, callback) {
CLIENT.sync(widget);
slicing[widget.id] = callback; slicing[widget.id] = callback;
send("slice", { send("slice", {

View file

@ -355,7 +355,9 @@
return map; return map;
}, },
new: newWidget, new: newWidget,
all: function() { return WIDGETS.slice() }, all: () => { return WIDGETS.slice() },
add: (widget) => { WIDGETS.push(widget) },
filter: (fn) => { WIDGETS = WIDGETS.filter(fn) },
for: forAllWidgets, for: forAllWidgets,
load: Widget.loadFromCatalog, load: Widget.loadFromCatalog,
meshes: meshArray, meshes: meshArray,
@ -858,6 +860,9 @@
setOpacity(color.slicing_opacity); setOpacity(color.slicing_opacity);
STACKS.clear(); STACKS.clear();
KIRI.client.clear();
KIRI.client.sync();
KIRI.client.rotate(settings);
// for each widget, slice // for each widget, slice
forAllWidgets(function(widget) { forAllWidgets(function(widget) {
@ -867,11 +872,12 @@
startTime = Date.now(), startTime = Date.now(),
lastMsg, lastMsg,
camOrLaser = mode === 'CAM' || mode === 'LASER', camOrLaser = mode === 'CAM' || mode === 'LASER',
stack = STACKS.create(widget.id, widget.mesh); stack = widget.stack = STACKS.create(widget.id, widget.mesh);
widget.stats.progress = 0; widget.stats.progress = 0;
widget.setColor(color.slicing); widget.setColor(color.slicing);
widget.slice(settings, function(sliced, error) { widget.slice(settings, function(sliced, error) {
widget.rotinfo = null;
let mark = Date.now(); let mark = Date.now();
// update UI info // update UI info
if (sliced) { if (sliced) {
@ -881,22 +887,26 @@
} }
API.event.emit('slice', getMode()); API.event.emit('slice', getMode());
} }
// on the last exit, update ui and call the callback
if (--countdown === 0 || error || errored) {
KIRI.client.unrotate(settings, () => {
forAllWidgets(widget => {
// on done // on done
segtimes[`${segNumber}_draw`] = widget.render(stack); segtimes[`${segNumber}_draw`] = widget.render(stack);
// rotate stack for belt beds // rotate stack for belt beds
if (settings.device.bedBelt && widget.rotinfo) { if (widget.rotinfo) {
stack.obj.rotate(widget.rotinfo); widget.stack.obj.rotate(widget.rotinfo);
} }
updateSliderMax(true);
setVisibleLayer(-1, 0);
if (scale === 1) { if (scale === 1) {
// clear wireframe // clear wireframe
widget.setWireframe(false, color.wireframe, color.wireframe_opacity); widget.setWireframe(false, color.wireframe, color.wireframe_opacity);
widget.setOpacity(camOrLaser ? color.cam_sliced_opacity : color.sliced_opacity); widget.setOpacity(camOrLaser ? color.cam_sliced_opacity : color.sliced_opacity);
widget.setColor(color.deselected); widget.setColor(color.deselected);
} }
// on the last exit, update ui and call the callback });
if (--countdown === 0 || error || errored) { updateSliderMax(true);
setVisibleLayer(-1, 0);
});
// mark slicing complete for prep/preview // mark slicing complete for prep/preview
complete.slice = true; complete.slice = true;
if (scale === 1) { if (scale === 1) {
@ -2077,6 +2087,7 @@
let newWidgets = [], let newWidgets = [],
oldWidgets = js2o(SDB.getItem('ws-widgets'), []); oldWidgets = js2o(SDB.getItem('ws-widgets'), []);
forAllWidgets(function(widget) { forAllWidgets(function(widget) {
if (widget.synth) return;
newWidgets.push(widget.id); newWidgets.push(widget.id);
oldWidgets.remove(widget.id); oldWidgets.remove(widget.id);
widget.saveState(); widget.saveState();

View file

@ -32,6 +32,10 @@
let Group = Widget.Groups = { let Group = Widget.Groups = {
list: () => {
return groups.slice()
},
forid: function(id) { forid: function(id) {
for (let i=0; i<groups.length; i++) { for (let i=0; i<groups.length; i++) {
if (groups[i].id === id) return groups[i]; if (groups[i].id === id) return groups[i];
@ -101,7 +105,7 @@
* @params {String} [id] * @params {String} [id]
* @constructor * @constructor
*/ */
function Widget(id,group) { function Widget(id, group) {
this.id = id || new Date().getTime().toString(36)+(nextId++); this.id = id || new Date().getTime().toString(36)+(nextId++);
this.group = group || []; this.group = group || [];
this.group.push(this); this.group.push(this);
@ -461,11 +465,11 @@
this.modified = true; this.modified = true;
}; };
PRO.rotate = function(x, y, z) { PRO.rotate = function(x, y, z, temp) {
this.group.forEach(w => { this.group.forEach(w => {
w._rotate(x, y, z, false); w._rotate(x, y, z, temp);
}); });
this.center(); this.center(false);
}; };
PRO._rotate = function(x, y, z, temp) { PRO._rotate = function(x, y, z, temp) {

View file

@ -10,7 +10,8 @@ let BASE = self.base,
print: null, print: null,
snap: null snap: null
}, },
cache = {}; wgroup = {},
wcache = {};
// catch clipper alerts and convert to console messages // catch clipper alerts and convert to console messages
self.alert = function(o) { self.alert = function(o) {
@ -24,7 +25,9 @@ BASE.debug.disable();
const dispatch = const dispatch =
KIRI.server = KIRI.server =
KIRI.worker = { KIRI.worker = {
cache: cache, group: wgroup,
cache: wcache,
decimate: function(data, send) { decimate: function(data, send) {
let { vertices, options } = data; let { vertices, options } = data;
@ -38,98 +41,128 @@ KIRI.worker = {
send.done(); send.done();
}, },
clear: function(data, send) {
current.snap = null;
current.print = null;
if (!data.id) {
dispatch.group = wgroup = {};
dispatch.cache = wcache = {};
send.done({ clear: true });
return;
}
let had = wcache[data.id] !== undefined;
delete wcache[data.id];
send.done({
id: data.id,
had: had,
has: wcache[data.id] !== undefined
});
},
// widget sync // widget sync
sync: function(data, send) { sync: function(data, send) {
let group = wgroup[data.group];
if (!group) {
group = [];
group.id = data.group;
wgroup[data.group] = group;
}
let vertices = new Float32Array(data.vertices), let vertices = new Float32Array(data.vertices),
points = BASE.verticesToPoints(vertices, { maxpass: 0 }), widget = KIRI.newWidget(data.id, group).loadVertices(vertices);
widget = KIRI.newWidget(data.id).setPoints(points);
// do it here so cancel can work // do it here so cancel can work
cache[data.id] = widget; wcache[data.id] = widget;
// stored for possible future rotations // stored for possible future rotations
widget.vertices = vertices; widget.vertices = vertices;
// restore tracking object
// fake mesh object to satisfy printing widget.track = data.track;
widget.track = data.tracking;
widget.mesh = {
widget: widget,
position: data.position
};
send.done(data.id); send.done(data.id);
}, },
slice: function(data, send) { rotate: function(data, send) {
let { id, settings } = data; let { settings } = data;
if (!settings.device.bedBelt) {
return send.done({});
}
send.data({update:0.05, updateStatus:"slicing"}); function mins(vert) {
let miny = Infinity, minz = Infinity;
let widget = cache[data.id],
last = time(),
xpos,
ypos,
now;
try {
let rotation = (Math.PI/180) * (settings.device.bedBelt ? 45 : 0);
if (rotation) {
// need min y for post-rotation offset in prepare
let vert = widget.vertices;
let miny = Infinity;
for (let i=0, l=vert.length; i<l; ) { for (let i=0, l=vert.length; i<l; ) {
let x = vert[i++]; let x = vert[i++];
let y = vert[i++]; let y = vert[i++];
let z = vert[i++]; let z = vert[i++];
if (z < 0.01) miny = Math.min(miny, y); miny = Math.min(miny, y);
minz = Math.min(minz, z);
}
return {miny, minz};
} }
let bb1 = widget.getBoundingBox(true); for (let group of Object.values(wgroup)) {
let widget = group[0];
let rotation = (Math.PI / 180) * 45;
// need min y for post-rotation offset in prepare
// let vert = widget.vertices;
// let miny = Infinity;
// for (let i=0, l=vert.length; i<l; ) {
// let x = vert[i++];
// let y = vert[i++];
// let z = vert[i++];
// if (z < 0.01) miny = Math.min(miny, y);
// }
let min1 = mins(widget.vertices);
let miny = min1.miny;
widget.groupBounds();
let track = widget.track; let track = widget.track;
xpos = track.pos.x; let xpos = track.pos.x;
ypos = settings.device.bedDepth / 2 + track.pos.y + miny; let ypos = settings.device.bedDepth / 2 + track.pos.y + miny;
widget.mesh = null; widget.rotate(rotation,0,0,true);
widget.points = null; widget.groupBounds();
// this super ugly hack injects fake support faces
// into the widget mesh so they can be rotated and extracted
let fv = Array.from(widget.vertices);
let fvl = fv.length;
let fixed = Object.values(settings.widget[widget.id].support || {});
for (let col of fixed) {
fv.push(col.x);
fv.push(col.y);
fv.push(col.z);
fv.push(col.x);
fv.push(col.y);
fv.push(col.z);
fv.push(col.x);
fv.push(col.y);
fv.push(col.z);
}
let f2l = fv.length;
let f32 = fv.toFloat32();
// use falsified vertices
widget.loadVertices(f32);
// widget.loadVertices(widget.vertices);
widget._rotate(rotation,0,0,true);
widget.center(false, true);
let bb2 = widget.getBoundingBox(true);
// hack part 2: recover rotated support column mid-points
for (let i=0; i<fixed.length; i++) {
let x = f32[fvl + i*9 + 0];
let y = f32[fvl + i*9 + 1];
let z = f32[fvl + i*9 + 2];
fixed[i].y = y;
fixed[i].z = z;
}
widget.belt = { xpos, ypos }; widget.belt = { xpos, ypos };
for (let others of group.slice(1)) {
others.belt = widget.belt;
} }
send.data({group: group.id, belt: widget.belt});
}
send.done({});
},
unrotate: function(data, send) {
let { settings } = data;
if (!settings.device.bedBelt) {
return send.done({});
}
let rotation = (Math.PI / 180) * 45;
for (let group of Object.values(wgroup)) {
let widget = group[0];
widget.groupBounds();
widget.rotate(-rotation,0,0,true);
let { dy, dz } = widget.track.center;
widget.groupBounds();
let { xpos, ypos } = widget.belt;
widget.rotinfo = { angle: 45, dy, dz, xpos, ypos };
for (let others of group.slice(1)) {
others.rotinfo = widget.rotinfo;
}
send.data({group: group.id, rotinfo: widget.rotinfo});
}
send.done({});
},
slice: function(data, send) {
send.data({update:0.001, updateStatus:"slicing"});
let settings = data.settings,
widget = wcache[data.id],
last = time(),
now;
try {
widget.slice(settings, function(error) { widget.slice(settings, function(error) {
if (error) { if (error) {
send.data({error: error}); send.data({error: error});
@ -145,19 +178,7 @@ KIRI.worker = {
send.data({index: index, slice: slice.encode(state)}, state.zeros); send.data({index: index, slice: slice.encode(state)}, state.zeros);
}) })
send.data({send_end: time()}); send.data({send_end: time()});
// unrotate and send delta coordinates
if (rotation) {
widget.setPoints(null);
widget._rotate(-rotation,0,0,true);
let wbb = widget.getBoundingBox(true);
widget.center(false, true);
let dy = (wbb.max.y + wbb.min.y)/2;
let dz = wbb.min.z;
widget.rotinfo = { angle: 45, dy, dz, xpos, ypos };
send.data({ rotinfo: widget.rotinfo });
} }
}
send.done({done: true}); send.done({done: true});
}, function(update, msg) { }, function(update, msg) {
now = time(); now = time();
@ -166,7 +187,6 @@ KIRI.worker = {
send.data({update: (0.05 + update * 0.95), updateStatus: msg}); send.data({update: (0.05 + update * 0.95), updateStatus: msg});
last = now; last = now;
}); });
} catch (error) { } catch (error) {
send.data({error: error.toString()}); send.data({error: error.toString()});
console.log(error); console.log(error);
@ -175,7 +195,7 @@ KIRI.worker = {
prepare: function(data, send) { prepare: function(data, send) {
// create widget array from id:widget cache // create widget array from id:widget cache
const widgets = Object.values(cache); const widgets = Object.values(wcache);
// let client know we've started // let client know we've started
send.data({update:0.05, updateStatus:"preview"}); send.data({update:0.05, updateStatus:"preview"});
@ -254,7 +274,7 @@ KIRI.worker = {
z: origin.z z: origin.z
}; };
const device = settings.device; const device = settings.device;
const print = current.print = KIRI.newPrint(settings, Object.values(cache)); const print = current.print = KIRI.newPrint(settings, Object.values(wcache));
const tools = device.extruders; const tools = device.extruders;
const mode = settings.mode; const mode = settings.mode;
const thin = settings.controller.lineType === 'line' || mode !== 'FDM'; const thin = settings.controller.lineType === 'line' || mode !== 'FDM';
@ -280,30 +300,13 @@ KIRI.worker = {
out.point = BASE.newPoint(x,y,z || 0); out.point = BASE.newPoint(x,y,z || 0);
}); });
}); });
const print = current.print = KIRI.newPrint(null, Object.values(cache)); const print = current.print = KIRI.newPrint(null, Object.values(wcache));
const layers = KIRI.driver.FDM.prepareRender(parsed, progress => { const layers = KIRI.driver.FDM.prepareRender(parsed, progress => {
send.data({ progress }); send.data({ progress });
}, { thin: true }); }, { thin: true });
send.done({parsed: KIRI.codec.encode(layers)}); send.done({parsed: KIRI.codec.encode(layers)});
}, },
clear: function(data, send) {
current.snap = null;
current.print = null;
if (!data.id) {
cache = {};
send.done({ clear: true });
return;
}
let had = cache[data.id] !== undefined;
delete cache[data.id];
send.done({
id: data.id,
had: had,
has: cache[data.id] !== undefined
});
},
config: function(data, send) { config: function(data, send) {
const update = {}; const update = {};
if (data.base) { if (data.base) {

View file

@ -20,6 +20,18 @@
FDM.init = function(kiri, api) { FDM.init = function(kiri, api) {
API = api; API = api;
SPACE = api.const.SPACE; SPACE = api.const.SPACE;
function filterSynth() {
// remove synth widgets
api.widgets.filter((widget) => {
if (widget.track.synth) {
api.const.SPACE.platform.remove(widget.mesh);
kiri.Widget.Groups.remove(widget);
}
return !widget.track.synth
});
}
api.event.on("mode.set", mode => { api.event.on("mode.set", mode => {
isFdmMode = mode === 'FDM'; isFdmMode = mode === 'FDM';
lastMode = mode; lastMode = mode;
@ -28,6 +40,7 @@
api.event.on("view.set", view => { api.event.on("view.set", view => {
lastView = view; lastView = view;
updateVisiblity(); updateVisiblity();
filterSynth();
}); });
api.event.on("settings.load", (settings) => { api.event.on("settings.load", (settings) => {
if (settings.mode !== 'FDM') return; if (settings.mode !== 'FDM') return;
@ -98,6 +111,37 @@
} }
func.sdone(); func.sdone();
updateVisiblity(); updateVisiblity();
// synth support widget for each widget group
let synth = [];
for (let group of kiri.Widget.Groups.list()) {
let count = 0;
let merged = new THREE.Geometry();
for (let widget of group) {
if (widget.sups) {
for (let sup of Object.values(widget.sups)) {
merged.merge(sup.box.geometry, sup.box.matrix);
count++;
}
}
}
if (!count) {
continue;
}
let bbg = new THREE.BufferGeometry().fromGeometry(merged);
let sw = kiri.newWidget(null, group);
let fwp = group[0].track.pos;
sw.loadGeometry(bbg);
sw._move(fwp.x, fwp.y, fwp.z);
api.widgets.add(sw);
sw.track.synth = true;
api.const.SPACE.platform.add(sw.mesh);
}
});
api.event.on("slice.end", () => {
if (!isFdmMode) {
return;
}
}); });
api.event.on("key.esc", () => { api.event.on("key.esc", () => {
if (!isFdmMode) { if (!isFdmMode) {

View file

@ -89,7 +89,7 @@
// nest and offset tops // nest and offset tops
POLY.nest(tops).forEach(function(poly) { POLY.nest(tops).forEach(function(poly) {
poly.offset(-offset + nozzle / 2).forEach(function(brim) { poly.offset(-offset + nozzle / 2).forEach(function(brim) {
brim.move(widget.mesh.position); brim.move(widget.track.pos);
brims.push(brim); brims.push(brim);
}); });
}); });
@ -290,7 +290,8 @@
// establish offsets // establish offsets
for (let widget of widgets) { for (let widget of widgets) {
let { rotinfo, belt } = widget; let { rotinfo, belt } = widget;
let offset = widget.mesh ? Object.clone(widget.mesh.position) : {x:0, y:0, z:0}; let offset = Object.clone(widget.track.pos);
// let offset = widget.mesh ? Object.clone(widget.mesh.position) : {x:0, y:0, z:0};
if (isBelt) { if (isBelt) {
offset.x = rotinfo.xpos; offset.x = rotinfo.xpos;
offset.y = -belt.midy; offset.y = -belt.midy;

View file

@ -199,8 +199,9 @@
}); });
// fixed supports (generated ahead of time) // fixed supports (generated ahead of time)
const fixed = Object.values(settings.widget[widget.id].support || {}); let wannot = settings.widget[widget.id] || {},
let shadow = null; fixed = Object.values(wannot.support || {}),
shadow = null;
if (fixed.length || spro.sliceSupportEnable) { if (fixed.length || spro.sliceSupportEnable) {
// create shadow. TODO test if any supports set // create shadow. TODO test if any supports set
let alltops = slices.map(slice => slice.topPolys()).flat(); let alltops = slices.map(slice => slice.topPolys()).flat();
@ -284,7 +285,7 @@
doSupportFill(slice, lineWidth, spro.sliceSupportDensity, minArea); doSupportFill(slice, lineWidth, spro.sliceSupportDensity, minArea);
}, "support"); }, "support");
} else if (true) { } else if (true) {
doSupportBelt(slices, spro, fixed, shadow, doupdate); // doSupportBelt(slices, spro, fixed, shadow, doupdate);
} }
// render if not explicitly disabled // render if not explicitly disabled