594 lines
23 KiB
JavaScript
594 lines
23 KiB
JavaScript
$(function() {
|
|
function GcodeViewModel(parameters) {
|
|
var self = this;
|
|
|
|
self.loginState = parameters[0];
|
|
self.settings = parameters[1];
|
|
|
|
self.ui_progress_percentage = ko.observable();
|
|
self.ui_progress_type = ko.observable();
|
|
self.ui_progress_text = ko.pureComputed(function() {
|
|
var text = "";
|
|
switch (self.ui_progress_type()) {
|
|
case "loading": {
|
|
text = gettext("Loading...") + " (" + self.ui_progress_percentage().toFixed(0) + "%)";
|
|
break;
|
|
}
|
|
case "analyzing": {
|
|
text = gettext("Analyzing...") + " (" + self.ui_progress_percentage().toFixed(0) + "%)";
|
|
break;
|
|
}
|
|
case "done": {
|
|
text = gettext("Analyzed");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return text;
|
|
});
|
|
self.ui_modelInfo = ko.observable("");
|
|
self.ui_layerInfo = ko.observable("");
|
|
|
|
self.tabActive = false;
|
|
self.enableReload = ko.observable(false);
|
|
|
|
self.waitForApproval = ko.observable(false);
|
|
self.selectedFile = {
|
|
name: ko.observable(undefined),
|
|
date: ko.observable(undefined),
|
|
size: ko.observable(undefined)
|
|
};
|
|
|
|
self.renderer_centerModel = ko.observable(false);
|
|
self.renderer_centerViewport = ko.observable(false);
|
|
self.renderer_zoomOnModel = ko.observable(false);
|
|
self.renderer_showMoves = ko.observable(true);
|
|
self.renderer_showRetracts = ko.observable(true);
|
|
self.renderer_extrusionWidthEnabled = ko.observable(false);
|
|
self.renderer_extrusionWidth = ko.observable(2);
|
|
self.renderer_showNext = ko.observable(false);
|
|
self.renderer_showPrevious = ko.observable(false);
|
|
self.renderer_syncProgress = ko.observable(true);
|
|
|
|
self.reader_sortLayers = ko.observable(true);
|
|
self.reader_hideEmptyLayers = ko.observable(true);
|
|
|
|
self.layerSelectionEnabled = ko.observable(false)
|
|
|
|
self.synchronizeOptions = function(additionalRendererOptions, additionalReaderOptions) {
|
|
var renderer = {
|
|
moveModel: self.renderer_centerModel(),
|
|
centerViewport: self.renderer_centerViewport(),
|
|
showMoves: self.renderer_showMoves(),
|
|
showRetracts: self.renderer_showRetracts(),
|
|
extrusionWidth: self.renderer_extrusionWidthEnabled() ? self.renderer_extrusionWidth() : 1,
|
|
showNextLayer: self.renderer_showNext(),
|
|
showPreviousLayer: self.renderer_showPrevious(),
|
|
zoomInOnModel: self.renderer_zoomOnModel(),
|
|
onInternalOptionChange: self._onInternalRendererOptionChange
|
|
};
|
|
if (additionalRendererOptions) {
|
|
_.extend(renderer, additionalRendererOptions);
|
|
}
|
|
|
|
var reader = {
|
|
sortLayers: self.reader_sortLayers(),
|
|
purgeEmptyLayers: self.reader_hideEmptyLayers()
|
|
};
|
|
if (additionalReaderOptions) {
|
|
_.extend(reader, additionalReaderOptions);
|
|
}
|
|
|
|
GCODE.ui.updateOptions({
|
|
renderer: renderer,
|
|
reader: reader
|
|
});
|
|
};
|
|
|
|
// subscribe to update Gcode view on updates...
|
|
self.renderer_centerModel.subscribe(self.synchronizeOptions);
|
|
self.renderer_centerViewport.subscribe(self.synchronizeOptions);
|
|
self.renderer_zoomOnModel.subscribe(self.synchronizeOptions);
|
|
self.renderer_showMoves.subscribe(self.synchronizeOptions);
|
|
self.renderer_showRetracts.subscribe(self.synchronizeOptions);
|
|
self.renderer_extrusionWidthEnabled.subscribe(self.synchronizeOptions);
|
|
self.renderer_extrusionWidth.subscribe(self.synchronizeOptions);
|
|
self.renderer_showNext.subscribe(self.synchronizeOptions);
|
|
self.renderer_showPrevious.subscribe(self.synchronizeOptions);
|
|
self.reader_sortLayers.subscribe(self.synchronizeOptions);
|
|
self.reader_hideEmptyLayers.subscribe(self.synchronizeOptions);
|
|
|
|
// subscribe to relevant printer settings...
|
|
self.settings.printerProfiles.currentProfileData.subscribe(function() {
|
|
if (!self.enabled) return;
|
|
|
|
var currentProfileData = self.settings.printerProfiles.currentProfileData();
|
|
if (!currentProfileData) return;
|
|
|
|
var toolOffsets = self._retrieveToolOffsets(currentProfileData);
|
|
if (toolOffsets) {
|
|
GCODE.ui.updateOptions({
|
|
reader: {
|
|
toolOffsets: toolOffsets
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
var bedDimensions = self._retrieveBedDimensions(currentProfileData);
|
|
if (toolOffsets) {
|
|
GCODE.ui.updateOptions({
|
|
renderer: {
|
|
bed: bedDimensions
|
|
}
|
|
});
|
|
}
|
|
|
|
var axesConfiguration = self._retrieveAxesConfiguration(currentProfileData);
|
|
if (axesConfiguration) {
|
|
GCODE.ui.updateOptions({
|
|
renderer: {
|
|
invertAxes: axesConfiguration
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
self._retrieveBedDimensions = function(currentProfileData) {
|
|
if (currentProfileData == undefined) {
|
|
currentProfileData = self.settings.printerProfiles.currentProfileData();
|
|
}
|
|
|
|
if (currentProfileData && currentProfileData.volume && currentProfileData.volume.formFactor() && currentProfileData.volume.width() && currentProfileData.volume.depth()) {
|
|
var x = undefined, y = undefined, r = undefined, circular = false, centeredOrigin = false;
|
|
|
|
var formFactor = currentProfileData.volume.formFactor();
|
|
if (formFactor == "circular") {
|
|
r = currentProfileData.volume.width() / 2;
|
|
circular = true;
|
|
centeredOrigin = true;
|
|
} else {
|
|
x = currentProfileData.volume.width();
|
|
y = currentProfileData.volume.depth();
|
|
if (currentProfileData.volume.origin) {
|
|
centeredOrigin = currentProfileData.volume.origin() == "center";
|
|
}
|
|
}
|
|
|
|
return {
|
|
x: x,
|
|
y: y,
|
|
r: r,
|
|
circular: circular,
|
|
centeredOrigin: centeredOrigin
|
|
};
|
|
} else {
|
|
return undefined;
|
|
}
|
|
};
|
|
|
|
self._retrieveToolOffsets = function(currentProfileData) {
|
|
if (currentProfileData == undefined) {
|
|
currentProfileData = self.settings.printerProfiles.currentProfileData();
|
|
}
|
|
|
|
if (currentProfileData && currentProfileData.extruder && currentProfileData.extruder.offsets()) {
|
|
var offsets = [];
|
|
_.each(currentProfileData.extruder.offsets(), function(offset) {
|
|
offsets.push({x: offset[0], y: offset[1]})
|
|
});
|
|
return offsets;
|
|
} else {
|
|
return undefined;
|
|
}
|
|
|
|
};
|
|
|
|
self._retrieveAxesConfiguration = function(currentProfileData) {
|
|
if (currentProfileData == undefined) {
|
|
currentProfileData = self.settings.printerProfiles.currentProfileData();
|
|
}
|
|
|
|
if (currentProfileData && currentProfileData.axes) {
|
|
var invertX = false, invertY = false;
|
|
if (currentProfileData.axes.x) {
|
|
invertX = currentProfileData.axes.x.inverted();
|
|
}
|
|
if (currentProfileData.axes.y) {
|
|
invertY = currentProfileData.axes.y.inverted();
|
|
}
|
|
|
|
return {
|
|
x: invertX,
|
|
y: invertY
|
|
}
|
|
} else {
|
|
return undefined;
|
|
}
|
|
};
|
|
|
|
self.loadedFilename = undefined;
|
|
self.loadedFileDate = undefined;
|
|
self.status = 'idle';
|
|
self.enabled = false;
|
|
|
|
self.currentlyPrinting = false;
|
|
|
|
self.errorCount = 0;
|
|
|
|
self.layerSlider = undefined;
|
|
self.layerCommandSlider = undefined;
|
|
|
|
self.currentLayer = undefined;
|
|
self.currentCommand = undefined;
|
|
|
|
self.initialize = function() {
|
|
var layerSliderElement = $("#gcode_slider_layers");
|
|
var commandSliderElement = $("#gcode_slider_commands");
|
|
var containerElement = $("#gcode_canvas");
|
|
|
|
if (!(layerSliderElement.length && commandSliderElement.length && containerElement.length)) {
|
|
return;
|
|
}
|
|
|
|
self._configureLayerSlider(layerSliderElement);
|
|
self._configureLayerCommandSlider(commandSliderElement);
|
|
|
|
self.settings.requestData()
|
|
.done(function() {
|
|
GCODE.ui.init({
|
|
container: "#gcode_canvas",
|
|
onProgress: self._onProgress,
|
|
onModelLoaded: self._onModelLoaded,
|
|
onLayerSelected: self._onLayerSelected,
|
|
bed: self._retrieveBedDimensions(),
|
|
toolOffsets: self._retrieveToolOffsets(),
|
|
invertAxes: self._retrieveAxesConfiguration()
|
|
});
|
|
self.synchronizeOptions();
|
|
self.enabled = true;
|
|
});
|
|
};
|
|
|
|
self.reset = function() {
|
|
self.enableReload(false);
|
|
self.loadedFilename = undefined;
|
|
self.loadedFileDate = undefined;
|
|
self.clear();
|
|
};
|
|
|
|
self.clear = function() {
|
|
GCODE.ui.clear();
|
|
};
|
|
|
|
self._configureLayerSlider = function(layerSliderElement) {
|
|
self.layerSlider = layerSliderElement.slider({
|
|
id: "gcode_layer_slider",
|
|
reversed: true,
|
|
selection: "after",
|
|
orientation: "vertical",
|
|
min: 0,
|
|
max: 1,
|
|
step: 1,
|
|
value: 0,
|
|
enabled: false,
|
|
formatter: function(value) { return "Layer #" + (value + 1); }
|
|
}).on("slide", self.changeLayer);
|
|
};
|
|
|
|
self._configureLayerCommandSlider = function(commandSliderElement) {
|
|
self.layerCommandSlider = commandSliderElement.slider({
|
|
id: "gcode_command_slider",
|
|
orientation: "horizontal",
|
|
min: 0,
|
|
max: 1,
|
|
step: 1,
|
|
value: [0, 1],
|
|
enabled: false,
|
|
tooltip: "hide"
|
|
}).on("slide", self.changeCommandRange);
|
|
};
|
|
|
|
self.loadFile = function(filename, date){
|
|
self.enableReload(false);
|
|
if (self.status == "idle" && self.errorCount < 3) {
|
|
self.status = "request";
|
|
OctoPrint.files.download("local", filename)
|
|
.done(function(response, rstatus) {
|
|
if(rstatus === 'success'){
|
|
self.showGCodeViewer(response, rstatus);
|
|
self.loadedFilename = filename;
|
|
self.loadedFileDate = date;
|
|
self.status = "idle";
|
|
self.enableReload(true);
|
|
}
|
|
})
|
|
.fail(function() {
|
|
self.status = "idle";
|
|
self.errorCount++;
|
|
});
|
|
}
|
|
};
|
|
|
|
self.showGCodeViewer = function(response, rstatus) {
|
|
var par = {
|
|
target: {
|
|
result: response
|
|
}
|
|
};
|
|
GCODE.gCodeReader.loadFile(par);
|
|
|
|
if (self.layerSlider != undefined) {
|
|
self.layerSlider.slider("disable");
|
|
}
|
|
if (self.layerCommandSlider != undefined) {
|
|
self.layerCommandSlider.slider("disable");
|
|
}
|
|
};
|
|
|
|
self.reload = function() {
|
|
if (!self.enableReload()) return;
|
|
self.loadFile(self.loadedFilename, self.loadedFileDate);
|
|
};
|
|
|
|
self.fromHistoryData = function(data) {
|
|
self._processData(data);
|
|
};
|
|
|
|
self.fromCurrentData = function(data) {
|
|
self._processData(data);
|
|
};
|
|
|
|
self._renderPercentage = function(percentage) {
|
|
var cmdIndex = GCODE.gCodeReader.getCmdIndexForPercentage(percentage);
|
|
if (!cmdIndex) return;
|
|
|
|
GCODE.renderer.render(cmdIndex.layer, 0, cmdIndex.cmd);
|
|
GCODE.ui.updateLayerInfo(cmdIndex.layer);
|
|
|
|
if (self.layerSlider != undefined) {
|
|
self.layerSlider.slider("setValue", cmdIndex.layer);
|
|
}
|
|
if (self.layerCommandSlider != undefined) {
|
|
self.layerCommandSlider.slider("setValue", [0, cmdIndex.cmd]);
|
|
}
|
|
};
|
|
|
|
self._processData = function(data) {
|
|
if (!data.job.file || !data.job.file.name && (self.loadedFilename || self.loadedFileDate)) {
|
|
self.waitForApproval(false);
|
|
|
|
self.loadedFilename = undefined;
|
|
self.loadedFileDate = undefined;
|
|
self.selectedFile.name(undefined);
|
|
self.selectedFile.date(undefined);
|
|
self.selectedFile.size(undefined);
|
|
|
|
self.clear();
|
|
return;
|
|
}
|
|
if (!self.enabled) return;
|
|
self.currentlyPrinting = data.state.flags && (data.state.flags.printing || data.state.flags.paused);
|
|
|
|
if(self.loadedFilename
|
|
&& self.loadedFilename == data.job.file.name
|
|
&& self.loadedFileDate == data.job.file.date) {
|
|
if (OctoPrint.coreui.browserTabVisible && self.tabActive && self.currentlyPrinting && self.renderer_syncProgress() && !self.waitForApproval()) {
|
|
self._renderPercentage(data.progress.completion);
|
|
}
|
|
self.errorCount = 0
|
|
} else {
|
|
self.clear();
|
|
if (data.job.file.name && data.job.file.origin != "sdcard"
|
|
&& self.status != "request"
|
|
&& (!self.waitForApproval() || self.selectedFile.name() != data.job.file.name || self.selectedFile.date() != data.job.file.date)) {
|
|
self.selectedFile.name(data.job.file.name);
|
|
self.selectedFile.date(data.job.file.date);
|
|
self.selectedFile.size(data.job.file.size);
|
|
|
|
if (data.job.file.size > CONFIG_GCODE_SIZE_THRESHOLD || ($.browser.mobile && data.job.file.size > CONFIG_GCODE_MOBILE_SIZE_THRESHOLD)) {
|
|
self.waitForApproval(true);
|
|
self.loadedFilename = undefined;
|
|
self.loadedFileDate = undefined;
|
|
} else {
|
|
self.waitForApproval(false);
|
|
self.loadFile(data.job.file.name, data.job.file.date);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
self.onEventPrintDone = function() {
|
|
if (self.renderer_syncProgress() && !self.waitForApproval()) {
|
|
self._renderPercentage(100.0);
|
|
}
|
|
};
|
|
|
|
self.approveLargeFile = function() {
|
|
self.waitForApproval(false);
|
|
self.loadFile(self.selectedFile.name(), self.selectedFile.date());
|
|
};
|
|
|
|
self._onProgress = function(type, percentage) {
|
|
self.ui_progress_type(type);
|
|
self.ui_progress_percentage(percentage);
|
|
};
|
|
|
|
self._onModelLoaded = function(model) {
|
|
if (!model) {
|
|
self.ui_modelInfo("");
|
|
if (self.layerSlider != undefined) {
|
|
self.layerSlider.slider("disable");
|
|
self.layerSlider.slider("setMax", 1);
|
|
self.layerSlider.slider("setValue", 0);
|
|
self.layerSelectionEnabled(false);
|
|
}
|
|
self.currentLayer = 0;
|
|
} else {
|
|
var output = [];
|
|
output.push(gettext("Model size") + ": " + model.width.toFixed(2) + "mm × " + model.depth.toFixed(2) + "mm × " + model.height.toFixed(2) + "mm");
|
|
output.push(gettext("Estimated total print time") + ": " + formatDuration(model.printTime));
|
|
output.push(gettext("Estimated layer height") + ": " + model.layerHeight.toFixed(2) + gettext("mm"));
|
|
output.push(gettext("Layer count") + ": " + model.layersPrinted.toFixed(0) + " " + gettext("printed") + ", " + model.layersTotal.toFixed(0) + " " + gettext("visited"));
|
|
|
|
self.ui_modelInfo(output.join("<br>"));
|
|
|
|
if (self.layerSlider != undefined) {
|
|
self.layerSlider.slider("enable");
|
|
self.layerSlider.slider("setMax", model.layersPrinted - 1);
|
|
self.layerSlider.slider("setValue", 0);
|
|
self.layerSelectionEnabled(true);
|
|
}
|
|
}
|
|
};
|
|
|
|
self._onLayerSelected = function(layer) {
|
|
if (!layer) {
|
|
self.ui_layerInfo("");
|
|
if (self.layerCommandSlider != undefined) {
|
|
self.layerCommandSlider.slider("disable");
|
|
self.layerCommandSlider.slider("setMax", 1);
|
|
self.layerCommandSlider.slider("setValue", [0, 1]);
|
|
}
|
|
self.currentCommand = [0, 1];
|
|
} else {
|
|
var output = [];
|
|
output.push(gettext("Layer number") + ": " + (layer.number + 1));
|
|
output.push(gettext("Layer height") + " (mm): " + layer.height);
|
|
output.push(gettext("GCODE commands in layer") + ": " + layer.commands);
|
|
if (layer.filament != undefined) {
|
|
if (layer.filament.length == 1) {
|
|
output.push(gettext("Filament used by layer") + ": " + layer.filament[0].toFixed(2) + "mm");
|
|
} else {
|
|
for (var i = 0; i < layer.filament.length; i++) {
|
|
output.push(gettext("Filament used by layer") + " (" + gettext("Tool") + " " + i + "): " + layer.filament[i].toFixed(2) + "mm");
|
|
}
|
|
}
|
|
}
|
|
output.push(gettext("Print time for layer") + ": " + formatDuration(layer.printTime));
|
|
|
|
self.ui_layerInfo(output.join("<br>"));
|
|
|
|
if (self.layerCommandSlider != undefined) {
|
|
self.layerCommandSlider.slider("enable");
|
|
self.layerCommandSlider.slider("setMax", layer.commands - 1);
|
|
self.layerCommandSlider.slider("setValue", [0, layer.commands - 1]);
|
|
}
|
|
}
|
|
};
|
|
|
|
self._onInternalRendererOptionChange = function(options) {
|
|
if (!options) return;
|
|
|
|
for (var opt in options) {
|
|
if (opt == "zoomInOnModel" && options[opt] != self.renderer_zoomOnModel()) {
|
|
self.renderer_zoomOnModel(false);
|
|
} else if (opt == "centerViewport" && options[opt] != self.renderer_centerViewport()) {
|
|
self.renderer_centerViewport(false);
|
|
} else if (opt == "moveModel" && options[opt] != self.renderer_centerModel()) {
|
|
self.renderer_centerModel(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
self.changeLayer = function(event) {
|
|
if (self.currentlyPrinting && self.renderer_syncProgress()) self.renderer_syncProgress(false);
|
|
|
|
var value = event.value;
|
|
if (self.currentLayer !== undefined && self.currentLayer == value) return;
|
|
self.currentLayer = value;
|
|
|
|
GCODE.ui.changeSelectedLayer(value);
|
|
};
|
|
|
|
self.onMouseOver = function(data, event) {
|
|
if (!self.settings.feature_keyboardControl() || self.layerSlider != undefined) return;
|
|
$("#canvas_container").focus();
|
|
|
|
};
|
|
self.onMouseOut = function(data, event) {
|
|
if (!self.settings.feature_keyboardControl() || self.layerSlider != undefined) return;
|
|
$("#canvas_container").blur();
|
|
};
|
|
self.onKeyDown = function(data, event) {
|
|
if (!self.settings.feature_keyboardControl() || self.layerSlider != undefined) return;
|
|
|
|
var value = self.currentLayer;
|
|
switch(event.which){
|
|
case 33: // Pg up
|
|
value = value + 10; // No need to check against max this is done by the Slider anyway
|
|
break;
|
|
case 34: // Pg down
|
|
value = value - 10; // No need to check against min, this is done by the Slider anyway
|
|
break;
|
|
case 38: // up arrow key
|
|
value = value + 1; // No need to check against max this is done by the Slider anyway
|
|
break;
|
|
case 40: // down arrow key
|
|
value = value - 1; // No need to check against min, this is done by the Slider anyway
|
|
break;
|
|
}
|
|
self.shiftLayer(value);
|
|
};
|
|
|
|
self.changeCommandRange = function(event) {
|
|
if (self.currentlyPrinting && self.renderer_syncProgress()) self.renderer_syncProgress(false);
|
|
|
|
var tuple = event.value;
|
|
if (self.currentCommand !== undefined && self.currentCommand[0] == tuple[0] && self.currentCommand[1] == tuple[1]) return;
|
|
self.currentCommand = tuple;
|
|
|
|
GCODE.ui.changeSelectedCommands(self.layerSlider.slider("getValue"), tuple[0], tuple[1]);
|
|
};
|
|
|
|
self.onDataUpdaterReconnect = function() {
|
|
self.reset();
|
|
};
|
|
|
|
self.onBeforeBinding = function() {
|
|
self.initialize();
|
|
};
|
|
|
|
self.onTabChange = function(current, previous) {
|
|
self.tabActive = current == "#gcode";
|
|
};
|
|
|
|
self.shiftLayer = function(value){
|
|
if (value != self.currentLayer) {
|
|
event.preventDefault();
|
|
|
|
self.layerSlider.slider('setValue', value);
|
|
value = self.layerSlider.slider('getValue');
|
|
//This sets the srollbar to the appropriate position.
|
|
self.layerSlider
|
|
.trigger({
|
|
type: 'slideStart',
|
|
value: value
|
|
})
|
|
.trigger({
|
|
type: 'slide',
|
|
value: value
|
|
}).trigger({
|
|
type: 'slideStop',
|
|
value: value
|
|
});
|
|
}
|
|
};
|
|
|
|
self.incrementLayer = function() {
|
|
var value = self.layerSlider.slider('getValue')+1;
|
|
self.shiftLayer(value);
|
|
};
|
|
|
|
self.decrementLayer = function() {
|
|
var value = self.layerSlider.slider('getValue')-1;
|
|
self.shiftLayer(value);
|
|
};
|
|
}
|
|
|
|
OCTOPRINT_VIEWMODELS.push([
|
|
GcodeViewModel,
|
|
["loginStateViewModel", "settingsViewModel"],
|
|
"#gcode"
|
|
]);
|
|
});
|