Small fixes prior to merge of #852
* Proper origin visualization in gcode viewer for centered rectangular * Validation of origin profile entry * Auto-migration of existing profiles upon load * Added changelog entry
This commit is contained in:
parent
12310c5e3a
commit
cd244f341d
4 changed files with 97 additions and 24 deletions
|
|
@ -118,6 +118,9 @@
|
||||||
* Renamed "Temperature Timeout" and "SD Status Timeout" in Settings to "Temperature Interval" and "SD Status Interval"
|
* Renamed "Temperature Timeout" and "SD Status Timeout" in Settings to "Temperature Interval" and "SD Status Interval"
|
||||||
to better reflect what those values are actually used for.
|
to better reflect what those values are actually used for.
|
||||||
* Better behaviour of the settings dialog on mobile devices.
|
* Better behaviour of the settings dialog on mobile devices.
|
||||||
|
* Added support for rectangular printer beds with the origin in the center ([#682](https://github.com/foosel/OctoPrint/issues/682)
|
||||||
|
and [#852](https://github.com/foosel/OctoPrint/pull/852)). Printer profiles now contain a new settings ``volume.origin``
|
||||||
|
which can either be ``lowerleft`` or ``center``. For circular beds only ``center`` is supported.
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,10 +80,9 @@ class PrinterProfileManager(object):
|
||||||
* - ``volume.formFactor``
|
* - ``volume.formFactor``
|
||||||
- ``string``
|
- ``string``
|
||||||
- Form factor of the print bed, either ``rectangular`` or ``circular``
|
- Form factor of the print bed, either ``rectangular`` or ``circular``
|
||||||
- ``volume.origin``
|
* - ``volume.origin``
|
||||||
- ``string``
|
- ``string``
|
||||||
- Location of gcode origin in the print volume, either ``lowerleft``
|
- Location of gcode origin in the print volume, either ``lowerleft`` or ``center``
|
||||||
- or ``center``
|
|
||||||
* - ``heatedBed``
|
* - ``heatedBed``
|
||||||
- ``bool``
|
- ``bool``
|
||||||
- Whether the printer has a heated bed (``True``) or not (``False``)
|
- Whether the printer has a heated bed (``True``) or not (``False``)
|
||||||
|
|
@ -304,7 +303,15 @@ class PrinterProfileManager(object):
|
||||||
import yaml
|
import yaml
|
||||||
with open(path) as f:
|
with open(path) as f:
|
||||||
profile = yaml.safe_load(f)
|
profile = yaml.safe_load(f)
|
||||||
|
|
||||||
|
if self._migrate_profile(profile):
|
||||||
|
try:
|
||||||
|
self._save_to_path(path, profile, allow_overwrite=True)
|
||||||
|
except:
|
||||||
|
self._logger.exception("Tried to save profile to {path} after migrating it while loading, ran into exception".format(path=path))
|
||||||
|
|
||||||
profile = self._ensure_valid_profile(profile)
|
profile = self._ensure_valid_profile(profile)
|
||||||
|
|
||||||
if not profile:
|
if not profile:
|
||||||
self._logger.warn("Invalid profile: %s" % path)
|
self._logger.warn("Invalid profile: %s" % path)
|
||||||
raise InvalidProfileError()
|
raise InvalidProfileError()
|
||||||
|
|
@ -356,6 +363,14 @@ class PrinterProfileManager(object):
|
||||||
sanitized_name = sanitized_name.replace(" ", "_")
|
sanitized_name = sanitized_name.replace(" ", "_")
|
||||||
return sanitized_name
|
return sanitized_name
|
||||||
|
|
||||||
|
def _migrate_profile(self, profile):
|
||||||
|
# make sure profile format is up to date
|
||||||
|
if "volume" in profile and "formFactor" in profile["volume"] and not "origin" in profile["volume"]:
|
||||||
|
profile["volume"]["origin"] = BedOrigin.CENTER if profile["volume"]["formFactor"] == BedTypes.CIRCULAR else BedOrigin.LOWERLEFT
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def _ensure_valid_profile(self, profile):
|
def _ensure_valid_profile(self, profile):
|
||||||
# ensure all keys are present
|
# ensure all keys are present
|
||||||
if not dict_contains_keys(self.default, profile):
|
if not dict_contains_keys(self.default, profile):
|
||||||
|
|
@ -399,6 +414,15 @@ class PrinterProfileManager(object):
|
||||||
if not profile["volume"]["formFactor"] in BedTypes.values():
|
if not profile["volume"]["formFactor"] in BedTypes.values():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# validate origin type
|
||||||
|
if not profile["volume"]["origin"] in BedOrigin.values():
|
||||||
|
return False
|
||||||
|
|
||||||
|
# ensure origin and form factor combination is legal
|
||||||
|
if profile["volume"]["formFactor"] == BedTypes.CIRCULAR and not profile["volume"]["origin"] == BedOrigin.CENTER:
|
||||||
|
# we do not support circular beds with anything other than a centered origin
|
||||||
|
return False
|
||||||
|
|
||||||
# validate offsets
|
# validate offsets
|
||||||
offsets = []
|
offsets = []
|
||||||
for offset in profile["extruder"]["offsets"]:
|
for offset in profile["extruder"]["offsets"]:
|
||||||
|
|
|
||||||
|
|
@ -258,43 +258,66 @@ GCODE.renderer = (function(){
|
||||||
};
|
};
|
||||||
|
|
||||||
var drawRectangularGrid = function() {
|
var drawRectangularGrid = function() {
|
||||||
var i;
|
var x, y;
|
||||||
var width = renderOptions["bed"]["x"] * zoomFactor;
|
var width = renderOptions["bed"]["x"];
|
||||||
var height = renderOptions["bed"]["y"] * zoomFactor;
|
var height = renderOptions["bed"]["y"];
|
||||||
var origin = {
|
|
||||||
x: 0,
|
|
||||||
y: -1 * renderOptions["bed"]["y"] * zoomFactor
|
|
||||||
};
|
|
||||||
|
|
||||||
|
var minX, maxX, minY, maxY;
|
||||||
if (renderOptions["bed"]["centeredOrigin"]) {
|
if (renderOptions["bed"]["centeredOrigin"]) {
|
||||||
origin.x -= width / 2;
|
var halfWidth = width / 2;
|
||||||
origin.y += height / 2;
|
var halfHeight = height / 2;
|
||||||
|
|
||||||
|
minX = -halfWidth;
|
||||||
|
maxX = halfWidth;
|
||||||
|
minY = -halfHeight;
|
||||||
|
maxY = halfHeight;
|
||||||
|
} else {
|
||||||
|
minX = 0;
|
||||||
|
maxX = width;
|
||||||
|
minY = 0;
|
||||||
|
maxY = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//~ bed outline and origin
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.strokeStyle = renderOptions["colorGrid"];
|
ctx.strokeStyle = renderOptions["colorGrid"];
|
||||||
ctx.fillStyle = "#ffffff";
|
ctx.fillStyle = "#ffffff";
|
||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = 2;
|
||||||
|
|
||||||
ctx.rect(origin.x, origin.y, width, height);
|
// outline
|
||||||
|
ctx.rect(minX * zoomFactor, -1 * minY * zoomFactor, width * zoomFactor, -1 * height * zoomFactor);
|
||||||
|
|
||||||
|
// origin
|
||||||
|
ctx.moveTo(minX * zoomFactor, 0);
|
||||||
|
ctx.lineTo(maxX * zoomFactor, 0);
|
||||||
|
ctx.moveTo(0, -1 * minY * zoomFactor);
|
||||||
|
ctx.lineTo(0, -1 * maxY * zoomFactor);
|
||||||
|
|
||||||
|
// draw
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
ctx.strokeStyle = renderOptions["colorGrid"];
|
ctx.strokeStyle = renderOptions["colorGrid"];
|
||||||
ctx.lineWidth = 1;
|
ctx.lineWidth = 1;
|
||||||
|
|
||||||
|
//~~ grid starting from origin
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
for (i = 0; i <= renderOptions["bed"]["x"]; i += gridStep) {
|
for (x = 0; x <= maxX; x += gridStep) {
|
||||||
ctx.moveTo(origin.x + i * zoomFactor, origin.y);
|
ctx.moveTo(x * zoomFactor, -1 * minY * zoomFactor);
|
||||||
ctx.lineTo(origin.x + i * zoomFactor, origin.y + height);
|
ctx.lineTo(x * zoomFactor, -1 * maxY * zoomFactor);
|
||||||
|
|
||||||
|
ctx.moveTo(-1 * x * zoomFactor, -1 * minY * zoomFactor);
|
||||||
|
ctx.lineTo(-1 * x * zoomFactor, -1 * maxY * zoomFactor);
|
||||||
}
|
}
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
for (i = 0; i <= renderOptions["bed"]["y"]; i += gridStep) {
|
for (y = 0; y <= maxY; y += gridStep) {
|
||||||
ctx.moveTo(origin.x, origin.y + i * zoomFactor);
|
ctx.moveTo(minX * zoomFactor, -1 * y * zoomFactor);
|
||||||
ctx.lineTo(origin.x + width, origin.y + i * zoomFactor);
|
ctx.lineTo(maxX * zoomFactor, -1 * y * zoomFactor);
|
||||||
|
|
||||||
|
ctx.moveTo(minX * zoomFactor, y * zoomFactor);
|
||||||
|
ctx.lineTo(maxX * zoomFactor, y * zoomFactor);
|
||||||
}
|
}
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,12 @@ $(function() {
|
||||||
self.editorVolumeFormFactor = ko.observable();
|
self.editorVolumeFormFactor = ko.observable();
|
||||||
self.editorVolumeOrigin = ko.observable();
|
self.editorVolumeOrigin = ko.observable();
|
||||||
|
|
||||||
|
self.editorVolumeFormFactor.subscribe(function(oldVal, newVal) {
|
||||||
|
if (oldVal != newVal && newVal == "circular") {
|
||||||
|
self.editorVolumeOrigin("center");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
self.editorHeatedBed = ko.observable();
|
self.editorHeatedBed = ko.observable();
|
||||||
|
|
||||||
self.editorNozzleDiameter = ko.observable();
|
self.editorNozzleDiameter = ko.observable();
|
||||||
|
|
@ -95,10 +101,27 @@ $(function() {
|
||||||
{key: "black", name: gettext("black")}
|
{key: "black", name: gettext("black")}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
self.availableOrigins = ko.observable([
|
self.availableOrigins = ko.computed(function() {
|
||||||
{key: "lowerleft", name: gettext("Lower Left")},
|
var formFactor = self.editorVolumeFormFactor();
|
||||||
{key: "center", name: gettext("Centered")}
|
|
||||||
]);
|
var possibleOrigins = {
|
||||||
|
"lowerleft": gettext("Lower Left"),
|
||||||
|
"center": gettext("Center")
|
||||||
|
};
|
||||||
|
|
||||||
|
var keys = [];
|
||||||
|
if (formFactor == "rectangular") {
|
||||||
|
keys = ["lowerleft", "center"];
|
||||||
|
} else if (formFactor == "circular") {
|
||||||
|
keys = ["center"];
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = [];
|
||||||
|
_.each(keys, function(key) {
|
||||||
|
result.push({key: key, name: possibleOrigins[key]});
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
self.koEditorExtruderOffsets = ko.computed(function() {
|
self.koEditorExtruderOffsets = ko.computed(function() {
|
||||||
var extruderOffsets = self.editorExtruderOffsets();
|
var extruderOffsets = self.editorExtruderOffsets();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue