From 79e79dd84ced4d20ad86957b1fd659704d9d4063 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 19 Jan 2018 14:07:59 +0100 Subject: [PATCH 1/7] Only import profiles that match with the active machine CURA-4837 --- cura/Settings/CuraContainerRegistry.py | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 4567226060..21ab8e16aa 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -211,6 +211,34 @@ class CuraContainerRegistry(ContainerRegistry): return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags or !", "Failed to import profile from {0}: {1}", file_name, str(e))} if profile_or_list: + # Ensure it is always a list of profiles + if not isinstance(profile_or_list, list): + profile_or_list = [profile_or_list] + + # First check if this profile is suitable for this machine + global_profile = None + if len(profile_or_list) == 1: + global_profile = profile_or_list[0] + else: + for profile in profile_or_list: + if not profile.getMetaDataEntry("extruder"): + global_profile = profile + break + if not global_profile: + Logger.log("e", "Incorrect profile [%s]. Could not find global profile", file_name) + return { "status": "error", + "message": catalog.i18nc("@info:status Don't translate the XML tags or !", "This profile {0} contains incorrect data, could not import it.", file_name)} + profile_definition = global_profile.getMetaDataEntry("definition") + expected_machine_definition = "fdmprinter" + if global_container_stack.getMetaDataEntry("has_machine_quality"): + expected_machine_definition = global_container_stack.getMetaDataEntry("quality_definition") + if not expected_machine_definition: + expected_machine_definition = global_container_stack.definition.getId() + if expected_machine_definition is not None and profile_definition is not None and profile_definition != expected_machine_definition: + Logger.log("e", "Profile [%s] is for machine [%s] but the current active machine is [%s]. Will not import the profile", file_name) + return { "status": "error", + "message": catalog.i18nc("@info:status Don't translate the XML tags or !", "The machine defined in profile {0} doesn't match with your current machine, could not import it.", file_name)} + name_seed = os.path.splitext(os.path.basename(file_name))[0] new_name = self.uniqueName(name_seed) From e7a19bcce597f30bc0f569036f35fbc2063e0d3d Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 19 Jan 2018 14:34:06 +0100 Subject: [PATCH 2/7] Fix has_machine_quality value parsing CURA-4837 --- cura/Settings/CuraContainerRegistry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 21ab8e16aa..add2512a2d 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -230,7 +230,7 @@ class CuraContainerRegistry(ContainerRegistry): "message": catalog.i18nc("@info:status Don't translate the XML tags or !", "This profile {0} contains incorrect data, could not import it.", file_name)} profile_definition = global_profile.getMetaDataEntry("definition") expected_machine_definition = "fdmprinter" - if global_container_stack.getMetaDataEntry("has_machine_quality"): + if parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", "False")): expected_machine_definition = global_container_stack.getMetaDataEntry("quality_definition") if not expected_machine_definition: expected_machine_definition = global_container_stack.definition.getId() From b92ebadfd03848b3071f7f7aa5bf4c867e85b7e2 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 19 Jan 2018 15:05:05 +0100 Subject: [PATCH 3/7] Fix quality_changes for single-extrusion machines CURA-4839 - Add newly created quality_changes container to ContainerRegistry - If an extruder is created by CuraContainerRegistry, in project loading, do not try to override extruder's quality changes. --- cura/Settings/CuraContainerRegistry.py | 5 ++- plugins/3MFReader/ThreeMFWorkspaceReader.py | 39 +++++++++++---------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index add2512a2d..7231fa1f72 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -562,8 +562,8 @@ class CuraContainerRegistry(ContainerRegistry): extruder_stack.setQualityChangesById(quality_changes_id) else: # if we still cannot find a quality changes container for the extruder, create a new one - container_id = self.uniqueName(extruder_stack.getId() + "_user") container_name = machine.qualityChanges.getName() + container_id = self.uniqueName(extruder_stack.getId() + "_qc_" + container_name) extruder_quality_changes_container = InstanceContainer(container_id) extruder_quality_changes_container.setName(container_name) extruder_quality_changes_container.addMetaDataEntry("type", "quality_changes") @@ -572,6 +572,9 @@ class CuraContainerRegistry(ContainerRegistry): extruder_quality_changes_container.addMetaDataEntry("quality_type", machine.qualityChanges.getMetaDataEntry("quality_type")) extruder_quality_changes_container.setDefinition(machine.qualityChanges.getDefinition().getId()) + self.addContainer(extruder_quality_changes_container) + extruder_stack.qualityChanges = extruder_quality_changes_container + if not extruder_quality_changes_container: Logger.log("w", "Could not find quality_changes named [%s] for extruder [%s]", machine.qualityChanges.getName(), extruder_stack.getId()) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 913cea4f26..7c9d31c47a 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -703,6 +703,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): return # load extruder stack files + has_extruder_stack_files = len(extruder_stack_files) > 0 try: for extruder_stack_file in extruder_stack_files: container_id = self._stripFileToId(extruder_stack_file) @@ -946,26 +947,28 @@ class ThreeMFWorkspaceReader(WorkspaceReader): continue # Replace the quality/definition changes container if it's in one of the ExtruderStacks - for each_extruder_stack in extruder_stacks: - changes_container = None - if changes_container_type == "quality_changes": - changes_container = each_extruder_stack.qualityChanges - elif changes_container_type == "definition_changes": - changes_container = each_extruder_stack.definitionChanges - - # sanity checks - # NOTE: The following cases SHOULD NOT happen!!!! - if not changes_container: - Logger.log("e", "We try to get [%s] from the extruder stack [%s] but we got None instead!", - changes_container_type, each_extruder_stack.getId()) - - # NOTE: we can get an empty container here, but the IDs will not match, - # so this comparison is fine. - if self._id_mapping.get(changes_container.getId()) == new_id: + # Only apply the change if we have loaded extruder stacks from the project + if has_extruder_stack_files: + for each_extruder_stack in extruder_stacks: + changes_container = None if changes_container_type == "quality_changes": - each_extruder_stack.qualityChanges = each_changes_container + changes_container = each_extruder_stack.qualityChanges elif changes_container_type == "definition_changes": - each_extruder_stack.definitionChanges = each_changes_container + changes_container = each_extruder_stack.definitionChanges + + # sanity checks + # NOTE: The following cases SHOULD NOT happen!!!! + if not changes_container: + Logger.log("e", "We try to get [%s] from the extruder stack [%s] but we got None instead!", + changes_container_type, each_extruder_stack.getId()) + + # NOTE: we can get an empty container here, but the IDs will not match, + # so this comparison is fine. + if self._id_mapping.get(changes_container.getId()) == new_id: + if changes_container_type == "quality_changes": + each_extruder_stack.qualityChanges = each_changes_container + elif changes_container_type == "definition_changes": + each_extruder_stack.definitionChanges = each_changes_container if self._resolve_strategies["material"] == "new": # the actual material instance container can have an ID such as From fe6019988c0693230047125a39168188378045d0 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 22 Jan 2018 10:53:52 +0100 Subject: [PATCH 4/7] CURA-4839 Create variable before asignment --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 7c9d31c47a..3eca1326d0 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -704,6 +704,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # load extruder stack files has_extruder_stack_files = len(extruder_stack_files) > 0 + empty_quality_container = self._container_registry.findInstanceContainers(id = "empty_quality")[0] + empty_quality_changes_container = self._container_registry.findInstanceContainers(id = "empty_quality_changes")[0] try: for extruder_stack_file in extruder_stack_files: container_id = self._stripFileToId(extruder_stack_file) @@ -779,7 +781,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader): self._container_registry.removeContainer(container.getId()) return - # Check quality profiles to make sure that if one stack has the "not supported" quality profile, # all others should have the same. # @@ -811,17 +812,12 @@ class ThreeMFWorkspaceReader(WorkspaceReader): quality_has_been_changed = False if has_not_supported: - empty_quality_container = self._container_registry.findInstanceContainers(id = "empty_quality")[0] for stack in [global_stack] + extruder_stacks_in_use: stack.replaceContainer(_ContainerIndexes.Quality, empty_quality_container) - empty_quality_changes_container = self._container_registry.findInstanceContainers(id = "empty_quality_changes")[0] - for stack in [global_stack] + extruder_stacks_in_use: stack.replaceContainer(_ContainerIndexes.QualityChanges, empty_quality_changes_container) quality_has_been_changed = True else: - empty_quality_changes_container = self._container_registry.findInstanceContainers(id="empty_quality_changes")[0] - # The machine in the project has non-empty quality and there are usable qualities for this machine. # We need to check if the current quality_type is still usable for this machine, if not, then the quality # will be reset to the "preferred quality" if present, otherwise "normal". From ba29d64592253f093e99195a68e3c5c1ce1dd018 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 22 Jan 2018 11:07:38 +0100 Subject: [PATCH 5/7] CURA-4821 Fix one at a time slicing for builtiplexer --- cura/OneAtATimeIterator.py | 3 ++- plugins/CuraEngineBackend/StartSliceJob.py | 13 +++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/cura/OneAtATimeIterator.py b/cura/OneAtATimeIterator.py index 5653c8f1fb..84d65bae8e 100644 --- a/cura/OneAtATimeIterator.py +++ b/cura/OneAtATimeIterator.py @@ -18,12 +18,13 @@ class OneAtATimeIterator(Iterator.Iterator): def _fillStack(self): node_list = [] for node in self._scene_node.getChildren(): - if not isinstance(node, SceneNode): + if not issubclass(type(node), SceneNode): continue if node.callDecoration("getConvexHull"): node_list.append(node) + if len(node_list) < 2: self._node_stack = node_list[:] return diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index b0e19e7f39..9e3621c782 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -15,7 +15,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Settings.Validator import ValidatorState from UM.Settings.SettingRelation import RelationType -from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode +from cura.Scene.CuraSceneNode import CuraSceneNode from cura.OneAtATimeIterator import OneAtATimeIterator from cura.Settings.ExtruderManager import ExtruderManager @@ -131,7 +131,7 @@ class StartSliceJob(Job): # Don't slice if there is a per object setting with an error value. for node in DepthFirstIterator(self._scene.getRoot()): - if type(node) is not SceneNode or not node.isSelectable(): + if type(node) is not CuraSceneNode or not node.isSelectable(): continue if self._checkStackForErrors(node.callDecoration("getStack")): @@ -155,10 +155,15 @@ class StartSliceJob(Job): if getattr(node, "_outside_buildarea", False): continue + # Filter on current build plate + build_plate_number = node.callDecoration("getBuildPlateNumber") + if build_plate_number is not None and build_plate_number != self._build_plate_number: + continue + children = node.getAllChildren() children.append(node) for child_node in children: - if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None: + if type(child_node) is CuraSceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None: temp_list.append(child_node) if temp_list: @@ -170,7 +175,7 @@ class StartSliceJob(Job): temp_list = [] has_printing_mesh = False for node in DepthFirstIterator(self._scene.getRoot()): - if node.callDecoration("isSliceable") and type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: + if node.callDecoration("isSliceable") and type(node) is CuraSceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: per_object_stack = node.callDecoration("getStack") is_non_printing_mesh = False if per_object_stack: From f589ce9570b3772217a3d655b32c13ace26532ca Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 22 Jan 2018 11:25:23 +0100 Subject: [PATCH 6/7] Copy first extruder values when they are available when upgrading - CURA-4835 --- cura/Settings/ExtruderStack.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index dd03ce7b3b..51b27fea57 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -55,15 +55,20 @@ class ExtruderStack(CuraContainerStack): # carried to the first extruder. # For material diameter, it was supposed to be applied to all extruders, so its value should be copied to all # extruders. - # - keys_to_copy = ["material_diameter"] # material diameter will be copied to all extruders - if self.getMetaDataEntry("position") == "0": - keys_to_copy.append("machine_nozzle_size") + + keys_to_copy = ["material_diameter", "machine_nozzle_size"] # these will be copied over to all extruders for key in keys_to_copy: + # Only copy the value when this extruder doesn't have the value. if self.definitionChanges.hasProperty(key, "value"): + # If the first extruder has a value for this setting, we must copy it to the other extruders via the global stack. + # Note: this assumes the extruders are loaded in the same order as they are positioned on the machine. + if self.getMetaDataEntry("position") == "0": + setting_value = self.definitionChanges.getProperty(key, "value") + stack.definitionChanges.setProperty(key, "value", setting_value) continue + setting_value = stack.definitionChanges.getProperty(key, "value") if setting_value is None: continue From 71cea55bdaecfadf0507b5f40300bcb3eafaa958 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 22 Jan 2018 12:48:29 +0100 Subject: [PATCH 7/7] Move untested settings to experimental category These settings are just some of the settings that were not tested by our materials and processing team. --- resources/definitions/fdmprinter.def.json | 425 +++++++++++----------- 1 file changed, 213 insertions(+), 212 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index e016fcfb5f..37b8b878af 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -653,20 +653,6 @@ "settable_per_mesh": false, "settable_per_extruder": false }, - "slicing_tolerance": - { - "label": "Slicing Tolerance", - "description": "How to slice layers with diagonal surfaces. The areas of a layer can be generated based on where the middle of the layer intersects the surface (Middle). Alternatively each layer can have the areas which fall inside of the volume throughout the height of the layer (Exclusive) or a layer has the areas which fall inside anywhere within the layer (Inclusive). Exclusive retains the most details, Inclusive makes for the best fit and Middle takes the least time to process.", - "type": "enum", - "options": - { - "middle": "Middle", - "exclusive": "Exclusive", - "inclusive": "Inclusive" - }, - "default_value": "middle", - "settable_per_mesh": true - }, "line_width": { "label": "Line Width", @@ -726,21 +712,6 @@ } } }, - "roofing_line_width": - { - "label": "Top Surface Skin Line Width", - "description": "Width of a single line of the areas at the top of the print.", - "unit": "mm", - "minimum_value": "0.001", - "minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size", - "maximum_value_warning": "2 * machine_nozzle_size", - "default_value": 0.4, - "type": "float", - "value": "skin_line_width", - "limit_to_extruder": "roofing_extruder_nr", - "settable_per_mesh": true, - "enabled": "roofing_layer_count > 0 and top_layers > 0" - }, "skin_line_width": { "label": "Top/Bottom Line Width", @@ -999,34 +970,6 @@ "settable_per_mesh": true, "enabled": "top_layers > 0" }, - "roofing_pattern": - { - "label": "Top Surface Skin Pattern", - "description": "The pattern of the top most layers.", - "type": "enum", - "options": - { - "lines": "Lines", - "concentric": "Concentric", - "zigzag": "Zig Zag" - }, - "default_value": "lines", - "value": "top_bottom_pattern", - "limit_to_extruder": "roofing_extruder_nr", - "settable_per_mesh": true, - "enabled": "roofing_layer_count > 0 and top_layers > 0" - }, - "roofing_angles": - { - "label": "Top Surface Skin Line Directions", - "description": "A list of integer line directions to use when the top surface skin layers use the lines or zig zag pattern. Elements from the list are used sequentially as the layers progress and when the end of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained in square brackets. Default is an empty list which means use the traditional default angles (45 and 135 degrees).", - "type": "[int]", - "default_value": "[ ]", - "value": "skin_angles", - "enabled": "roofing_pattern != 'concentric' and roofing_layer_count > 0 and top_layers > 0", - "limit_to_extruder": "roofing_extruder_nr", - "settable_per_mesh": true - }, "top_bottom_extruder_nr": { "label": "Top/Bottom Extruder", @@ -1861,14 +1804,6 @@ "settable_per_mesh": true } } - }, - "infill_enable_travel_optimization": - { - "label": "Enable Travel Optimization", - "description": "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased.", - "type": "bool", - "default_value": false, - "settable_per_mesh": true } } }, @@ -1880,16 +1815,6 @@ "type": "category", "children": { - "material_flow_dependent_temperature": - { - "label": "Auto Temperature", - "description": "Change the temperature for each layer automatically with the average flow speed of that layer.", - "type": "bool", - "default_value": false, - "enabled": "machine_nozzle_temp_enabled and False", - "settable_per_mesh": false, - "settable_per_extruder": true - }, "default_material_print_temperature": { "label": "Default Printing Temperature", @@ -1964,17 +1889,6 @@ "settable_per_mesh": false, "settable_per_extruder": true }, - "material_flow_temp_graph": - { - "label": "Flow Temperature Graph", - "description": "Data linking material flow (in mm3 per second) to temperature (degrees Celsius).", - "unit": "[[mm³,°C]]", - "type": "str", - "default_value": "[[3.5,200],[7.0,240]]", - "enabled": "False and machine_nozzle_temp_enabled and material_flow_dependent_temperature", - "settable_per_mesh": false, - "settable_per_extruder": true - }, "material_extrusion_cool_down_speed": { "label": "Extrusion Cool Down Speed Modifier", @@ -2083,7 +1997,8 @@ "settable_per_mesh": false, "settable_per_extruder": true }, - "retract_at_layer_change":{ + "retract_at_layer_change": + { "label": "Retract at Layer Change", "description": "Retract the filament when the nozzle is moving to the next layer.", "type": "bool", @@ -3491,15 +3406,6 @@ "settable_per_mesh": true, "settable_per_extruder": false }, - "support_tree_enable": - { - "label": "Tree Support", - "description": "Generate a tree-like support with branches that support your print. This may reduce material usage and print time.", - "type": "bool", - "default_value": false, - "settable_per_mesh": true, - "settable_per_extruder": false - }, "support_extruder_nr": { "label": "Support Extruder", @@ -3818,110 +3724,6 @@ "limit_to_extruder": "support_infill_extruder_nr", "settable_per_mesh": false }, - "support_tree_angle": - { - "label": "Tree Support Branch Angle", - "description": "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach.", - "unit": "°", - "type": "float", - "minimum_value": "0", - "maximum_value": "90", - "maximum_value_warning": "60", - "default_value": 40, - "limit_to_extruder": "support_infill_extruder_nr", - "enabled": "support_tree_enable", - "settable_per_mesh": false, - "settable_per_extruder": true - }, - "support_tree_branch_distance": - { - "label": "Tree Support Branch Distance", - "description": "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove.", - "unit": "mm", - "type": "float", - "minimum_value": "0.001", - "default_value": 4, - "limit_to_extruder": "support_infill_extruder_nr", - "enabled": "support_tree_enable", - "settable_per_mesh": true - }, - "support_tree_branch_diameter": - { - "label": "Tree Support Branch Diameter", - "description": "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this.", - "unit": "mm", - "type": "float", - "minimum_value": "0.001", - "minimum_value_warning": "support_line_width * 2", - "default_value": 2, - "limit_to_extruder": "support_infill_extruder_nr", - "enabled": "support_tree_enable", - "settable_per_mesh": false, - "settable_per_extruder": true - }, - "support_tree_branch_diameter_angle": - { - "label": "Tree Support Branch Diameter Angle", - "description": "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support.", - "unit": "°", - "type": "float", - "minimum_value": "0", - "maximum_value": "89.9999", - "maximum_value_warning": "15", - "default_value": 5, - "limit_to_extruder": "support_infill_extruder_nr", - "enabled": "support_tree_enable", - "settable_per_mesh": false, - "settable_per_extruder": true - }, - "support_tree_collision_resolution": - { - "label": "Tree Support Collision Resolution", - "description": "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically.", - "unit": "mm", - "type": "float", - "minimum_value": "0.001", - "minimum_value_warning": "support_line_width / 4", - "maximum_value_warning": "support_line_width * 2", - "default_value": 0.4, - "value": "support_line_width / 2", - "limit_to_extruder": "support_infill_extruder_nr", - "enabled": "support_tree_enable and support_tree_branch_diameter_angle > 0", - "settable_per_mesh": false, - "settable_per_extruder": true - }, - "support_tree_wall_thickness": - { - "label": "Tree Support Wall Thickness", - "description": "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily.", - "unit": "mm", - "type": "float", - "minimum_value": "0", - "minimum_value_warning": "wall_line_width", - "default_value": 0.8, - "value": "support_line_width", - "limit_to_extruder": "support_infill_extruder_nr", - "enabled": "support_tree_enable", - "settable_per_mesh": false, - "settable_per_extruder": true, - "children": - { - "support_tree_wall_count": - { - "label": "Tree Support Wall Line Count", - "description": "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily.", - "type": "int", - "minimum_value": "0", - "minimum_value_warning": "1", - "default_value": 1, - "value": "round(support_tree_wall_thickness / support_line_width)", - "limit_to_extruder": "support_infill_extruder_nr", - "enabled": "support_tree_enable", - "settable_per_mesh": false, - "settable_per_extruder": true - } - } - }, "gradual_support_infill_steps": { "label": "Gradual Support Infill Steps", @@ -5106,18 +4908,6 @@ "default_value": false, "settable_per_mesh": true }, - "meshfix_maximum_resolution": - { - "label": "Maximum Resolution", - "description": "The minimum size of a line segment after slicing. If you increase this, the mesh will have a lower resolution. This may allow the printer to keep up with the speed it has to process g-code and will increase slice speed by removing details of the mesh that it can't process anyway.", - "type": "float", - "unit": "mm", - "default_value": 0.01, - "minimum_value": "0.001", - "minimum_value_warning": "0.005", - "maximum_value_warning": "0.1", - "settable_per_mesh": true - }, "multiple_mesh_overlap": { "label": "Merged Meshes Overlap", @@ -5346,6 +5136,217 @@ "description": "experimental!", "children": { + "support_tree_enable": + { + "label": "Tree Support", + "description": "Generate a tree-like support with branches that support your print. This may reduce material usage and print time.", + "type": "bool", + "default_value": false, + "settable_per_mesh": true, + "settable_per_extruder": false + }, + "support_tree_angle": + { + "label": "Tree Support Branch Angle", + "description": "The angle of the branches. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach.", + "unit": "°", + "type": "float", + "minimum_value": "0", + "maximum_value": "90", + "maximum_value_warning": "60", + "default_value": 40, + "limit_to_extruder": "support_infill_extruder_nr", + "enabled": "support_tree_enable", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "support_tree_branch_distance": + { + "label": "Tree Support Branch Distance", + "description": "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove.", + "unit": "mm", + "type": "float", + "minimum_value": "0.001", + "default_value": 4, + "limit_to_extruder": "support_infill_extruder_nr", + "enabled": "support_tree_enable", + "settable_per_mesh": true + }, + "support_tree_branch_diameter": + { + "label": "Tree Support Branch Diameter", + "description": "The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this.", + "unit": "mm", + "type": "float", + "minimum_value": "0.001", + "minimum_value_warning": "support_line_width * 2", + "default_value": 2, + "limit_to_extruder": "support_infill_extruder_nr", + "enabled": "support_tree_enable", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "support_tree_branch_diameter_angle": + { + "label": "Tree Support Branch Diameter Angle", + "description": "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the tree support.", + "unit": "°", + "type": "float", + "minimum_value": "0", + "maximum_value": "89.9999", + "maximum_value_warning": "15", + "default_value": 5, + "limit_to_extruder": "support_infill_extruder_nr", + "enabled": "support_tree_enable", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "support_tree_collision_resolution": + { + "label": "Tree Support Collision Resolution", + "description": "Resolution to compute collisions with to avoid hitting the model. Setting this lower will produce more accurate trees that fail less often, but increases slicing time dramatically.", + "unit": "mm", + "type": "float", + "minimum_value": "0.001", + "minimum_value_warning": "support_line_width / 4", + "maximum_value_warning": "support_line_width * 2", + "default_value": 0.4, + "value": "support_line_width / 2", + "limit_to_extruder": "support_infill_extruder_nr", + "enabled": "support_tree_enable and support_tree_branch_diameter_angle > 0", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "support_tree_wall_thickness": + { + "label": "Tree Support Wall Thickness", + "description": "The thickness of the walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily.", + "unit": "mm", + "type": "float", + "minimum_value": "0", + "minimum_value_warning": "wall_line_width", + "default_value": 0.8, + "value": "support_line_width", + "limit_to_extruder": "support_infill_extruder_nr", + "enabled": "support_tree_enable", + "settable_per_mesh": false, + "settable_per_extruder": true, + "children": + { + "support_tree_wall_count": + { + "label": "Tree Support Wall Line Count", + "description": "The number of walls of the branches of tree support. Thicker walls take longer to print but don't fall over as easily.", + "type": "int", + "minimum_value": "0", + "minimum_value_warning": "1", + "default_value": 1, + "value": "round(support_tree_wall_thickness / support_line_width)", + "limit_to_extruder": "support_infill_extruder_nr", + "enabled": "support_tree_enable", + "settable_per_mesh": false, + "settable_per_extruder": true + } + } + }, + "slicing_tolerance": + { + "label": "Slicing Tolerance", + "description": "How to slice layers with diagonal surfaces. The areas of a layer can be generated based on where the middle of the layer intersects the surface (Middle). Alternatively each layer can have the areas which fall inside of the volume throughout the height of the layer (Exclusive) or a layer has the areas which fall inside anywhere within the layer (Inclusive). Exclusive retains the most details, Inclusive makes for the best fit and Middle takes the least time to process.", + "type": "enum", + "options": + { + "middle": "Middle", + "exclusive": "Exclusive", + "inclusive": "Inclusive" + }, + "default_value": "middle", + "settable_per_mesh": true + }, + "roofing_line_width": + { + "label": "Top Surface Skin Line Width", + "description": "Width of a single line of the areas at the top of the print.", + "unit": "mm", + "minimum_value": "0.001", + "minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size", + "maximum_value_warning": "2 * machine_nozzle_size", + "default_value": 0.4, + "type": "float", + "value": "skin_line_width", + "limit_to_extruder": "roofing_extruder_nr", + "settable_per_mesh": true, + "enabled": "roofing_layer_count > 0 and top_layers > 0" + }, + "roofing_pattern": + { + "label": "Top Surface Skin Pattern", + "description": "The pattern of the top most layers.", + "type": "enum", + "options": + { + "lines": "Lines", + "concentric": "Concentric", + "zigzag": "Zig Zag" + }, + "default_value": "lines", + "value": "top_bottom_pattern", + "limit_to_extruder": "roofing_extruder_nr", + "settable_per_mesh": true, + "enabled": "roofing_layer_count > 0 and top_layers > 0" + }, + "roofing_angles": + { + "label": "Top Surface Skin Line Directions", + "description": "A list of integer line directions to use when the top surface skin layers use the lines or zig zag pattern. Elements from the list are used sequentially as the layers progress and when the end of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained in square brackets. Default is an empty list which means use the traditional default angles (45 and 135 degrees).", + "type": "[int]", + "default_value": "[ ]", + "value": "skin_angles", + "enabled": "roofing_pattern != 'concentric' and roofing_layer_count > 0 and top_layers > 0", + "limit_to_extruder": "roofing_extruder_nr", + "settable_per_mesh": true + }, + "infill_enable_travel_optimization": + { + "label": "Enable Travel Optimization", + "description": "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased.", + "type": "bool", + "default_value": false, + "settable_per_mesh": true + }, + "material_flow_dependent_temperature": + { + "label": "Auto Temperature", + "description": "Change the temperature for each layer automatically with the average flow speed of that layer.", + "type": "bool", + "default_value": false, + "enabled": "machine_nozzle_temp_enabled and False", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "material_flow_temp_graph": + { + "label": "Flow Temperature Graph", + "description": "Data linking material flow (in mm3 per second) to temperature (degrees Celsius).", + "unit": "[[mm³,°C]]", + "type": "str", + "default_value": "[[3.5,200],[7.0,240]]", + "enabled": "False and machine_nozzle_temp_enabled and material_flow_dependent_temperature", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "meshfix_maximum_resolution": + { + "label": "Maximum Resolution", + "description": "The minimum size of a line segment after slicing. If you increase this, the mesh will have a lower resolution. This may allow the printer to keep up with the speed it has to process g-code and will increase slice speed by removing details of the mesh that it can't process anyway.", + "type": "float", + "unit": "mm", + "default_value": 0.01, + "minimum_value": "0.001", + "minimum_value_warning": "0.005", + "maximum_value_warning": "0.1", + "settable_per_mesh": true + }, "support_skip_some_zags": { "label": "Break Up Support In Chunks",