From 92279fb3a00a57387da168de6cc31e8acc36518d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Mart=C3=ADnez=20Arrieta?= Date: Thu, 12 May 2016 15:40:18 +0200 Subject: [PATCH 1/2] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit cd6688be2691997feb1c76ca785e096779ac51db Author: Javierma87 Date: Thu May 12 15:13:23 2016 +0200 Removed previousE commit 413655f7051012b26406be013db9b4b3697404bc Author: Javierma87 Date: Thu May 12 12:36:54 2016 +0200 Changed to detect in case of two extruders commit b0cfa28eaff7648e9fa1b809122205f5c6a56899 Author: Javierma87 Date: Mon Apr 11 18:11:54 2016 +0200 Identation issues solved and code improved commit 95b6eba2ad034d48c1b56576a2c665b4bb3abf72 Author: Javierma87 Date: Mon Apr 4 18:48:47 2016 +0200 Changed message shown to user using sprintf and improved 'if' checks commit 3baf9d7fc0b05a336e17a3920fd5d267423959b3 Author: Javierma87 Date: Tue Mar 8 17:46:40 2016 +0000 Analysis now done in frontend, taking also into account centre origin commit e742e74475dbacdb54517528e79533fbad8fa188 Author: Javier Martínez Arrieta Date: Sat Mar 5 21:37:02 2016 +0100 Update analysis.py commit 29a821c2f40daa0fe15807d0c12435eef6a63b4f Author: Javierma87 Date: Mon Feb 29 19:11:59 2016 +0000 Removed unrelated folders and files for pull request commit 0edbcf3d3cbe3b6bb3e93c8a65fd7e6cbf8c7c45 Author: Javierma87 Date: Sat Feb 27 10:54:47 2016 +0000 Completed requirements and solutions for Slic3r integration and added requirements for scheduled printing commit a92b7e3adeedbdc1a8f084aca0be248bb713e7c2 Author: Javierma87 Date: Mon Feb 22 11:06:22 2016 +0000 Changed requirements and solution file for Slic3r integration commit 201b5723d9a537262d490a568ce32cbacb872dc9 Author: Javierma87 Date: Thu Feb 18 12:15:54 2016 +0000 Solved problem with character '}' at static/js/viewmodels/file.js commit d89319c2a57d0aecaf96a3d91f58f9585e00fc25 Author: Javierma87 Date: Wed Feb 17 21:04:44 2016 +0000 Completed requirementes to add Slic3r as a plugin commit 9e07a3b7c014fd77720746624c6e8433610b2e81 Merge: caf4e84 6945394 Author: Javierma87 Date: Wed Feb 17 10:03:56 2016 +0000 Added functionality to warn in case object exceeds printing area commit caf4e84ab278455bf969c5630f5d8be11fec0d03 Author: Javierma87 Date: Thu Feb 11 18:32:36 2016 +0000 Added docs-tfg and implemented functionality of warning in case object is bigger than printer area --- src/octoprint/filemanager/analysis.py | 2 + .../static/js/app/viewmodels/files.js | 89 ++++++++++++++++++- src/octoprint/util/gcodeInterpreter.py | 16 +++- 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/src/octoprint/filemanager/analysis.py b/src/octoprint/filemanager/analysis.py index cd4ca7fd..8fed445a 100644 --- a/src/octoprint/filemanager/analysis.py +++ b/src/octoprint/filemanager/analysis.py @@ -260,6 +260,8 @@ class GcodeAnalysisQueue(AbstractAnalysisQueue): self._gcode.load(self._current.absolute_path, self._current.printer_profile, throttle=throttle_callback) result = dict() + result["printingArea"] = {"minX" : self._gcode.minX, "minY" : self._gcode.minY, "minZ" : self._gcode.minZ, + "maxX" : self._gcode.maxX, "maxY" : self._gcode.maxY, "maxZ" : self._gcode.maxZ} if self._gcode.totalMoveTimeMinute: result["estimatedPrintTime"] = self._gcode.totalMoveTimeMinute * 60 if self._gcode.extrusionAmount: diff --git a/src/octoprint/static/js/app/viewmodels/files.js b/src/octoprint/static/js/app/viewmodels/files.js index 30da0ada..ecc660b5 100644 --- a/src/octoprint/static/js/app/viewmodels/files.js +++ b/src/octoprint/static/js/app/viewmodels/files.js @@ -6,6 +6,7 @@ $(function() { self.loginState = parameters[1]; self.printerState = parameters[2]; self.slicing = parameters[3]; + self.printerProfiles=parameters[4]; self.isErrorOrClosed = ko.observable(undefined); self.isOperational = ko.observable(undefined); @@ -423,6 +424,82 @@ $(function() { } } output += gettext("Estimated Print Time") + ": " + formatDuration(data["gcodeAnalysis"]["estimatedPrintTime"]) + "
"; + if (self.loginState.isUser()) + { + var profileItems=self.printerProfiles.profiles.items(); + var count=0; + while(count < profileItems.length && !profileItems[count].current) + { + count++; + } + if (count < profileItems.length) + { + var warn=false; + var volumeInfo=profileItems[count].volume; + var printingArea=data["gcodeAnalysis"]["printingArea"]; + if(volumeInfo.origin=="lowerleft") + { + if(printingArea["maxX"]>volumeInfo.width || printingArea["maxY"]>volumeInfo.depth || + printingArea["maxZ"]>volumeInfo.height) + { + warn=true; + } + } + else //origin is centre + { + if( printingArea["maxX"]>(volumeInfo.width/2) || printingArea["maxY"]>(volumeInfo.depth/2) || + printingArea["maxZ"]>volumeInfo.height || Math.abs(printingArea["minX"])>(volumeInfo.width/2) || + Math.abs(printingArea["minY"])>(volumeInfo.depth/2)) + { + warn=true; + } + } + } + if(warn) + { + var warning = "

" + _.sprintf(gettext("Revise file %s"), data["name"]) + "

"; + var info=""; + + // set print volume boundaries + var boundaries = { + minX : 0, + maxX : volumeInfo.width, + minY : 0, + maxY : volumeInfo.depth, + minZ : 0, + maxZ : volumeInfo.height + }; + if (volumeInfo.origin === "center") { + boundaries["maxX"] = volumeInfo.width / 2; + boundaries["minX"] = -1 * boundaries["maxX"]; + boundaries["maxY"] = volumeInfo.depth / 2; + boundaries["minY"] = -1 * boundaries["maxY"]; + } + + // find exceeded dimensions + if (printingArea["minX"] < boundaries["minX"] || printingArea["maxX"] > boundaries["maxX"]) { + info += _.sprintf(gettext("Profile's width: ( %s, %s) vs object's width: (%s, %s)\n"), boundaries["minX"], + boundaries["maxX"],printingArea["minX"], printingArea["maxX"]); + } + if (printingArea["minY"] < boundaries["minY"] || printingArea["maxY"] > boundaries["maxY"]) { + info += _.sprintf(gettext("Profile's depth: (%s, %s) vs object's depth: (%s, %s)\n"),boundaries["minY"], + boundaries["maxY"], printingArea["minY"], printingArea["maxY"]); + } + if (printingArea["minZ"] < boundaries["minZ"] || printingArea["maxZ"] > boundaries["maxZ"]) { + info += _.sprintf(gettext("Profile's height: (%s, %s) vs object's height: (%s, %s)\n"),boundaries["minZ"], + boundaries["maxZ"], printingArea["minZ"], printingArea["maxZ"]); + } + + //warn user + warning += pnotifyAdditionalInfo("
" + info + "
"); + new PNotify({ + title: "Object exceeds the printing area", + text: warning, + type: "warning", + hide: false + }); + } + } } if (data["prints"] && data["prints"]["last"]) { output += gettext("Last Printed") + ": " + formatTimeAgo(data["prints"]["last"]["date"]) + "
"; @@ -501,8 +578,16 @@ $(function() { self.uploadSdButton = $("#gcode_upload_sd"); if (!self.uploadSdButton.length) { self.uploadSdButton = undefined; + if (_.endsWith(filename.toLowerCase(), ".stl")) { + self.slicing.show(location, filename); + } + + if (data.result.done) { + $("#gcode_upload_progress .bar").css("width", "0%"); + $("#gcode_upload_progress").removeClass("progress-striped").removeClass("active"); + $("#gcode_upload_progress .bar").text(""); + } } - self.uploadProgress = $("#gcode_upload_progress"); self.uploadProgressBar = $(".bar", self.uploadProgress); @@ -708,7 +793,7 @@ $(function() { OCTOPRINT_VIEWMODELS.push([ GcodeFilesViewModel, - ["settingsViewModel", "loginStateViewModel", "printerStateViewModel", "slicingViewModel"], + ["settingsViewModel", "loginStateViewModel", "printerStateViewModel", "slicingViewModel","printerProfilesViewModel"], ["#files_wrapper", "#add_folder_dialog"] ]); }); diff --git a/src/octoprint/util/gcodeInterpreter.py b/src/octoprint/util/gcodeInterpreter.py index e32b3062..08d0f5cc 100644 --- a/src/octoprint/util/gcodeInterpreter.py +++ b/src/octoprint/util/gcodeInterpreter.py @@ -21,7 +21,6 @@ class AnalysisAborted(Exception): class gcode(object): def __init__(self): self._logger = logging.getLogger(__name__) - self.layerList = None self.extrusionAmount = [0] self.extrusionVolume = [0] @@ -30,6 +29,13 @@ class gcode(object): self.progressCallback = None self._abort = False self._filamentDiameter = 0 + #Parameters for object size check + self.minX=None + self.minY=None + self.minZ=None + self.maxX=None + self.maxY=None + self.maxZ=None def load(self, filename, printer_profile, throttle=None): if os.path.isfile(filename): @@ -143,7 +149,14 @@ class gcode(object): if e is not None: if absoluteE: e -= currentE[currentExtruder] + # If move includes extrusion, calculate new min/max coordinates of model if e > 0.0: + self.minX = pos[0] if self.minX is None or pos[0] < self.minX else self.minX + self.maxX = pos[0] if self.maxX is None or pos[0] > self.maxX else self.maxX + self.minY = pos[1] if self.minY is None or pos[1] < self.minY else self.minY + self.maxY = pos[1] if self.maxY is None or pos[1] > self.maxY else self.maxY + self.minZ = pos[2] if self.minZ is None or pos[2] < self.minZ else self.minZ + self.maxZ = pos[2] if self.maxZ is None or pos[2] > self.maxZ else self.maxZ moveType = 'extrude' if e < 0.0: moveType = 'retract' @@ -271,7 +284,6 @@ class gcode(object): def _parseCuraProfileString(self, comment, prefix): return {key: value for (key, value) in map(lambda x: x.split("=", 1), zlib.decompress(base64.b64decode(comment[len(prefix):])).split("\b"))} - def getCodeInt(line, code): n = line.find(code) + 1 if n < 1: From 2680d135ee426ba25bc8ce743f8148077b834f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 19 May 2016 12:52:35 +0200 Subject: [PATCH 2/2] Small changes before merging of PR * Do not trigger notifications within additional data generation, that would trigger a LOT of notifications just on page load. Instead adjusted to only display notification if a problematic file gets selected for printing (and select & print gets downgraded to just select in such a case too) * Made some left-over english-only strings translatable * Added Model Size display to additional data in file list (if we have it, why not show it :)) * Slight rewording of notification. * Fixed a merge error. --- .../static/js/app/viewmodels/files.js | 174 +++++++++--------- 1 file changed, 88 insertions(+), 86 deletions(-) diff --git a/src/octoprint/static/js/app/viewmodels/files.js b/src/octoprint/static/js/app/viewmodels/files.js index ecc660b5..92d110aa 100644 --- a/src/octoprint/static/js/app/viewmodels/files.js +++ b/src/octoprint/static/js/app/viewmodels/files.js @@ -289,7 +289,8 @@ $(function() { } OctoPrint.files.select(file.origin, OctoPrint.files.pathForElement(file)) .done(function() { - if (printAfterLoad) { + var withinPrintDimensions = self.evaluatePrintDimensions(file, true); + if (withinPrintDimensions && printAfterLoad) { OctoPrint.job.start(); } }); @@ -411,6 +412,17 @@ $(function() { self.getAdditionalData = function(data) { var output = ""; if (data["gcodeAnalysis"]) { + if (data["gcodeAnalysis"]["printingArea"]) { + var area = data["gcodeAnalysis"]["printingArea"]; + var dimensions = { + width: area["maxX"] - area["minX"], + depth: area["maxY"] - area["minY"], + height: area["maxZ"] - area["minZ"] + }; + + output += gettext("Model Size") + ": " + _.sprintf("%(width).2fmm × %(depth).2fmm × %(height).2fmm", dimensions); + output += "
"; + } if (data["gcodeAnalysis"]["filament"] && typeof(data["gcodeAnalysis"]["filament"]) == "object") { var filament = data["gcodeAnalysis"]["filament"]; if (_.keys(filament).length == 1) { @@ -424,82 +436,6 @@ $(function() { } } output += gettext("Estimated Print Time") + ": " + formatDuration(data["gcodeAnalysis"]["estimatedPrintTime"]) + "
"; - if (self.loginState.isUser()) - { - var profileItems=self.printerProfiles.profiles.items(); - var count=0; - while(count < profileItems.length && !profileItems[count].current) - { - count++; - } - if (count < profileItems.length) - { - var warn=false; - var volumeInfo=profileItems[count].volume; - var printingArea=data["gcodeAnalysis"]["printingArea"]; - if(volumeInfo.origin=="lowerleft") - { - if(printingArea["maxX"]>volumeInfo.width || printingArea["maxY"]>volumeInfo.depth || - printingArea["maxZ"]>volumeInfo.height) - { - warn=true; - } - } - else //origin is centre - { - if( printingArea["maxX"]>(volumeInfo.width/2) || printingArea["maxY"]>(volumeInfo.depth/2) || - printingArea["maxZ"]>volumeInfo.height || Math.abs(printingArea["minX"])>(volumeInfo.width/2) || - Math.abs(printingArea["minY"])>(volumeInfo.depth/2)) - { - warn=true; - } - } - } - if(warn) - { - var warning = "

" + _.sprintf(gettext("Revise file %s"), data["name"]) + "

"; - var info=""; - - // set print volume boundaries - var boundaries = { - minX : 0, - maxX : volumeInfo.width, - minY : 0, - maxY : volumeInfo.depth, - minZ : 0, - maxZ : volumeInfo.height - }; - if (volumeInfo.origin === "center") { - boundaries["maxX"] = volumeInfo.width / 2; - boundaries["minX"] = -1 * boundaries["maxX"]; - boundaries["maxY"] = volumeInfo.depth / 2; - boundaries["minY"] = -1 * boundaries["maxY"]; - } - - // find exceeded dimensions - if (printingArea["minX"] < boundaries["minX"] || printingArea["maxX"] > boundaries["maxX"]) { - info += _.sprintf(gettext("Profile's width: ( %s, %s) vs object's width: (%s, %s)\n"), boundaries["minX"], - boundaries["maxX"],printingArea["minX"], printingArea["maxX"]); - } - if (printingArea["minY"] < boundaries["minY"] || printingArea["maxY"] > boundaries["maxY"]) { - info += _.sprintf(gettext("Profile's depth: (%s, %s) vs object's depth: (%s, %s)\n"),boundaries["minY"], - boundaries["maxY"], printingArea["minY"], printingArea["maxY"]); - } - if (printingArea["minZ"] < boundaries["minZ"] || printingArea["maxZ"] > boundaries["maxZ"]) { - info += _.sprintf(gettext("Profile's height: (%s, %s) vs object's height: (%s, %s)\n"),boundaries["minZ"], - boundaries["maxZ"], printingArea["minZ"], printingArea["maxZ"]); - } - - //warn user - warning += pnotifyAdditionalInfo("
" + info + "
"); - new PNotify({ - title: "Object exceeds the printing area", - text: warning, - type: "warning", - hide: false - }); - } - } } if (data["prints"] && data["prints"]["last"]) { output += gettext("Last Printed") + ": " + formatTimeAgo(data["prints"]["last"]["date"]) + "
"; @@ -510,6 +446,80 @@ $(function() { return output; }; + self.evaluatePrintDimensions = function(data, notify) { + var printingArea = data["gcodeAnalysis"]["printingArea"]; + if (!printingArea) { + return true; + } + + var printerProfile = self.printerProfiles.currentProfileData(); + if (!printerProfile) { + return true; + } + + var volumeInfo = printerProfile.volume; + if (!volumeInfo) { + return true; + } + + // set print volume boundaries + var boundaries = { + minX : 0, + maxX : volumeInfo.width(), + minY : 0, + maxY : volumeInfo.depth(), + minZ : 0, + maxZ : volumeInfo.height() + }; + if (volumeInfo.origin() == "center") { + boundaries["maxX"] = volumeInfo.width() / 2; + boundaries["minX"] = -1 * boundaries["maxX"]; + boundaries["maxY"] = volumeInfo.depth() / 2; + boundaries["minY"] = -1 * boundaries["maxY"]; + } + + // model not within bounds, we need to prepare a warning + var warning = "

" + _.sprintf(gettext("Object in %(name)s exceeds the print volume of the currently selected printer profile, be careful when printing this."), data) + "

"; + var info = ""; + + var formatData = { + profile: boundaries, + object: printingArea + }; + + info += _.sprintf(gettext("Object's bounding box: (%(object.minX).2f, %(object.minY).2f, %(object.minZ).2f) × (%(object.maxX).2f, %(object.maxY).2f, %(object.maxZ).2f)"), formatData); + info += "
"; + info += _.sprintf(gettext("Print volume: (%(profile.minX).2f, %(profile.minY).2f, %(profile.minZ).2f) × (%(profile.maxX).2f, %(profile.maxY).2f, %(profile.maxZ).2f)"), formatData); + + // find exceeded dimensions + if (printingArea["minX"] < boundaries["minX"] || printingArea["maxX"] > boundaries["maxX"]) { + info += gettext("
Object exceeds print volume in width."); + } + if (printingArea["minY"] < boundaries["minY"] || printingArea["maxY"] > boundaries["maxY"]) { + info += gettext("
Object exceeds print volume in depth."); + } + if (printingArea["minZ"] < boundaries["minZ"] || printingArea["maxZ"] > boundaries["maxZ"]) { + info += gettext("
Object exceeds print volume in height."); + } + + //warn user + if (info != "") { + if (notify) { + warning += pnotifyAdditionalInfo(info); + + new PNotify({ + title: gettext("Object doesn't fit print volume"), + text: warning, + type: "warning", + hide: false + }); + } + return false; + } else { + return true; + } + }; + self.performSearch = function(e) { var query = self.searchQuery(); if (query !== undefined && query.trim() != "") { @@ -578,16 +588,8 @@ $(function() { self.uploadSdButton = $("#gcode_upload_sd"); if (!self.uploadSdButton.length) { self.uploadSdButton = undefined; - if (_.endsWith(filename.toLowerCase(), ".stl")) { - self.slicing.show(location, filename); - } - - if (data.result.done) { - $("#gcode_upload_progress .bar").css("width", "0%"); - $("#gcode_upload_progress").removeClass("progress-striped").removeClass("active"); - $("#gcode_upload_progress .bar").text(""); - } } + self.uploadProgress = $("#gcode_upload_progress"); self.uploadProgressBar = $(".bar", self.uploadProgress);