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:
Gina Häußge 2015-04-16 15:20:18 +02:00
parent 12310c5e3a
commit cd244f341d
4 changed files with 97 additions and 24 deletions

View file

@ -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

View file

@ -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"]:

View file

@ -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();
}; };

View file

@ -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();