diff --git a/docs/api/fileops.rst b/docs/api/fileops.rst index 84f20814..75c68c4d 100644 --- a/docs/api/fileops.rst +++ b/docs/api/fileops.rst @@ -311,6 +311,9 @@ Issue a file command * ``slicer``: The slicing engine to use, defaults to ``cura`` if not set, which is also the only supported slicer right now. * ``gcode``: Name of the GCODE file to generated, in the same location as the STL file. Defaults to the STL file name with extension ``.gco`` if not set. + * ``position``: Position of the object-to-slice's center on the print bed. A dictionary containing both ``x`` and ``y`` + coordinate in mm is expected + * ``printerProfile``: Name of the printer profile to use, if not set the default printer profile will be used. * ``profile``: Name of the slicing profile to use, if not set the default slicing profile of the slicer will be used. * ``profile.*``: Override parameters, the ``profile.`` prefix will be stripped and the matching profile key will be overridden with the supplied value. Use this if you want to specify things that change often like a different @@ -352,9 +355,11 @@ Issue a file command "command": "slice", "slicer": "cura", "gcode": "some_model.first_try.gcode", + "printerProfile": "my_custom_reprap", "profile": "high_quality", "profile.infill": 75, - "profile.fill_density": 15 + "profile.fill_density": 15, + "position": {"x": 100, "y": 100} } .. sourcecode:: http diff --git a/src/octoprint/filemanager/__init__.py b/src/octoprint/filemanager/__init__.py index b541eaa7..2a41b061 100644 --- a/src/octoprint/filemanager/__init__.py +++ b/src/octoprint/filemanager/__init__.py @@ -152,7 +152,7 @@ class FileManager(object): def default_slicer(self): return self._slicing_manager.default_slicer - def slice(self, slicer_name, source_location, source_path, dest_location, dest_path, profile=None, printer_profile_id=None, overrides=None, callback=None, callback_args=None): + def slice(self, slicer_name, source_location, source_path, dest_location, dest_path, position=None, profile=None, printer_profile_id=None, overrides=None, callback=None, callback_args=None): absolute_source_path = self.get_absolute_path(source_location, source_path) def stlProcessed(source_location, source_path, tmp_path, dest_location, dest_path, start_time, printer_profile_id, callback, callback_args, _error=None, _cancelled=False): @@ -233,6 +233,7 @@ class FileManager(object): temp_path, profile, stlProcessed, + position=position, callback_args=args, overrides=overrides, printer_profile_id=printer_profile_id, diff --git a/src/octoprint/plugins/cura/__init__.py b/src/octoprint/plugins/cura/__init__.py index 3120ddd3..5f0a3265 100644 --- a/src/octoprint/plugins/cura/__init__.py +++ b/src/octoprint/plugins/cura/__init__.py @@ -229,13 +229,20 @@ class CuraPlugin(octoprint.plugin.SlicerPlugin, self._save_profile(path, new_profile, allow_overwrite=allow_overwrite) - def do_slice(self, model_path, printer_profile, machinecode_path=None, profile_path=None, on_progress=None, on_progress_args=None, on_progress_kwargs=None): + def do_slice(self, model_path, printer_profile, machinecode_path=None, profile_path=None, position=None, on_progress=None, on_progress_args=None, on_progress_kwargs=None): if not profile_path: profile_path = s.get(["default_profile"]) if not machinecode_path: path, _ = os.path.splitext(model_path) machinecode_path = path + ".gco" + if position and isinstance(position, dict) and "x" in position and "y" in position: + posX = position["x"] + posY = position["y"] + else: + posX = None + posY = None + if on_progress: if not on_progress_args: on_progress_args = () @@ -244,7 +251,7 @@ class CuraPlugin(octoprint.plugin.SlicerPlugin, self._cura_logger.info("### Slicing %s to %s using profile stored at %s" % (model_path, machinecode_path, profile_path)) - engine_settings = self._convert_to_engine(profile_path, printer_profile) + engine_settings = self._convert_to_engine(profile_path, printer_profile, posX, posY) executable = s.get(["cura_engine"]) if not executable: @@ -381,8 +388,8 @@ class CuraPlugin(octoprint.plugin.SlicerPlugin, with open(path, "wb") as f: yaml.safe_dump(profile, f, default_flow_style=False, indent=" ", allow_unicode=True) - def _convert_to_engine(self, profile_path, printer_profile): - profile = Profile(self._load_profile(profile_path), printer_profile) + def _convert_to_engine(self, profile_path, printer_profile, posX, posY): + profile = Profile(self._load_profile(profile_path), printer_profile, posX, posY) return profile.convert_to_engine() def _sanitize_name(name): diff --git a/src/octoprint/plugins/cura/profile.py b/src/octoprint/plugins/cura/profile.py index f1bff514..15a20bcc 100644 --- a/src/octoprint/plugins/cura/profile.py +++ b/src/octoprint/plugins/cura/profile.py @@ -514,9 +514,11 @@ class Profile(object): return result - def __init__(self, profile, printer_profile, overrides=None): + def __init__(self, profile, printer_profile, posX, posY, overrides=None): self._profile = self.__class__.merge_profile(profile, overrides=overrides) self._printer_profile = printer_profile + self._posX = posX + self._posY = posY def profile(self): import copy @@ -796,6 +798,24 @@ class Profile(object): return 2 return 1 + def get_pos_x(self): + if self._posX: + try: + return int(float(self._posX) * 1000) + except ValueError: + pass + + return int(self.get_float("machine_width") / 2.0 * 1000) if not self.get_boolean("machine_center_is_zero") else 0.0 + + def get_pos_y(self): + if self._posY: + try: + return int(float(self._posY) * 1000) + except ValueError: + pass + + return int(self.get_float("machine_depth") / 2.0 * 1000) if not self.get_boolean("machine_center_is_zero") else 0.0 + def convert_to_engine(self): edge_width, line_count = self.calculate_edge_width_and_line_count() @@ -844,8 +864,8 @@ class Profile(object): "coolHeadLift": 1 if self.get_boolean("cool_head_lift") else 0, # model positioning - "posx": int(self.get_float("machine_width") / 2.0 * 1000) if not self.get_boolean("machine_center_is_zero") else 0.0, - "posy": int(self.get_float("machine_depth") / 2.0 * 1000) if not self.get_boolean("machine_center_is_zero") else 0.0, + "posx": self.get_pos_x(), + "posy": self.get_pos_y(), # gcodes "startCode": self.get_gcode("start_gcode"), diff --git a/src/octoprint/server/api/files.py b/src/octoprint/server/api/files.py index 708e3db3..f8cd32a0 100644 --- a/src/octoprint/server/api/files.py +++ b/src/octoprint/server/api/files.py @@ -340,12 +340,18 @@ def gcodeFileCommand(filename, target): else: printerProfile = None + if "position" in data.keys() and data["position"] and isinstance(data["position"], dict) and "x" in data["position"] and "y" in data["position"]: + position = data["position"] + del data["position"] + else: + position = None + override_keys = [k for k in data if k.startswith("profile.") and data[k] is not None] overrides = dict() for key in override_keys: overrides[key[len("profile."):]] = data[key] - ok, result = fileManager.slice(slicer, target, filename, target, gcode_name, profile=profile, printer_profile_id=printerProfile, overrides=overrides) + ok, result = fileManager.slice(slicer, target, filename, target, gcode_name, profile=profile, printer_profile_id=printerProfile, position=position, overrides=overrides) if ok: files = {} location = url_for(".readGcodeFile", target=target, filename=gcode_name, _external=True) diff --git a/src/octoprint/slicing/__init__.py b/src/octoprint/slicing/__init__.py index 0585d4d8..afefae3e 100644 --- a/src/octoprint/slicing/__init__.py +++ b/src/octoprint/slicing/__init__.py @@ -95,7 +95,7 @@ class SlicingManager(object): def get_slicer(self, slicer, require_configured=True): return self._slicers[slicer] if slicer in self._slicers and (not require_configured or self._slicers[slicer].is_slicer_configured()) else None - def slice(self, slicer_name, source_path, dest_path, profile_name, callback, callback_args=None, callback_kwargs=None, overrides=None, on_progress=None, on_progress_args=None, on_progress_kwargs=None, printer_profile_id=None): + def slice(self, slicer_name, source_path, dest_path, profile_name, callback, callback_args=None, callback_kwargs=None, overrides=None, on_progress=None, on_progress_args=None, on_progress_kwargs=None, printer_profile_id=None, position=None): if callback_args is None: callback_args = () if callback_kwargs is None: @@ -119,7 +119,7 @@ class SlicingManager(object): if printer_profile is None: printer_profile = self._printer_profile_manager.get_current_or_default() - def slicer_worker(slicer, model_path, machinecode_path, profile_name, overrides, printer_profile, callback, callback_args, callback_kwargs): + def slicer_worker(slicer, model_path, machinecode_path, profile_name, overrides, printer_profile, position, callback, callback_args, callback_kwargs): try: slicer_name = slicer.get_slicer_properties()["type"] with self.temporary_profile(slicer_name, name=profile_name, overrides=overrides) as profile_path: @@ -128,6 +128,7 @@ class SlicingManager(object): printer_profile, machinecode_path=machinecode_path, profile_path=profile_path, + position=position, on_progress=on_progress, on_progress_args=on_progress_args, on_progress_kwargs=on_progress_kwargs @@ -142,7 +143,7 @@ class SlicingManager(object): import threading slicer_worker_thread = threading.Thread(target=slicer_worker, - args=(slicer, source_path, dest_path, profile_name, overrides, printer_profile, callback, callback_args, callback_kwargs)) + args=(slicer, source_path, dest_path, profile_name, overrides, printer_profile, position, callback, callback_args, callback_kwargs)) slicer_worker_thread.daemon = True slicer_worker_thread.start() return True, None