diff --git a/CMakeLists.txt b/CMakeLists.txt index bf93166e2e..002662152e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "") CREATE_TRANSLATION_TARGETS() endif() -find_package(PythonInterp 3.4.0 REQUIRED) +find_package(PythonInterp 3.5.0 REQUIRED) install(DIRECTORY resources DESTINATION ${CMAKE_INSTALL_DATADIR}/cura) diff --git a/cura.desktop.in b/cura.desktop.in index 0ac25c9521..ceb5c99f08 100644 --- a/cura.desktop.in +++ b/cura.desktop.in @@ -5,6 +5,7 @@ Name[de]=Cura GenericName=3D Printing Software GenericName[de]=3D-Druck-Software Comment=Cura converts 3D models into paths for a 3D printer. It prepares your print for maximum accuracy, minimum printing time and good reliability with many extra features that make your print come out great. +Comment[de]=Cura wandelt 3D-Modelle in Pfade für einen 3D-Drucker um. Es bereitet Ihren Druck für maximale Genauigkeit, minimale Druckzeit und guter Zuverlässigkeit mit vielen zusätzlichen Funktionen vor, damit Ihr Druck großartig wird. Exec=@CMAKE_INSTALL_FULL_BINDIR@/cura %F TryExec=@CMAKE_INSTALL_FULL_BINDIR@/cura Icon=@CMAKE_INSTALL_FULL_DATADIR@/cura/resources/images/cura-icon.png diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index b9208ad1c0..603cda14d1 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -27,7 +27,7 @@ import UM.Settings.ContainerRegistry # Setting for clearance around the prime -PRIME_CLEARANCE = 1.5 +PRIME_CLEARANCE = 6.5 ## Build volume is a special kind of node that is responsible for rendering the printable area & disallowed areas. @@ -75,8 +75,8 @@ class BuildVolume(SceneNode): self._has_errors = False Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged) - # Number of objects loaded at the moment. - self._number_of_objects = 0 + #Objects loaded at the moment. We are connected to the property changed events of these objects. + self._scene_objects = set() self._change_timer = QTimer() self._change_timer.setInterval(100) @@ -102,14 +102,33 @@ class BuildVolume(SceneNode): def _onChangeTimerFinished(self): root = Application.getInstance().getController().getScene().getRoot() - new_number_of_objects = len([node for node in BreadthFirstIterator(root) if node.getMeshData() and type(node) is SceneNode]) - if new_number_of_objects != self._number_of_objects: - recalculate = False - if self._global_container_stack.getProperty("print_sequence", "value") == "one_at_a_time": - recalculate = (new_number_of_objects < 2 and self._number_of_objects > 1) or (new_number_of_objects > 1 and self._number_of_objects < 2) - self._number_of_objects = new_number_of_objects - if recalculate: - self._onSettingPropertyChanged("print_sequence", "value") # Create fake event, so right settings are triggered. + new_scene_objects = set(node for node in BreadthFirstIterator(root) if node.getMeshData() and type(node) is SceneNode) + if new_scene_objects != self._scene_objects: + for node in new_scene_objects - self._scene_objects: #Nodes that were added to the scene. + node.decoratorsChanged.connect(self._onNodeDecoratorChanged) + for node in self._scene_objects - new_scene_objects: #Nodes that were removed from the scene. + per_mesh_stack = node.callDecoration("getStack") + if per_mesh_stack: + per_mesh_stack.propertyChanged.disconnect(self._onSettingPropertyChanged) + active_extruder_changed = node.callDecoration("getActiveExtruderChangedSignal") + if active_extruder_changed is not None: + node.callDecoration("getActiveExtruderChangedSignal").disconnect(self._updateDisallowedAreasAndRebuild) + node.decoratorsChanged.disconnect(self._onNodeDecoratorChanged) + + self._scene_objects = new_scene_objects + self._onSettingPropertyChanged("print_sequence", "value") # Create fake event, so right settings are triggered. + + ## Updates the listeners that listen for changes in per-mesh stacks. + # + # \param node The node for which the decorators changed. + def _onNodeDecoratorChanged(self, node): + per_mesh_stack = node.callDecoration("getStack") + if per_mesh_stack: + per_mesh_stack.propertyChanged.connect(self._onSettingPropertyChanged) + active_extruder_changed = node.callDecoration("getActiveExtruderChangedSignal") + if active_extruder_changed is not None: + active_extruder_changed.connect(self._updateDisallowedAreasAndRebuild) + self._updateDisallowedAreasAndRebuild() def setWidth(self, width): if width: self._width = width @@ -324,7 +343,7 @@ class BuildVolume(SceneNode): self._width = self._global_container_stack.getProperty("machine_width", "value") machine_height = self._global_container_stack.getProperty("machine_height", "value") - if self._global_container_stack.getProperty("print_sequence", "value") == "one_at_a_time" and self._number_of_objects > 1: + if self._global_container_stack.getProperty("print_sequence", "value") == "one_at_a_time" and len(self._scene_objects) > 1: self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height) if self._height < machine_height: self._build_volume_message.show() @@ -347,7 +366,7 @@ class BuildVolume(SceneNode): rebuild_me = False if setting_key == "print_sequence": machine_height = self._global_container_stack.getProperty("machine_height", "value") - if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time" and self._number_of_objects > 1: + if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time" and len(self._scene_objects) > 1: self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height) if self._height < machine_height: self._build_volume_message.show() @@ -358,7 +377,7 @@ class BuildVolume(SceneNode): self._build_volume_message.hide() rebuild_me = True - if setting_key in self._skirt_settings or setting_key in self._prime_settings or setting_key in self._tower_settings or setting_key == "print_sequence" or setting_key in self._ooze_shield_settings or setting_key in self._distance_settings: + if setting_key in self._skirt_settings or setting_key in self._prime_settings or setting_key in self._tower_settings or setting_key == "print_sequence" or setting_key in self._ooze_shield_settings or setting_key in self._distance_settings or setting_key in self._extruder_settings: self._updateDisallowedAreas() rebuild_me = True @@ -372,24 +391,109 @@ class BuildVolume(SceneNode): def hasErrors(self): return self._has_errors + ## Calls _updateDisallowedAreas and makes sure the changes appear in the + # scene. + # + # This is required for a signal to trigger the update in one go. The + # ``_updateDisallowedAreas`` method itself shouldn't call ``rebuild``, + # since there may be other changes before it needs to be rebuilt, which + # would hit performance. + def _updateDisallowedAreasAndRebuild(self): + self._updateDisallowedAreas() + self.rebuild() + def _updateDisallowedAreas(self): if not self._global_container_stack: return - self._has_errors = False # Reset. self._error_areas = [] - disallowed_areas = copy.deepcopy( - self._global_container_stack.getProperty("machine_disallowed_areas", "value")) - areas = [] - machine_width = self._global_container_stack.getProperty("machine_width", "value") - machine_depth = self._global_container_stack.getProperty("machine_depth", "value") - prime_tower_area = None + extruder_manager = ExtruderManager.getInstance() + used_extruders = extruder_manager.getUsedExtruderStacks() + disallowed_border_size = self._getEdgeDisallowedSize() + + result_areas = self._computeDisallowedAreasStatic(disallowed_border_size, used_extruders) #Normal machine disallowed areas can always be added. + prime_areas = self._computeDisallowedAreasPrime(disallowed_border_size, used_extruders) + prime_disallowed_areas = self._computeDisallowedAreasStatic(0, used_extruders) #Where the priming is not allowed to happen. This is not added to the result, just for collision checking. + + #Check if prime positions intersect with disallowed areas. + for extruder in used_extruders: + extruder_id = extruder.getId() + + collision = False + for prime_polygon in prime_areas[extruder_id]: + for disallowed_polygon in prime_disallowed_areas[extruder_id]: + if prime_polygon.intersectsPolygon(disallowed_polygon) is not None: + collision = True + break + if collision: + break + + #Also check other prime positions (without additional offset). + for other_extruder_id in prime_areas: + if extruder_id == other_extruder_id: #It is allowed to collide with itself. + continue + for other_prime_polygon in prime_areas[other_extruder_id]: + if prime_polygon.intersectsPolygon(other_prime_polygon): + collision = True + break + if collision: + break + if collision: + break + + + if not collision: + #Prime areas are valid. Add as normal. + result_areas[extruder_id].extend(prime_areas[extruder_id]) + + nozzle_disallowed_areas = extruder.getProperty("nozzle_disallowed_areas", "value") + for area in nozzle_disallowed_areas: + polygon = Polygon(numpy.array(area, numpy.float32)) + polygon = polygon.getMinkowskiHull(Polygon.approximatedCircle(disallowed_border_size)) + result_areas[extruder_id].append(polygon) #Don't perform the offset on these. # Add prime tower location as disallowed area. + prime_tower_collision = False + prime_tower_areas = self._computeDisallowedAreasPrinted(used_extruders) + for extruder_id in prime_tower_areas: + for prime_tower_area in prime_tower_areas[extruder_id]: + for area in result_areas[extruder_id]: + if prime_tower_area.intersectsPolygon(area) is not None: + prime_tower_collision = True + break + if prime_tower_collision: #Already found a collision. + break + if not prime_tower_collision: + result_areas[extruder_id].extend(prime_tower_areas[extruder_id]) + else: + self._error_areas.extend(prime_tower_areas[extruder_id]) + + self._has_errors = len(self._error_areas) > 0 + + self._disallowed_areas = [] + for extruder_id in result_areas: + self._disallowed_areas.extend(result_areas[extruder_id]) + + ## Computes the disallowed areas for objects that are printed with print + # features. + # + # This means that the brim, travel avoidance and such will be applied to + # these features. + # + # \return A dictionary with for each used extruder ID the disallowed areas + # where that extruder may not print. + def _computeDisallowedAreasPrinted(self, used_extruders): + result = {} + for extruder in used_extruders: + result[extruder.getId()] = [] + + #Currently, the only normally printed object is the prime tower. if ExtruderManager.getInstance().getResolveOrValue("prime_tower_enable") == True: prime_tower_size = self._global_container_stack.getProperty("prime_tower_size", "value") - prime_tower_x = self._global_container_stack.getProperty("prime_tower_position_x", "value") - machine_width / 2 + machine_width = self._global_container_stack.getProperty("machine_width", "value") + machine_depth = self._global_container_stack.getProperty("machine_depth", "value") + prime_tower_x = self._global_container_stack.getProperty("prime_tower_position_x", "value") - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left. prime_tower_y = - self._global_container_stack.getProperty("prime_tower_position_y", "value") + machine_depth / 2 prime_tower_area = Polygon([ @@ -398,118 +502,118 @@ class BuildVolume(SceneNode): [prime_tower_x, prime_tower_y], [prime_tower_x - prime_tower_size, prime_tower_y], ]) - disallowed_polygons = [] + prime_tower_area = prime_tower_area.getMinkowskiHull(Polygon.approximatedCircle(0)) + for extruder in used_extruders: + result[extruder.getId()].append(prime_tower_area) #The prime tower location is the same for each extruder, regardless of offset. - # Check if prime positions intersect with disallowed areas - prime_collision = False - if disallowed_areas: - for area in disallowed_areas: - poly = Polygon(numpy.array(area, numpy.float32)) + return result - # Minkowski with zero, to ensure that the polygon is correct & watertight. - poly = poly.getMinkowskiHull(Polygon.approximatedCircle(0)) - disallowed_polygons.append(poly) + ## Computes the disallowed areas for the prime locations. + # + # These are special because they are not subject to things like brim or + # travel avoidance. They do get a dilute with the border size though + # because they may not intersect with brims and such of other objects. + # + # \param border_size The size with which to offset the disallowed areas + # due to skirt, brim, travel avoid distance, etc. + # \param used_extruders The extruder stacks to generate disallowed areas + # for. + # \return A dictionary with for each used extruder ID the prime areas. + def _computeDisallowedAreasPrime(self, border_size, used_extruders): + result = {} - extruder_manager = ExtruderManager.getInstance() - extruders = extruder_manager.getMachineExtruders(self._global_container_stack.getId()) - prime_polygons = [] - # Each extruder has it's own prime location - for extruder in extruders: - prime_x = extruder.getProperty("extruder_prime_pos_x", "value") - machine_width / 2 - prime_y = machine_depth / 2 - extruder.getProperty("extruder_prime_pos_y", "value") + machine_width = self._global_container_stack.getProperty("machine_width", "value") + machine_depth = self._global_container_stack.getProperty("machine_depth", "value") + for extruder in used_extruders: + prime_x = extruder.getProperty("extruder_prime_pos_x", "value") - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left. + prime_y = machine_depth / 2 - extruder.getProperty("extruder_prime_pos_y", "value") - prime_polygon = Polygon([ - [prime_x - PRIME_CLEARANCE, prime_y - PRIME_CLEARANCE], - [prime_x + PRIME_CLEARANCE, prime_y - PRIME_CLEARANCE], - [prime_x + PRIME_CLEARANCE, prime_y + PRIME_CLEARANCE], - [prime_x - PRIME_CLEARANCE, prime_y + PRIME_CLEARANCE], - ]) - prime_polygon = prime_polygon.getMinkowskiHull(Polygon.approximatedCircle(0)) - collision = False + prime_polygon = Polygon.approximatedCircle(PRIME_CLEARANCE) + prime_polygon = prime_polygon.translate(prime_x, prime_y) + prime_polygon = prime_polygon.getMinkowskiHull(Polygon.approximatedCircle(border_size)) + result[extruder.getId()] = [prime_polygon] - # Check if prime polygon is intersecting with any of the other disallowed areas. - # Note that we check the prime area without bed adhesion. - for poly in disallowed_polygons: - if prime_polygon.intersectsPolygon(poly) is not None: - collision = True - break + return result - # Also collide with other prime positions - for poly in prime_polygons: - if prime_polygon.intersectsPolygon(poly) is not None: - collision = True - break + ## Computes the disallowed areas that are statically placed in the machine. + # + # It computes different disallowed areas depending on the offset of the + # extruder. The resulting dictionary will therefore have an entry for each + # extruder that is used. + # + # \param border_size The size with which to offset the disallowed areas + # due to skirt, brim, travel avoid distance, etc. + # \param used_extruders The extruder stacks to generate disallowed areas + # for. + # \return A dictionary with for each used extruder ID the disallowed areas + # where that extruder may not print. + def _computeDisallowedAreasStatic(self, border_size, used_extruders): + #Convert disallowed areas to polygons and dilate them. + machine_disallowed_polygons = [] + for area in self._global_container_stack.getProperty("machine_disallowed_areas", "value"): + polygon = Polygon(numpy.array(area, numpy.float32)) + polygon = polygon.getMinkowskiHull(Polygon.approximatedCircle(border_size)) + machine_disallowed_polygons.append(polygon) - if not collision: - # Prime area is valid. Add as normal. - # Once it's added like this, it will recieve a bed adhesion offset, just like the others. - prime_polygons.append(prime_polygon) - else: - self._error_areas.append(prime_polygon) - prime_collision = collision or prime_collision + result = {} + for extruder in used_extruders: + extruder_id = extruder.getId() + offset_x = extruder.getProperty("machine_nozzle_offset_x", "value") + if offset_x is None: + offset_x = 0 + offset_y = extruder.getProperty("machine_nozzle_offset_y", "value") + if offset_y is None: + offset_y = 0 + result[extruder_id] = [] - disallowed_polygons.extend(prime_polygons) + for polygon in machine_disallowed_polygons: + result[extruder_id].append(polygon.translate(offset_x, offset_y)) #Compensate for the nozzle offset of this extruder. - disallowed_border_size = self._getEdgeDisallowedSize() - - # Extend every area already in the disallowed_areas with the skirt size. - if disallowed_areas: - for poly in disallowed_polygons: - poly = poly.getMinkowskiHull(Polygon.approximatedCircle(disallowed_border_size)) - areas.append(poly) - - # Add the skirt areas around the borders of the build plate. - if disallowed_border_size > 0: + #Add the border around the edge of the build volume. + left_unreachable_border = 0 + right_unreachable_border = 0 + top_unreachable_border = 0 + bottom_unreachable_border = 0 + #The build volume is defined as the union of the area that all extruders can reach, so we need to know the relative offset to all extruders. + for other_extruder in ExtruderManager.getInstance().getActiveExtruderStacks(): + other_offset_x = other_extruder.getProperty("machine_nozzle_offset_x", "value") + other_offset_y = other_extruder.getProperty("machine_nozzle_offset_y", "value") + left_unreachable_border = min(left_unreachable_border, other_offset_x - offset_x) + right_unreachable_border = max(right_unreachable_border, other_offset_x - offset_x) + top_unreachable_border = min(top_unreachable_border, other_offset_y - offset_y) + bottom_unreachable_border = max(bottom_unreachable_border, other_offset_y - offset_y) half_machine_width = self._global_container_stack.getProperty("machine_width", "value") / 2 half_machine_depth = self._global_container_stack.getProperty("machine_depth", "value") / 2 + if border_size - left_unreachable_border > 0: + result[extruder_id].append(Polygon(numpy.array([ + [-half_machine_width, -half_machine_depth], + [-half_machine_width, half_machine_depth], + [-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border], + [-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border] + ], numpy.float32))) + if border_size + right_unreachable_border > 0: + result[extruder_id].append(Polygon(numpy.array([ + [half_machine_width, half_machine_depth], + [half_machine_width, -half_machine_depth], + [half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border], + [half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border] + ], numpy.float32))) + if border_size + bottom_unreachable_border > 0: + result[extruder_id].append(Polygon(numpy.array([ + [-half_machine_width, half_machine_depth], + [half_machine_width, half_machine_depth], + [half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border], + [-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border] + ], numpy.float32))) + if border_size - top_unreachable_border > 0: + result[extruder_id].append(Polygon(numpy.array([ + [half_machine_width, -half_machine_depth], + [-half_machine_width, -half_machine_depth], + [-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border], + [half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border] + ], numpy.float32))) - areas.append(Polygon(numpy.array([ - [-half_machine_width, -half_machine_depth], - [-half_machine_width, half_machine_depth], - [-half_machine_width + disallowed_border_size, half_machine_depth - disallowed_border_size], - [-half_machine_width + disallowed_border_size, -half_machine_depth + disallowed_border_size] - ], numpy.float32))) - - areas.append(Polygon(numpy.array([ - [half_machine_width, half_machine_depth], - [half_machine_width, -half_machine_depth], - [half_machine_width - disallowed_border_size, -half_machine_depth + disallowed_border_size], - [half_machine_width - disallowed_border_size, half_machine_depth - disallowed_border_size] - ], numpy.float32))) - - areas.append(Polygon(numpy.array([ - [-half_machine_width, half_machine_depth], - [half_machine_width, half_machine_depth], - [half_machine_width - disallowed_border_size, half_machine_depth - disallowed_border_size], - [-half_machine_width + disallowed_border_size, half_machine_depth - disallowed_border_size] - ], numpy.float32))) - - areas.append(Polygon(numpy.array([ - [half_machine_width, -half_machine_depth], - [-half_machine_width, -half_machine_depth], - [-half_machine_width + disallowed_border_size, -half_machine_depth + disallowed_border_size], - [half_machine_width - disallowed_border_size, -half_machine_depth + disallowed_border_size] - ], numpy.float32))) - - # Check if the prime tower area intersects with any of the other areas. - # If this is the case, add it to the error area's so it can be drawn in red. - # If not, add it back to disallowed area's, so it's rendered as normal. - prime_tower_collision = False - if prime_tower_area: - # Using Minkowski of 0 fixes the prime tower area so it's rendered correctly - prime_tower_area = prime_tower_area.getMinkowskiHull(Polygon.approximatedCircle(0)) - for area in areas: - if prime_tower_area.intersectsPolygon(area) is not None: - prime_tower_collision = True - break - - if not prime_tower_collision: - areas.append(prime_tower_area) - else: - self._error_areas.append(prime_tower_area) - # The buildplate has errors if either prime tower or prime has a colission. - self._has_errors = prime_tower_collision or prime_collision - self._disallowed_areas = areas + return result ## Private convenience function to get a setting from the adhesion # extruder. @@ -520,6 +624,15 @@ class BuildVolume(SceneNode): def _getSettingFromAdhesionExtruder(self, setting_key, property = "value"): return self._getSettingFromExtruder(setting_key, "adhesion_extruder_nr", property) + ## Private convenience function to get a setting from every extruder. + # + # For single extrusion machines, this gets the setting from the global + # stack. + # + # \return A sequence of setting values, one for each extruder. + def _getSettingFromAllExtruders(self, setting_key, property = "value"): + return ExtruderManager.getInstance().getAllExtruderSettings(setting_key, property) + ## Private convenience function to get a setting from the support infill # extruder. # @@ -588,6 +701,8 @@ class BuildVolume(SceneNode): bed_adhesion_size += value elif adhesion_type == "raft": bed_adhesion_size = self._getSettingFromAdhesionExtruder("raft_margin") + elif adhesion_type == "none": + bed_adhesion_size = 0 else: raise Exception("Unknown bed adhesion type. Did you forget to update the build volume calculations for your new bed adhesion type?") @@ -602,10 +717,12 @@ class BuildVolume(SceneNode): farthest_shield_distance = max(farthest_shield_distance, container_stack.getProperty("ooze_shield_dist", "value")) move_from_wall_radius = 0 # Moves that start from outer wall. - if self._getSettingFromAdhesionExtruder("infill_wipe_dist"): - move_from_wall_radius = max(move_from_wall_radius, self._getSettingFromAdhesionExtruder("infill_wipe_dist")) - if self._getSettingFromAdhesionExtruder("travel_avoid_distance") and self._getSettingFromAdhesionExtruder("travel_avoid_other_parts"): - move_from_wall_radius = max(move_from_wall_radius, self._getSettingFromAdhesionExtruder("travel_avoid_distance")) + move_from_wall_radius = max(move_from_wall_radius, max(self._getSettingFromAllExtruders("infill_wipe_dist"))) + avoid_enabled_per_extruder = self._getSettingFromAllExtruders(("travel_avoid_other_parts")) + avoid_distance_per_extruder = self._getSettingFromAllExtruders("travel_avoid_distance") + for index, avoid_other_parts_enabled in enumerate(avoid_enabled_per_extruder): #For each extruder (or just global). + if avoid_other_parts_enabled: + move_from_wall_radius = max(move_from_wall_radius, avoid_distance_per_extruder[index]) #Index of the same extruder. #Now combine our different pieces of data to get the final border size. #Support expansion is added to the bed adhesion, since the bed adhesion goes around support. @@ -622,3 +739,4 @@ class BuildVolume(SceneNode): _tower_settings = ["prime_tower_enable", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y"] _ooze_shield_settings = ["ooze_shield_enabled", "ooze_shield_dist"] _distance_settings = ["infill_wipe_dist", "travel_avoid_distance", "support_offset", "support_enable", "travel_avoid_other_parts"] + _extruder_settings = ["support_enable", "support_interface_enable", "support_infill_extruder_nr", "support_extruder_nr_layer_0", "support_interface_extruder_nr", "brim_line_count", "adhesion_extruder_nr", "adhesion_type"] #Settings that can affect which extruders are used. \ No newline at end of file diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py index a7d0775e21..c4b2fe0337 100644 --- a/cura/ConvexHullDecorator.py +++ b/cura/ConvexHullDecorator.py @@ -236,6 +236,8 @@ class ConvexHullDecorator(SceneNodeDecorator): extra_margin = max(0, self._getSettingProperty("raft_margin", "value")) elif adhesion_type == "brim": extra_margin = max(0, self._getSettingProperty("brim_line_count", "value") * self._getSettingProperty("skirt_brim_line_width", "value")) + elif adhesion_type == "none": + extra_margin = 0 elif adhesion_type == "skirt": extra_margin = max( 0, self._getSettingProperty("skirt_gap", "value") + diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index bd54209e36..59752026f5 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -258,7 +258,7 @@ class CuraApplication(QtApplication): Preferences.getInstance().setDefault("general/visible_settings", """ machine_settings - resolution + resolution layer_height shell wall_thickness @@ -558,7 +558,9 @@ class CuraApplication(QtApplication): qmlRegisterSingletonType(cura.Settings.ContainerManager, "Cura", 1, 0, "ContainerManager", cura.Settings.ContainerManager.createContainerManager) - qmlRegisterSingletonType(QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")), "Cura", 1, 0, "Actions") + # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. + actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) + qmlRegisterSingletonType(actions_url, "Cura", 1, 0, "Actions") engine.rootContext().setContextProperty("ExtruderManager", cura.Settings.ExtruderManager.getInstance()) @@ -571,11 +573,19 @@ class CuraApplication(QtApplication): def onSelectionChanged(self): if Selection.hasSelection(): - if not self.getController().getActiveTool(): + if self.getController().getActiveTool(): + # If the tool has been disabled by the new selection + if not self.getController().getActiveTool().getEnabled(): + # Default + self.getController().setActiveTool("TranslateTool") + else: if self._previous_active_tool: self.getController().setActiveTool(self._previous_active_tool) + if not self.getController().getActiveTool().getEnabled(): + self.getController().setActiveTool("TranslateTool") self._previous_active_tool = None else: + # Default self.getController().setActiveTool("TranslateTool") if Preferences.getInstance().getValue("view/center_on_select"): self._center_after_select = True @@ -686,10 +696,9 @@ class CuraApplication(QtApplication): while current_node.getParent() and current_node.getParent().callDecoration("isGroup"): current_node = current_node.getParent() - new_node = copy.deepcopy(current_node) - op = GroupedOperation() for _ in range(count): + new_node = copy.deepcopy(current_node) op.addOperation(AddSceneNodeOperation(new_node, current_node.getParent())) op.push() @@ -770,7 +779,11 @@ class CuraApplication(QtApplication): for node in nodes: # Ensure that the object is above the build platform node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator) - op.addOperation(SetTransformOperation(node, Vector(0, node.getWorldPosition().y - node.getBoundingBox().bottom, 0))) + if node.getBoundingBox(): + center_y = node.getWorldPosition().y - node.getBoundingBox().bottom + else: + center_y = 0 + op.addOperation(SetTransformOperation(node, Vector(0, center_y, 0))) op.push() ## Reset all transformations on nodes with mesh data. @@ -792,11 +805,10 @@ class CuraApplication(QtApplication): for node in nodes: # Ensure that the object is above the build platform node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator) - center_y = 0 - if node.callDecoration("isGroup"): + if node.getBoundingBox(): center_y = node.getWorldPosition().y - node.getBoundingBox().bottom else: - center_y = node.getMeshData().getCenterPosition().y + center_y = 0 op.addOperation(SetTransformOperation(node, Vector(0, center_y, 0), Quaternion(), Vector(1, 1, 1))) op.push() @@ -865,8 +877,15 @@ class CuraApplication(QtApplication): Logger.log("d", "mergeSelected: Exception:", e) return - # Compute the center of the objects when their origins are aligned. - object_centers = [node.getMeshData().getCenterPosition().scale(node.getScale()) for node in group_node.getChildren() if node.getMeshData()] + meshes = [node.getMeshData() for node in group_node.getAllChildren() if node.getMeshData()] + + # Compute the center of the objects + object_centers = [] + for mesh, node in zip(meshes, group_node.getChildren()): + orientation = node.getOrientation().toMatrix() + rotated_mesh = mesh.getTransformed(orientation) + center = rotated_mesh.getCenterPosition().scale(node.getScale()) + object_centers.append(center) if object_centers and len(object_centers) > 0: middle_x = sum([v.x for v in object_centers]) / len(object_centers) middle_y = sum([v.y for v in object_centers]) / len(object_centers) @@ -876,9 +895,13 @@ class CuraApplication(QtApplication): offset = Vector(0, 0, 0) # Move each node to the same position. - for center, node in zip(object_centers, group_node.getChildren()): - # Align the object and also apply the offset to center it inside the group. - node.setPosition(center - offset) + for mesh, node in zip(meshes, group_node.getChildren()): + orientation = node.getOrientation().toMatrix() + rotated_mesh = mesh.getTransformed(orientation) + + # Align the object around its zero position + # and also apply the offset to center it inside the group. + node.setPosition(-rotated_mesh.getZeroPosition().scale(node.getScale()) - offset) # Use the previously found center of the group bounding box as the new location of the group group_node.setPosition(group_node.getBoundingBox().center) @@ -932,15 +955,16 @@ class CuraApplication(QtApplication): fileLoaded = pyqtSignal(str) def _onFileLoaded(self, job): - node = job.getResult() - if node != None: - self.fileLoaded.emit(job.getFileName()) - node.setSelectable(True) - node.setName(os.path.basename(job.getFileName())) - op = AddSceneNodeOperation(node, self.getController().getScene().getRoot()) - op.push() + nodes = job.getResult() + for node in nodes: + if node is not None: + self.fileLoaded.emit(job.getFileName()) + node.setSelectable(True) + node.setName(os.path.basename(job.getFileName())) + op = AddSceneNodeOperation(node, self.getController().getScene().getRoot()) + op.push() - self.getController().getScene().sceneChanged.emit(node) #Force scene change. + self.getController().getScene().sceneChanged.emit(node) #Force scene change. def _onJobFinished(self, job): if type(job) is not ReadMeshJob or not job.getResult(): diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index bbf35da6f8..6eae259e1e 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -134,6 +134,20 @@ class PrinterOutputDevice(QObject, OutputDevice): def _setJobState(self, job_state): Logger.log("w", "_setJobState is not implemented by this output device") + @pyqtSlot() + def startCamera(self): + self._startCamera() + + def _startCamera(self): + Logger.log("w", "_startCamera is not implemented by this output device") + + @pyqtSlot() + def stopCamera(self): + self._stopCamera() + + def _stopCamera(self): + Logger.log("w", "_stopCamera is not implemented by this output device") + @pyqtProperty(str, notify = jobNameChanged) def jobName(self): return self._job_name diff --git a/cura/QualityManager.py b/cura/QualityManager.py index 52c052ad28..0b4353442e 100644 --- a/cura/QualityManager.py +++ b/cura/QualityManager.py @@ -27,10 +27,17 @@ class QualityManager: # specified then the currently selected machine definition is used. # \param material_containers (Optional) \type{List[ContainerInstance]} If nothing is specified then # the current set of selected materials is used. - # \return the matching quality containers \type{List[ContainerInstance]} + # \return the matching quality container \type{ContainerInstance} def findQualityByName(self, quality_name, machine_definition=None, material_containers=None): criteria = {"type": "quality", "name": quality_name} - return self._getFilteredContainersForStack(machine_definition, material_containers, **criteria) + result = self._getFilteredContainersForStack(machine_definition, material_containers, **criteria) + + # Fall back to using generic materials and qualities if nothing could be found. + if not result and material_containers and len(material_containers) == 1: + basic_materials = self._getBasicMaterials(material_containers[0]) + result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria) + + return result[0] if result else None ## Find a quality changes container by name. # diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index f94f628686..93caa89106 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -4,7 +4,7 @@ import os.path import urllib -from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal, QUrl +from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal, QUrl, QVariant from PyQt5.QtWidgets import QMessageBox import UM.PluginRegistry @@ -261,8 +261,9 @@ class ContainerManager(QObject): @pyqtSlot(str, result = bool) def isContainerUsed(self, container_id): - UM.Logger.log("d", "Checking if container %s is currently used in the active stacks", container_id) - for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): + UM.Logger.log("d", "Checking if container %s is currently used", container_id) + containers = self._container_registry.findContainerStacks() + for stack in containers: if container_id in [child.getId() for child in stack.getContainers()]: UM.Logger.log("d", "The container is in use by %s", stack.getId()) return True @@ -611,13 +612,12 @@ class ContainerManager(QObject): if base_name is None: base_name = quality_name - # Try to find a Quality with the name. - containers = QualityManager.getInstance().findQualityByName(quality_name, machine_definition, material_instances) - if containers: - container = containers[0] + container = QualityManager.getInstance().findQualityByName(quality_name, machine_definition, material_instances) + if container: + UM.Logger.log("d", "We found a quality to duplicate.") return self._duplicateQualityForMachineType(container, base_name, machine_definition) - + UM.Logger.log("d", "We found a quality_changes to duplicate.") # Assume it is a quality changes. return self._duplicateQualityChangesForMachineType(quality_name, base_name, machine_definition) @@ -791,7 +791,6 @@ class ContainerManager(QObject): # Create a new quality_changes container for the quality. quality_changes = UM.Settings.InstanceContainer(self._createUniqueId(base_id, new_name)) - print(quality_changes.getId()) quality_changes.setName(new_name) quality_changes.addMetaDataEntry("type", "quality_changes") quality_changes.addMetaDataEntry("quality_type", quality_container.getMetaDataEntry("quality_type")) @@ -806,3 +805,50 @@ class ContainerManager(QObject): else: quality_changes.setDefinition(QualityManager.getInstance().getParentMachineDefinition(machine_definition)) return quality_changes + + + ## Import profiles from a list of file_urls. + # Each QUrl item must end with .curaprofile, or it will not be imported. + # + # \param QVariant, essentially a list with QUrl objects. + # \return Dict with keys status, text + @pyqtSlot(QVariant, result="QVariantMap") + def importProfiles(self, file_urls): + status = "ok" + results = {"ok": [], "error": []} + for file_url in file_urls: + if not file_url.isValid(): + continue + path = file_url.toLocalFile() + if not path: + continue + if not path.endswith(".curaprofile"): + continue + + single_result = UM.Settings.ContainerRegistry.getInstance().importProfile(path) + if single_result["status"] == "error": + status = "error" + results[single_result["status"]].append(single_result["message"]) + + return { + "status": status, + "message": "\n".join(results["ok"] + results["error"])} + + ## Import single profile, file_url does not have to end with curaprofile + @pyqtSlot(QUrl, result="QVariantMap") + def importProfile(self, file_url): + if not file_url.isValid(): + return + path = file_url.toLocalFile() + if not path: + return + return UM.Settings.ContainerRegistry.getInstance().importProfile(path) + + @pyqtSlot("QVariantList", QUrl, str) + def exportProfile(self, instance_id, file_url, file_type): + if not file_url.isValid(): + return + path = file_url.toLocalFile() + if not path: + return + UM.Settings.ContainerRegistry.getInstance().exportProfile(instance_id, path, file_type) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 98c37da617..3982418070 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -210,7 +210,7 @@ class CuraContainerRegistry(ContainerRegistry): return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())} # If it hasn't returned by now, none of the plugins loaded the profile successfully. - return {"status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type.", file_name)} + return {"status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type or is corrupted.", file_name)} def _configureProfile(self, profile, id_seed, new_name): profile.setReadOnly(False) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 0236f158aa..a3eef51009 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -5,6 +5,8 @@ from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot, QObject, QVariant # import UM.Application #To get the global container stack to find the current machine. import UM.Logger +from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator #To find which extruders are used in the scene. +from UM.Scene.SceneNode import SceneNode #To find which extruders are used in the scene. import UM.Settings.ContainerRegistry #Finding containers by ID. import UM.Settings.SettingFunction @@ -268,18 +270,78 @@ class ExtruderManager(QObject): container_registry.addContainer(container_stack) def getAllExtruderValues(self, setting_key): + return self.getAllExtruderSettings(setting_key, "value") + + ## Gets a property of a setting for all extruders. + # + # \param setting_key \type{str} The setting to get the property of. + # \param property \type{str} The property to get. + # \return \type{List} the list of results + def getAllExtruderSettings(self, setting_key, property): global_container_stack = UM.Application.getInstance().getGlobalContainerStack() - multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1 - if not multi_extrusion: - return [global_container_stack.getProperty(setting_key, "value")] + if global_container_stack.getProperty("machine_extruder_count", "value") <= 1: + return [global_container_stack.getProperty(setting_key, property)] result = [] for index in self.extruderIds: extruder_stack_id = self.extruderIds[str(index)] - stack = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id=extruder_stack_id)[0] - result.append(stack.getProperty(setting_key, "value")) + stack = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = extruder_stack_id)[0] + result.append(stack.getProperty(setting_key, property)) return result + ## Gets the extruder stacks that are actually being used at the moment. + # + # An extruder stack is being used if it is the extruder to print any mesh + # with, or if it is the support infill extruder, the support interface + # extruder, or the bed adhesion extruder. + # + # If there are no extruders, this returns the global stack as a singleton + # list. + # + # \return A list of extruder stacks. + def getUsedExtruderStacks(self): + global_stack = UM.Application.getInstance().getGlobalContainerStack() + container_registry = UM.Settings.ContainerRegistry.getInstance() + + if global_stack.getProperty("machine_extruder_count", "value") <= 1: #For single extrusion. + return [global_stack] + + used_extruder_stack_ids = set() + + #Get the extruders of all meshes in the scene. + support_enabled = False + support_interface_enabled = False + scene_root = UM.Application.getInstance().getController().getScene().getRoot() + meshes = [node for node in DepthFirstIterator(scene_root) if type(node) is SceneNode and node.isSelectable()] #Only use the nodes that will be printed. + for mesh in meshes: + extruder_stack_id = mesh.callDecoration("getActiveExtruder") + if not extruder_stack_id: #No per-object settings for this node. + extruder_stack_id = self.extruderIds["0"] + used_extruder_stack_ids.add(extruder_stack_id) + + #Get whether any of them use support. + per_mesh_stack = mesh.callDecoration("getStack") + if per_mesh_stack: + support_enabled |= per_mesh_stack.getProperty("support_enable", "value") + support_interface_enabled |= per_mesh_stack.getProperty("support_interface_enable", "value") + else: #Take the setting from the build extruder stack. + extruder_stack = container_registry.findContainerStacks(id = extruder_stack_id)[0] + support_enabled |= extruder_stack.getProperty("support_enable", "value") + support_interface_enabled |= extruder_stack.getProperty("support_enable", "value") + + #The support extruders. + if support_enabled: + used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_infill_extruder_nr", "value"))]) + used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_extruder_nr_layer_0", "value"))]) + if support_interface_enabled: + used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_interface_extruder_nr", "value"))]) + + #The platform adhesion extruder. Not used if using none. + if global_stack.getProperty("adhesion_type", "value") != "none": + used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("adhesion_extruder_nr", "value"))]) + + return [container_registry.findContainerStacks(id = stack_id)[0] for stack_id in used_extruder_stack_ids] + ## Removes the container stack and user profile for the extruders for a specific machine. # # \param machine_id The machine to remove the extruders for. diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 909ad2f4d6..fce82212cd 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -34,7 +34,7 @@ class MachineManager(QObject): self.globalContainerChanged.connect(self.activeVariantChanged) self.globalContainerChanged.connect(self.activeQualityChanged) - self._active_stack_valid = None + self._stacks_have_errors = None self._onGlobalContainerChanged() ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged) @@ -85,6 +85,7 @@ class MachineManager(QObject): globalValueChanged = pyqtSignal() # Emitted whenever a value inside global container is changed. activeStackValueChanged = pyqtSignal() # Emitted whenever a value inside the active stack is changed. activeStackValidationChanged = pyqtSignal() # Emitted whenever a validation inside active container is changed + stacksValidationChanged = pyqtSignal() # Emitted whenever a validation is changed blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly @@ -246,9 +247,15 @@ class MachineManager(QObject): quality.nameChanged.connect(self._onQualityNameChanged) + ## Update self._stacks_valid according to _checkStacksForErrors and emit if change. + def _updateStacksHaveErrors(self): + old_stacks_have_errors = self._stacks_have_errors + self._stacks_have_errors = self._checkStacksHaveErrors() + if old_stacks_have_errors != self._stacks_have_errors: + self.stacksValidationChanged.emit() + def _onActiveExtruderStackChanged(self): self.blurSettings.emit() # Ensure no-one has focus. - old_active_container_stack = self._active_container_stack if self._active_container_stack and self._active_container_stack != self._global_container_stack: @@ -261,10 +268,7 @@ class MachineManager(QObject): else: self._active_container_stack = self._global_container_stack - old_active_stack_valid = self._active_stack_valid - self._active_stack_valid = not self._checkStackForErrors(self._active_container_stack) - if old_active_stack_valid != self._active_stack_valid: - self.activeStackValidationChanged.emit() + self._updateStacksHaveErrors() if old_active_container_stack != self._active_container_stack: # Many methods and properties related to the active quality actually depend @@ -287,18 +291,18 @@ class MachineManager(QObject): self.activeStackValueChanged.emit() if property_name == "validationState": - if self._active_stack_valid: + if not self._stacks_have_errors: + # fast update, we only have to look at the current changed property if self._active_container_stack.getProperty(key, "settable_per_extruder"): changed_validation_state = self._active_container_stack.getProperty(key, property_name) else: changed_validation_state = self._global_container_stack.getProperty(key, property_name) if changed_validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError): - self._active_stack_valid = False - self.activeStackValidationChanged.emit() + self._stacks_have_errors = True + self.stacksValidationChanged.emit() else: - if not self._checkStackForErrors(self._active_container_stack) and not self._checkStackForErrors(self._global_container_stack): - self._active_stack_valid = True - self.activeStackValidationChanged.emit() + # Normal check + self._updateStacksHaveErrors() @pyqtSlot(str) def setActiveMachine(self, stack_id): @@ -352,15 +356,17 @@ class MachineManager(QObject): def _createUniqueName(self, container_type, current_name, new_name, fallback_name): return UM.Settings.ContainerRegistry.getInstance().createUniqueName(container_type, current_name, new_name, fallback_name) - ## Convenience function to check if a stack has errors. - def _checkStackForErrors(self, stack): - if stack is None: - return False + def _checkStacksHaveErrors(self): + if self._global_container_stack is not None and self._global_container_stack.hasErrors(): + return True - for key in stack.getAllKeys(): - validation_state = stack.getProperty(key, "validationState") - if validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError): + if self._global_container_stack is None: + return False + stacks = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())) + for stack in stacks: + if stack.hasErrors(): return True + return False ## Remove all instances from the top instanceContainer (effectively removing all user-changed settings) @@ -420,12 +426,12 @@ class MachineManager(QObject): for container in send_emits_containers: container.sendPostponedEmits() - ## Check if the global profile does not contain error states - # Note that the _active_stack_valid is cached due to performance issues - # Calling _checkStackForErrors on every change is simply too expensive - @pyqtProperty(bool, notify = activeStackValidationChanged) - def isActiveStackValid(self): - return bool(self._active_stack_valid) + ## Check if none of the stacks contain error states + # Note that the _stacks_have_errors is cached due to performance issues + # Calling _checkStack(s)ForErrors on every change is simply too expensive + @pyqtProperty(bool, notify = stacksValidationChanged) + def stacksHaveErrors(self): + return bool(self._stacks_have_errors) @pyqtProperty(str, notify = activeStackChanged) def activeUserProfileId(self): diff --git a/cura/Settings/MachineNameValidator.py b/cura/Settings/MachineNameValidator.py index d859e85343..34b6351144 100644 --- a/cura/Settings/MachineNameValidator.py +++ b/cura/Settings/MachineNameValidator.py @@ -28,7 +28,7 @@ class MachineNameValidator(QObject): # special character, and that up to [machine_name_max_length / 12] times. maximum_special_characters = int(machine_name_max_length / 12) unescaped = r"[a-zA-Z0-9_\-\.\/]" - self.machine_name_regex = r"((" + unescaped + "){0,12}|.){0," + str(maximum_special_characters) + r"}" + self.machine_name_regex = r"^((" + unescaped + "){0,12}|.){0," + str(maximum_special_characters) + r"}$" validationChanged = pyqtSignal() @@ -56,14 +56,11 @@ class MachineNameValidator(QObject): def updateValidation(self, new_name): is_valid = self.validate(new_name, 0) if is_valid == QValidator.Acceptable: - print("VALID") self.validation_regex = "^.*$" #Matches anything. else: - print("BROKEN!") self.validation_regex = "a^" #Never matches (unless you manage to get "a" before the start of the string... good luck). self.validationChanged.emit() @pyqtProperty("QRegExp", notify=validationChanged) def machineNameRegex(self): - print(self.machine_name_regex) return QRegExp(self.machine_name_regex) \ No newline at end of file diff --git a/cura/Settings/SettingInheritanceManager.py b/cura/Settings/SettingInheritanceManager.py index 9a2db55dc7..4d1e60a739 100644 --- a/cura/Settings/SettingInheritanceManager.py +++ b/cura/Settings/SettingInheritanceManager.py @@ -163,13 +163,15 @@ class SettingInheritanceManager(QObject): for container in containers: try: value = container.getProperty(key, "value") - if value is not None: - has_setting_function = isinstance(value, UM.Settings.SettingFunction) - if has_setting_function is False: - has_non_function_value = True - continue except AttributeError: continue + if value is not None: + # If a setting doesn't use any keys, it won't change it's value, so treat it as if it's a fixed value + has_setting_function = isinstance(value, UM.Settings.SettingFunction) and len(value.getUsedSettingKeys()) > 0 + if has_setting_function is False: + has_non_function_value = True + continue + if has_setting_function: break # There is a setting function somewhere, stop looking deeper. return has_setting_function and has_non_function_value diff --git a/cura/Settings/SettingOverrideDecorator.py b/cura/Settings/SettingOverrideDecorator.py index a8cfcd8d80..d38dac565b 100644 --- a/cura/Settings/SettingOverrideDecorator.py +++ b/cura/Settings/SettingOverrideDecorator.py @@ -61,6 +61,12 @@ class SettingOverrideDecorator(SceneNodeDecorator): def getActiveExtruder(self): return self._extruder_stack + ## Gets the signal that emits if the active extruder changed. + # + # This can then be accessed via a decorator. + def getActiveExtruderChangedSignal(self): + return self.activeExtruderChanged + ## Gets the currently active extruders position # # \return An extruder's position, or None if no position info is available. diff --git a/docs/Cura_Data_Model.odg b/docs/Cura_Data_Model.odg new file mode 100644 index 0000000000..b4a6dd548b Binary files /dev/null and b/docs/Cura_Data_Model.odg differ diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 0d6b9f3095..2aa719018d 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -10,7 +10,10 @@ from UM.Scene.SceneNode import SceneNode from UM.Scene.GroupDecorator import GroupDecorator import UM.Application from UM.Job import Job - +from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator +from UM.Application import Application +from cura.Settings.ExtruderManager import ExtruderManager +from cura.QualityManager import QualityManager import os.path import zipfile @@ -18,6 +21,7 @@ import zipfile try: import xml.etree.cElementTree as ET except ImportError: + Logger.log("w", "Unable to load cElementTree, switching to slower version") import xml.etree.ElementTree as ET ## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes! @@ -55,6 +59,46 @@ class ThreeMFReader(MeshReader): vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")]) Job.yieldThread() + xml_settings = list(object.findall(".//cura:setting", self._namespaces)) + + # Add the setting override decorator, so we can add settings to this node. + if xml_settings: + node.addDecorator(SettingOverrideDecorator()) + + global_container_stack = Application.getInstance().getGlobalContainerStack() + # Ensure the correct next container for the SettingOverride decorator is set. + if global_container_stack: + multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1 + # Ensure that all extruder data is reset + if not multi_extrusion: + default_stack_id = global_container_stack.getId() + else: + default_stack = ExtruderManager.getInstance().getExtruderStack(0) + if default_stack: + default_stack_id = default_stack.getId() + else: + default_stack_id = global_container_stack.getId() + node.callDecoration("setActiveExtruder", default_stack_id) + + # Get the definition & set it + definition = QualityManager.getInstance().getParentMachineDefinition(global_container_stack.getBottom()) + node.callDecoration("getStack").getTop().setDefinition(definition) + + setting_container = node.callDecoration("getStack").getTop() + for setting in xml_settings: + setting_key = setting.get("key") + setting_value = setting.text + + # Extruder_nr is a special case. + if setting_key == "extruder_nr": + extruder_stack = ExtruderManager.getInstance().getExtruderStack(int(setting_value)) + if extruder_stack: + node.callDecoration("setActiveExtruder", extruder_stack.getId()) + else: + Logger.log("w", "Unable to find extruder in position %s", setting_value) + continue + setting_container.setProperty(setting_key,"value", setting_value) + if len(node.getChildren()) > 0: group_decorator = GroupDecorator() node.addDecorator(group_decorator) @@ -112,7 +156,7 @@ class ThreeMFReader(MeshReader): return temp_mat def read(self, file_name): - result = SceneNode() + result = [] # The base object of 3mf is a zipped archive. archive = zipfile.ZipFile(file_name, "r") self._base_name = os.path.basename(file_name) @@ -125,33 +169,58 @@ class ThreeMFReader(MeshReader): for build_item in build_items: id = build_item.get("objectid") object = self._root.find("./3mf:resources/3mf:object[@id='{0}']".format(id), self._namespaces) + if "type" in object.attrib: + if object.attrib["type"] == "support" or object.attrib["type"] == "other": + # Ignore support objects, as cura does not support these. + # We can't guarantee that they wont be made solid. + # We also ignore "other", as I have no idea what to do with them. + Logger.log("w", "3MF file contained an object of type %s which is not supported by Cura", object.attrib["type"]) + continue + elif object.attrib["type"] == "solidsupport" or object.attrib["type"] == "model": + pass # Load these as normal + else: + # We should technically fail at this point because it's an invalid 3MF, but try to continue anyway. + Logger.log("e", "3MF file contained an object of type %s which is not supported by the 3mf spec", + object.attrib["type"]) + continue + build_item_node = self._createNodeFromObject(object, self._base_name + "_" + str(id)) transform = build_item.get("transform") if transform is not None: build_item_node.setTransformation(self._createMatrixFromTransformationString(transform)) - result.addChild(build_item_node) + global_container_stack = UM.Application.getInstance().getGlobalContainerStack() + + # Create a transformation Matrix to convert from 3mf worldspace into ours. + # First step: flip the y and z axis. + transformation_matrix = Matrix() + transformation_matrix._data[1, 1] = 0 + transformation_matrix._data[1, 2] = 1 + transformation_matrix._data[2, 1] = -1 + transformation_matrix._data[2, 2] = 0 + + # Second step: 3MF defines the left corner of the machine as center, whereas cura uses the center of the + # build volume. + if global_container_stack: + translation_vector = Vector(x = -global_container_stack.getProperty("machine_width", "value") / 2, + y = -global_container_stack.getProperty("machine_depth", "value") / 2, + z = 0) + translation_matrix = Matrix() + translation_matrix.setByTranslation(translation_vector) + transformation_matrix.multiply(translation_matrix) + + # Third step: 3MF also defines a unit, wheras Cura always assumes mm. + scale_matrix = Matrix() + scale_matrix.setByScaleVector(self._getScaleFromUnit(self._unit)) + transformation_matrix.multiply(scale_matrix) + + # Pre multiply the transformation with the loaded transformation, so the data is handled correctly. + build_item_node.setTransformation(build_item_node.getLocalTransformation().preMultiply(transformation_matrix)) + + result.append(build_item_node) except Exception as e: - Logger.log("e", "exception occured in 3mf reader: %s", e) - try: # Selftest - There might be more functions that should fail - bounding_box = result.getBoundingBox() - bounding_box.isValid() - except: - return None + Logger.log("e", "An exception occurred in 3mf reader: %s", e) - global_container_stack = UM.Application.getInstance().getGlobalContainerStack() - flip_matrix = Matrix() - flip_matrix._data[1, 1] = 0 - flip_matrix._data[1, 2] = 1 - flip_matrix._data[2, 1] = -1 - flip_matrix._data[2, 2] = 0 - result.setTransformation(flip_matrix) - if global_container_stack: - translation = Vector(x=-global_container_stack.getProperty("machine_width", "value") / 2, z=0, - y=-global_container_stack.getProperty("machine_depth", "value") / 2) - result.translate(translation) - result.scale(self._getScaleFromUnit(self._unit)) - result.setEnabled(False) # The result should not be moved in any way, so disable it. return result ## Create a scale vector based on a unit string. diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index 496efd1615..0a39004f9f 100644 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -1,14 +1,14 @@ [2.3.1] *Layer Height in Profile Selection -The layer height of each profile is now shown in the profile selection menu. +Added the layer height to the profile selection menu. *Bug fixes -Editing material settings has actual effect on the prints again -Upgrading from version 2.1 on OSX works again -You can import g-code from related machines as profile -Fixed inheritance taking from the wrong extruder -The i-symbol is updated properly -Fixed a freeze that could sometimes occur while printing via Wi-Fi +Fixed the option to import g-code from related machines as a profile +Fixed a bug where editing material settings has no effect on 3D prints +Fixed an issue with automatic profile importing on Cura 2.1 on Mac OSX +Fixed an inheritance issue for dual extrusion +Fixed an issue with "i" symbol updates +Fixed a freeze that can occur while printing via Wi-Fi [2.3.0] *Multi Extrusion Support diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index f82e210524..9d448e11bb 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -220,6 +220,9 @@ class CuraEngineBackend(Backend): # # \param job The start slice job that was just finished. def _onStartSliceCompleted(self, job): + if self._error_message: + self._error_message.hide() + # Note that cancelled slice jobs can still call this method. if self._start_slice_job is job: self._start_slice_job = None @@ -239,13 +242,33 @@ class CuraEngineBackend(Backend): if job.getResult() == StartSliceJob.StartJobResult.SettingError: if Application.getInstance().getPlatformActivity: - self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice with the current settings. Please check your settings for errors.")) + extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())) + error_keys = [] + for extruder in extruders: + error_keys.extend(extruder.getErrorKeys()) + if not extruders: + error_keys = self._global_container_stack.getErrorKeys() + error_labels = set() + definition_container = self._global_container_stack.getBottom() + for key in error_keys: + error_labels.add(definition_container.findDefinitions(key = key)[0].label) + + error_labels = ", ".join(error_labels) + self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice with the current settings. The following settings have errors: {0}".format(error_labels))) self._error_message.show() self.backendStateChange.emit(BackendState.Error) else: self.backendStateChange.emit(BackendState.NotStarted) return + if job.getResult() == StartSliceJob.StartJobResult.BuildPlateError: + if Application.getInstance().getPlatformActivity: + self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice because the prime tower or prime position(s) are invalid.")) + self._error_message.show() + self.backendStateChange.emit(BackendState.Error) + else: + self.backendStateChange.emit(BackendState.NotStarted) + if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice: if Application.getInstance().getPlatformActivity: self._error_message = Message(catalog.i18nc("@info:status", "Nothing to slice because none of the models fit the build volume. Please scale or rotate models to fit.")) @@ -296,7 +319,7 @@ class CuraEngineBackend(Backend): self._terminate() if error.getErrorCode() not in [Arcus.ErrorCode.BindFailedError, Arcus.ErrorCode.ConnectionResetError, Arcus.ErrorCode.Debug]: - Logger.log("e", "A socket error caused the connection to be reset") + Logger.log("w", "A socket error caused the connection to be reset") ## A setting has changed, so check if we must reslice. # diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 18c7fb1f5c..0319186518 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -25,6 +25,7 @@ class StartJobResult(IntEnum): SettingError = 3 NothingToSlice = 4 MaterialIncompatible = 5 + BuildPlateError = 6 ## Formatter class that handles token expansion in start/end gcod @@ -75,12 +76,12 @@ class StartSliceJob(Job): return # Don't slice if there is a setting with an error value. - if not Application.getInstance().getMachineManager().isActiveStackValid: + if Application.getInstance().getMachineManager().stacksHaveErrors: self.setResult(StartJobResult.SettingError) return if Application.getInstance().getBuildVolume().hasErrors(): - self.setResult(StartJobResult.SettingError) + self.setResult(StartJobResult.BuildPlateError) return for extruder_stack in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(stack.getId()): @@ -164,7 +165,12 @@ class StartSliceJob(Job): indices = mesh_data.getIndices() if indices is not None: #TODO: This is a very slow way of doing it! It also locks up the GUI. - verts = numpy.array([verts[vert_index] for face in indices for vert_index in face]) + flat_vert_list = [] + for face in indices: + for vert_index in face: + flat_vert_list.append(verts[vert_index]) + Job.yieldThread() + verts = numpy.array(flat_vert_list) else: verts = numpy.array(verts) @@ -238,6 +244,7 @@ class StartSliceJob(Job): else: # Normal case settings[key] = stack.getProperty(key, "value") + Job.yieldThread() start_gcode = settings["machine_start_gcode"] settings["material_bed_temp_prepend"] = "{material_bed_temperature}" not in start_gcode #Pre-compute material material_bed_temp_prepend and material_print_temp_prepend @@ -250,6 +257,7 @@ class StartSliceJob(Job): setting_message.value = self._expandGcodeTokens(key, value, settings) else: setting_message.value = str(value).encode("utf-8") + Job.yieldThread() ## Sends for some settings which extruder they should fallback to if not # set. @@ -266,6 +274,7 @@ class StartSliceJob(Job): setting_extruder = self._slice_message.addRepeatedMessage("limit_to_extruder") setting_extruder.name = key setting_extruder.extruder = extruder + Job.yieldThread() ## Check if a node has per object settings and ensure that they are set correctly in the message # \param node \type{SceneNode} Node to check. diff --git a/plugins/CuraProfileReader/CuraProfileReader.py b/plugins/CuraProfileReader/CuraProfileReader.py index d1be5b59a8..2198d73b22 100644 --- a/plugins/CuraProfileReader/CuraProfileReader.py +++ b/plugins/CuraProfileReader/CuraProfileReader.py @@ -99,4 +99,6 @@ class CuraProfileReader(ProfileReader): return [] filenames, outputs = profile_convert_funcs[0](serialized, profile_id) + if filenames is None and outputs is None: + return [] return list(zip(outputs, filenames)) diff --git a/plugins/GCodeProfileReader/GCodeProfileReader.py b/plugins/GCodeProfileReader/GCodeProfileReader.py index 24c92d08e9..abfef6e296 100644 --- a/plugins/GCodeProfileReader/GCodeProfileReader.py +++ b/plugins/GCodeProfileReader/GCodeProfileReader.py @@ -70,10 +70,19 @@ class GCodeProfileReader(ProfileReader): json_data = json.loads(serialized) - profile_strings = [json_data["global_quality"]] - profile_strings.extend(json_data.get("extruder_quality", [])) + profiles = [] + global_profile = readQualityProfileFromString(json_data["global_quality"]) - return [readQualityProfileFromString(profile_string) for profile_string in profile_strings] + # This is a fix for profiles created with 2.3.0 For some reason it added the "extruder" property to the + # global profile. + # The fix is simple and safe, as a global profile should never have the extruder entry. + if global_profile.getMetaDataEntry("extruder", None) is not None: + global_profile.setMetaDataEntry("extruder", None) + profiles.append(global_profile) + + for profile_string in json_data.get("extruder_quality", []): + profiles.append(readQualityProfileFromString(profile_string)) + return profiles ## Unescape a string which has been escaped for use in a gcode comment. # diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index fa6957a991..82512dd1e6 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -401,7 +401,7 @@ Item { } visibilityHandler: UM.SettingPreferenceVisibilityHandler {} expanded: [ "*" ] - exclude: [ "machine_settings" ] + exclude: [ "machine_settings", "command_line_settings" ] } delegate:Loader { diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py index 5834a5c606..865401804c 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py @@ -22,6 +22,7 @@ class PerObjectSettingsTool(Tool): self._advanced_mode = False self._multi_extrusion = False + self._single_model_selected = False Selection.selectionChanged.connect(self.propertyChanged) @@ -30,6 +31,8 @@ class PerObjectSettingsTool(Tool): Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged) self._onGlobalContainerChanged() + Selection.selectionChanged.connect(self._updateEnabled) + def event(self, event): super().event(event) @@ -102,4 +105,11 @@ class PerObjectSettingsTool(Tool): self._updateEnabled() def _updateEnabled(self): - Application.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, self._advanced_mode or self._multi_extrusion) \ No newline at end of file + selected_objects = Selection.getAllSelectedObjects() + if len(selected_objects)> 1: + self._single_model_selected = False + elif len(selected_objects) == 1 and selected_objects[0].callDecoration("isGroup"): + self._single_model_selected = False # Group is selected, so tool needs to be disabled + else: + self._single_model_selected = True + Application.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, (self._advanced_mode or self._multi_extrusion) and self._single_model_selected) \ No newline at end of file diff --git a/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py index 61f3bdb3be..3fdd6b3e3e 100644 --- a/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py +++ b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py @@ -20,7 +20,7 @@ class RemovableDriveOutputDevice(OutputDevice): super().__init__(device_id) self.setName(device_name) - self.setShortDescription(catalog.i18nc("@action:button", "Save to Removable Drive")) + self.setShortDescription(catalog.i18nc("@action:button Preceded by 'Ready to'.", "Save to Removable Drive")) self.setDescription(catalog.i18nc("@item:inlistbox", "Save to Removable Drive {0}").format(device_name)) self.setIconName("save_sd") self.setPriority(1) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index d98f631a2e..13dfe967b3 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -19,13 +19,12 @@ from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal, pyqtProperty from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") - class USBPrinterOutputDevice(PrinterOutputDevice): def __init__(self, serial_port): super().__init__(serial_port) self.setName(catalog.i18nc("@item:inmenu", "USB printing")) - self.setShortDescription(catalog.i18nc("@action:button", "Print via USB")) + self.setShortDescription(catalog.i18nc("@action:button Preceded by 'Ready to'.", "Print via USB")) self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB")) self.setIconName("print") self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB")) diff --git a/plugins/USBPrinting/avr_isp/intelHex.py b/plugins/USBPrinting/avr_isp/intelHex.py index 4444006b3d..a51c798d8e 100644 --- a/plugins/USBPrinting/avr_isp/intelHex.py +++ b/plugins/USBPrinting/avr_isp/intelHex.py @@ -5,6 +5,7 @@ See: http://en.wikipedia.org/wiki/Intel_HEX This is a python 3 conversion of the code created by David Braam for the Cura project. """ import io +from UM.Logger import Logger def readHex(filename): """ @@ -41,6 +42,6 @@ def readHex(filename): elif rec_type == 2: #Extended Segment Address Record extra_addr = int(line[9:13], 16) * 16 else: - print(rec_type, rec_len, addr, check_sum, line) + Logger.log("d", "%s, %s, %s, %s, %s", rec_type, rec_len, addr, check_sum, line) f.close() return data diff --git a/plugins/USBPrinting/avr_isp/ispBase.py b/plugins/USBPrinting/avr_isp/ispBase.py index fbd3bf091f..077cfc36e6 100644 --- a/plugins/USBPrinting/avr_isp/ispBase.py +++ b/plugins/USBPrinting/avr_isp/ispBase.py @@ -8,6 +8,7 @@ The ISP AVR programmer can load firmware into AVR chips. Which are commonly used """ from . import chipDB +from UM.Logger import Logger class IspBase(): """ @@ -22,11 +23,11 @@ class IspBase(): raise IspError("Chip with signature: " + str(self.getSignature()) + "not found") self.chipErase() - print("Flashing %i bytes" % len(flash_data)) + Logger.log("d", "Flashing %i bytes", len(flash_data)) self.writeFlash(flash_data) - print("Verifying %i bytes" % len(flash_data)) + Logger.log("d", "Verifying %i bytes", len(flash_data)) self.verifyFlash(flash_data) - print("Completed") + Logger.log("d", "Completed") def getSignature(self): """ diff --git a/plugins/USBPrinting/avr_isp/stk500v2.py b/plugins/USBPrinting/avr_isp/stk500v2.py index afdb50553e..ccbaa3de53 100644 --- a/plugins/USBPrinting/avr_isp/stk500v2.py +++ b/plugins/USBPrinting/avr_isp/stk500v2.py @@ -3,7 +3,6 @@ STK500v2 protocol implementation for programming AVR chips. The STK500v2 protocol is used by the ArduinoMega2560 and a few other Arduino platforms to load firmware. This is a python 3 conversion of the code created by David Braam for the Cura project. """ -import os import struct import sys import time @@ -11,6 +10,7 @@ import time from serial import Serial from serial import SerialException from serial import SerialTimeoutException +from UM.Logger import Logger from . import ispBase, intelHex @@ -27,7 +27,7 @@ class Stk500v2(ispBase.IspBase): self.close() try: self.serial = Serial(str(port), speed, timeout=1, writeTimeout=10000) - except SerialException as e: + except SerialException: raise ispBase.IspError("Failed to open serial port") except: raise ispBase.IspError("Unexpected error while connecting to serial port:" + port + ":" + str(sys.exc_info()[0])) @@ -84,14 +84,14 @@ class Stk500v2(ispBase.IspBase): #Set load addr to 0, in case we have more then 64k flash we need to enable the address extension page_size = self.chip["pageSize"] * 2 flash_size = page_size * self.chip["pageCount"] - print("Writing flash") + Logger.log("d", "Writing flash") if flash_size > 0xFFFF: self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00]) else: self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00]) load_count = (len(flash_data) + page_size - 1) / page_size for i in range(0, int(load_count)): - recv = self.sendMessage([0x13, page_size >> 8, page_size & 0xFF, 0xc1, 0x0a, 0x40, 0x4c, 0x20, 0x00, 0x00] + flash_data[(i * page_size):(i * page_size + page_size)]) + self.sendMessage([0x13, page_size >> 8, page_size & 0xFF, 0xc1, 0x0a, 0x40, 0x4c, 0x20, 0x00, 0x00] + flash_data[(i * page_size):(i * page_size + page_size)]) if self.progress_callback is not None: if self._has_checksum: self.progress_callback(i + 1, load_count) @@ -151,7 +151,6 @@ class Stk500v2(ispBase.IspBase): raise ispBase.IspError("Timeout") b = struct.unpack(">B", s)[0] checksum ^= b - #print(hex(b)) if state == "Start": if b == 0x1B: state = "GetSeq" @@ -183,11 +182,11 @@ class Stk500v2(ispBase.IspBase): def portList(): ret = [] import _winreg - key=_winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,"HARDWARE\\DEVICEMAP\\SERIALCOMM") + key=_winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,"HARDWARE\\DEVICEMAP\\SERIALCOMM") #@UndefinedVariable i=0 while True: try: - values = _winreg.EnumValue(key, i) + values = _winreg.EnumValue(key, i) #@UndefinedVariable except: return ret if "USBSER" in values[0]: @@ -206,7 +205,7 @@ def main(): """ Entry point to call the stk500v2 programmer from the commandline. """ import threading if sys.argv[1] == "AUTO": - print(portList()) + Logger.log("d", "portList(): ", repr(portList())) for port in portList(): threading.Thread(target=runProgrammer, args=(port,sys.argv[2])).start() time.sleep(5) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index d7d20db071..8be5a63151 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -5,6 +5,7 @@ import configparser #To read config files. import io #To write config files to strings as if they were files. import UM.VersionUpgrade +from UM.Logger import Logger ## Creates a new profile instance by parsing a serialised profile in version 1 # of the file format. diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 07acc5c37c..30d1307d01 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -165,7 +165,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): machine_container_map = {} machine_nozzle_map = {} - all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID")) + all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID"), base_file = self._id) for container in all_containers: definition_id = container.getDefinition().id if definition_id == "fdmprinter": @@ -209,7 +209,17 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): if not variant_containers: continue - builder.start("hotend", { "id": variant_containers[0].getName() }) + builder.start("hotend", {"id": variant_containers[0].getName()}) + + # Compatible is a special case, as it's added as a meta data entry (instead of an instance). + compatible = hotend.getMetaDataEntry("compatible") + if compatible is not None: + builder.start("setting", {"key": "hardware compatible"}) + if compatible: + builder.data("yes") + else: + builder.data("no") + builder.end("setting") for instance in hotend.findInstances(): if container.getInstance(instance.definition.key) and container.getProperty(instance.definition.key, "value") == instance.value: diff --git a/resources/definitions/bfb.def.json b/resources/definitions/bfb.def.json new file mode 100644 index 0000000000..b685b3d94d --- /dev/null +++ b/resources/definitions/bfb.def.json @@ -0,0 +1,40 @@ +{ + "id": "bfb", + "name": "BFB", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "BFB", + "category": "Other", + "file_formats": "text/x-gcode", + "platform_offset": [ 0, 0, 0] + }, + + "overrides": { + "speed_topbottom": { "default_value": 40 }, + "speed_print": { "default_value": 40 }, + "machine_extruder_count": { "default_value": 1 }, + "prime_tower_size": { "default_value": 7.745966692414834 }, + "machine_name": { "default_value": "BFB_Test" }, + "machine_heated_bed": { "default_value": false }, + "machine_nozzle_size": { "default_value": 0.5 }, + "speed_layer_0": { "default_value": 25 }, + "machine_width": { "default_value": 275 }, + "machine_gcode_flavor": { "default_value": "BFB" }, + "machine_depth": { "default_value": 265 }, + "speed_infill": { "default_value": 30 }, + "material_diameter": { "default_value": 1.7 }, + "machine_center_is_zero": { "default_value": true }, + "machine_height": { "default_value": 240 }, + "layer_height": { "default_value": 0.25 }, + "material_print_temperature": { "default_value": 200 }, + "retraction_amount": { "default_value": 0.05 }, + "speed_wall_0": { "default_value": 25 }, + "speed_travel": { "default_value": 50 }, + "infill_sparse_density": { "default_value": 10 }, + "layer_height_0": { "default_value": 0.5 }, + "speed_wall_x": { "default_value": 20 } + } +} diff --git a/resources/definitions/deltabot.def.json b/resources/definitions/deltabot.def.json new file mode 100644 index 0000000000..5d36857e9e --- /dev/null +++ b/resources/definitions/deltabot.def.json @@ -0,0 +1,35 @@ +{ + "id": "deltabot", + "name": "DeltaBot", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "Danny Lu", + "category": "Other", + "file_formats": "text/x-gcode", + "platform_offset": [ 0, 0, 0] + }, + + "overrides": { + "speed_travel": { "default_value": 150 }, + "prime_tower_size": { "default_value": 8.660254037844387 }, + "infill_sparse_density": { "default_value": 10 }, + "speed_wall_x": { "default_value": 30 }, + "speed_wall_0": { "default_value": 30 }, + "speed_topbottom": { "default_value": 30 }, + "layer_height": { "default_value": 0.2 }, + "machine_nozzle_size": { "default_value": 0.5 }, + "speed_print": { "default_value": 30 }, + "speed_infill": { "default_value": 30 }, + "machine_extruder_count": { "default_value": 1 }, + "machine_heated_bed": { "default_value": true }, + "machine_center_is_zero": { "default_value": true }, + "machine_height": { "default_value": 150 }, + "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_depth": { "default_value": 150 }, + "machine_width": { "default_value": 150 }, + "machine_name": { "default_value": "DeltaBot style" } + } +} diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index abe6a3e378..6591a9f3ec 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -169,7 +169,7 @@ }, "machine_extruder_count": { - "label": "Number extruders", + "label": "Number of Extruders", "description": "Number of extruder trains. An extruder train is the combination of a feeder, bowden tube, and nozzle.", "default_value": 1, "minimum_value": "1", @@ -283,6 +283,18 @@ "settable_per_extruder": false, "settable_per_meshgroup": false }, + "nozzle_disallowed_areas": + { + "label": "Nozzle Disallowed Areas", + "description": "A list of polygons with areas the nozzle is not allowed to enter.", + "type": "polygons", + "default_value": + [ + ], + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, "machine_head_polygon": { "label": "Machine head polygon", @@ -756,6 +768,18 @@ } } }, + "wall_0_wipe_dist": + { + "label": "Outer Wall Wipe Distance", + "description": "Distance of a travel move inserted after the outer wall, to hide the Z seam better.", + "unit": "mm", + "type": "float", + "default_value": 0.2, + "value": "machine_nozzle_size / 2", + "minimum_value": "0", + "maximum_value_warning": "machine_nozzle_size", + "settable_per_mesh": true + }, "top_bottom_thickness": { "label": "Top/Bottom Thickness", @@ -897,6 +921,17 @@ } } }, + "fill_perimeter_gaps": { + "label": "Fill Gaps Between Walls", + "description": "Fills the gaps between walls where no walls fit.", + "type": "enum", + "options": { + "nowhere": "Nowhere", + "everywhere": "Everywhere" + }, + "default_value": "everywhere", + "settable_per_mesh": true + }, "xy_offset": { "label": "Horizontal Expansion", @@ -979,6 +1014,7 @@ "cubic": "Cubic", "tetrahedral": "Tetrahedral", "concentric": "Concentric", + "concentric_3d": "Concentric 3D", "zigzag": "Zig Zag" }, "default_value": "grid", @@ -1135,7 +1171,22 @@ "minimum_value": "-273.15", "minimum_value_warning": "0", "maximum_value_warning": "260", - "enabled": "not (material_flow_dependent_temperature)", + "enabled": "not (material_flow_dependent_temperature) and machine_gcode_flavor != \"UltiGCode\"", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "material_print_temperature_layer_0": + { + "label": "Printing Temperature Initial Layer", + "description": "The temperature used for printing the first layer. Set at 0 to disable special handling of the initial layer.", + "unit": "°C", + "type": "float", + "default_value": 215, + "value": "material_print_temperature + 5", + "minimum_value": "-273.15", + "minimum_value_warning": "0", + "maximum_value_warning": "260", + "enabled": "machine_gcode_flavor != \"UltiGCode\"", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -1176,7 +1227,23 @@ "minimum_value": "-273.15", "minimum_value_warning": "0", "maximum_value_warning": "260", - "enabled": "machine_heated_bed", + "enabled": "machine_heated_bed and machine_gcode_flavor != \"UltiGCode\"", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, + "material_bed_temperature_layer_0": + { + "label": "Build Plate Temperature Initial Layer", + "description": "The temperature used for the heated build plate at the first layer.", + "unit": "°C", + "type": "float", + "resolve": "max(extruderValues('material_bed_temperature_layer_0'))", + "default_value": 60, + "minimum_value": "-273.15", + "minimum_value_warning": "0", + "maximum_value_warning": "260", + "enabled": "machine_heated_bed and machine_gcode_flavor != \"UltiGCode\"", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false @@ -1191,6 +1258,7 @@ "minimum_value": "0.0001", "minimum_value_warning": "0.4", "maximum_value_warning": "3.5", + "enabled": "machine_gcode_flavor != \"UltiGCode\"", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -1204,6 +1272,7 @@ "minimum_value": "5", "minimum_value_warning": "50", "maximum_value_warning": "150", + "enabled": "machine_gcode_flavor != \"UltiGCode\"", "settable_per_mesh": true }, "retraction_enable": @@ -1215,6 +1284,14 @@ "settable_per_mesh": false, "settable_per_extruder": true }, + "retract_at_layer_change":{ + "label": "Retract at Layer Change", + "description": "Retract the filament when the nozzle is moving to the next layer.", + "type": "bool", + "default_value": false, + "settable_per_mesh": false, + "settable_per_extruder": true + }, "retraction_amount": { "label": "Retraction Distance", @@ -1224,7 +1301,7 @@ "default_value": 6.5, "minimum_value_warning": "-0.0001", "maximum_value_warning": "10.0", - "enabled": "retraction_enable", + "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\"", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -1239,7 +1316,7 @@ "minimum_value_warning": "1", "maximum_value": "machine_max_feedrate_e", "maximum_value_warning": "70", - "enabled": "retraction_enable", + "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\"", "settable_per_mesh": false, "settable_per_extruder": true, "children": @@ -1255,7 +1332,7 @@ "maximum_value": "machine_max_feedrate_e", "minimum_value_warning": "1", "maximum_value_warning": "70", - "enabled": "retraction_enable", + "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\"", "value": "retraction_speed", "settable_per_mesh": false, "settable_per_extruder": true @@ -1271,7 +1348,7 @@ "maximum_value": "machine_max_feedrate_e", "minimum_value_warning": "1", "maximum_value_warning": "70", - "enabled": "retraction_enable", + "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\"", "value": "retraction_speed", "settable_per_mesh": false, "settable_per_extruder": true @@ -1341,6 +1418,7 @@ "minimum_value": "-273.15", "minimum_value_warning": "0", "maximum_value_warning": "260", + "enabled": "machine_extruder_count > 1 and machine_gcode_flavor != \"UltiGCode\"", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -2179,7 +2257,7 @@ "retraction_combing": { "label": "Combing Mode", - "description": "Combing keeps the nozzle within already printed areas when traveling. This results in slightly longer travel moves but reduces the need for retractions. If combing is off, the material will retract and the nozzle moves in a straight line to the next point. It is also possible to avoid combing over top/bottom skin areas by combing within the infill only. It is also possible to avoid combing over top/bottom skin areas by combing within the infill only.", + "description": "Combing keeps the nozzle within already printed areas when traveling. This results in slightly longer travel moves but reduces the need for retractions. If combing is off, the material will retract and the nozzle moves in a straight line to the next point. It is also possible to avoid combing over top/bottom skin areas by combing within the infill only.", "type": "enum", "options": { @@ -2217,6 +2295,42 @@ "settable_per_mesh": false, "settable_per_extruder": true }, + "start_layers_at_same_position": + { + "label": "Start Layers Near Same Point", + "description": "Start printing the objects in each layer near the same point, so that we don't start a new layer with printing the piece which the previous layer ended with. This makes for better overhangs and small parts, but increases printing time.", + "type": "bool", + "default_value": false, + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": true + }, + "layer_start_x": + { + "label": "Layer Start X", + "description": "The X coordinate of the position near where to start printing objects each layer.", + "unit": "mm", + "type": "float", + "default_value": 0.0, + "minimum_value": "0", + "enabled": "start_layers_at_same_position", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": true + }, + "layer_start_y": + { + "label": "Layer Start Y", + "description": "The Y coordinate of the position near where to start printing objects each layer.", + "unit": "mm", + "type": "float", + "default_value": 0.0, + "minimum_value": "0", + "enabled": "start_layers_at_same_position", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": true + }, "retraction_hop_enabled": { "label": "Z Hop when Retracted", "description": "Whenever a retraction is done, the build plate is lowered to create clearance between the nozzle and the print. It prevents the nozzle from hitting the print during travel moves, reducing the chance to knock the print from the build plate.", @@ -2290,6 +2404,20 @@ "settable_per_extruder": true, "children": { + "cool_fan_speed_0": + { + "label": "Initial Fan Speed", + "description": "The speed at which the fans spin at the start of the print. In subsequent layers the fan speed is gradually increased up to the layer corresponding to Regular Fan Speed at Height.", + "unit": "%", + "type": "float", + "minimum_value": "0", + "maximum_value": "100", + "value": "cool_fan_speed", + "default_value": 100, + "enabled": "cool_fan_enabled", + "settable_per_mesh": false, + "settable_per_extruder": true + }, "cool_fan_speed_min": { "label": "Regular Fan Speed", @@ -2335,7 +2463,7 @@ "cool_fan_full_at_height": { "label": "Regular Fan Speed at Height", - "description": "The height at which the fans spin on regular fan speed. At the layers below the fan speed gradually increases from zero to regular fan speed.", + "description": "The height at which the fans spin on regular fan speed. At the layers below the fan speed gradually increases from Initial Fan Speed to Regular Fan Speed.", "unit": "mm", "type": "float", "default_value": 0.5, @@ -2498,6 +2626,7 @@ "grid": "Grid", "triangles": "Triangles", "concentric": "Concentric", + "concentric_3d": "Concentric 3D", "zigzag": "Zig Zag" }, "default_value": "zigzag", @@ -2767,7 +2896,7 @@ "type": "float", "default_value": 0.4, "minimum_value": "0", - "minimum_value_warning": "support_interface_line_width", + "minimum_value_warning": "support_interface_line_width - 0.0001", "value": "0 if support_interface_density == 0 else (support_interface_line_width * 100) / support_interface_density * (2 if support_interface_pattern == 'grid' else (3 if support_interface_pattern == 'triangles' else 1))", "limit_to_extruder": "support_interface_extruder_nr", "enabled": "extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable", @@ -2787,6 +2916,7 @@ "grid": "Grid", "triangles": "Triangles", "concentric": "Concentric", + "concentric_3d": "Concentric 3D", "zigzag": "Zig Zag" }, "default_value": "concentric", @@ -2892,7 +3022,8 @@ { "skirt": "Skirt", "brim": "Brim", - "raft": "Raft" + "raft": "Raft", + "none": "None" }, "default_value": "brim", "resolve": "'raft' if 'raft' in extruderValues('adhesion_type') else ('brim' if 'brim' in extruderValues('adhesion_type') else 'skirt')", @@ -2905,7 +3036,7 @@ "description": "The extruder train to use for printing the skirt/brim/raft. This is used in multi-extrusion.", "type": "extruder", "default_value": "0", - "enabled": "machine_extruder_count > 1", + "enabled": "machine_extruder_count > 1 and resolveOrValue('adhesion_type') != 'none'", "settable_per_mesh": false, "settable_per_extruder": false }, @@ -3635,13 +3766,24 @@ "carve_multiple_volumes": { "label": "Remove Mesh Intersection", - "description": "Remove areas where multiple objecs are overlapping with each other. This is may be used if merged dual material objects overlap with each other.", + "description": "Remove areas where multiple objects are overlapping with each other. This may be used if merged dual material objects overlap with each other.", "type": "bool", "default_value": true, "value": "machine_extruder_count > 1", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": true + }, + "alternate_carve_order": + { + "label": "Alternate Mesh Removal", + "description": "With every layer switch from which model intersecting volumes are removed, so that the overlapping volumes become interwoven.", + "type": "bool", + "default_value": true, + "enabled": "carve_multiple_volumes", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": true } } }, @@ -3664,6 +3806,7 @@ "one_at_a_time": "One at a Time" }, "default_value": "all_at_once", + "enabled": "machine_extruder_count == 1", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false @@ -3902,6 +4045,14 @@ "limit_to_extruder": "support_infill_extruder_nr", "settable_per_mesh": true }, + "infill_hollow": + { + "label": "Hollow Out Objects", + "description": "Remove all infill and make the inside of the object eligible for support.", + "type": "bool", + "default_value": false, + "settable_per_mesh": true + }, "magic_fuzzy_skin_enabled": { "label": "Fuzzy Skin", @@ -4309,6 +4460,49 @@ "settable_per_meshgroup": false } } + }, + "command_line_settings": { + "label": "Command Line Settings", + "description": "Settings which are only used if CuraEngine isn't called from the Cura frontend.", + "type": "category", + "enabled": false, + "children": { + "center_object": { + "description": "Whether to center the object on the middle of the build platform (0,0), instead of using the coordinate system in which the object was saved.", + "type": "bool", + "label": "Center object", + "default_value": false, + "enabled": false + }, + "mesh_position_x": { + "description": "Offset applied to the object in the x direction.", + "type": "float", + "label": "Mesh position x", + "default_value": 0, + "enabled": false + }, + "mesh_position_y": { + "description": "Offset applied to the object in the y direction.", + "type": "float", + "label": "Mesh position y", + "default_value": 0, + "enabled": false + }, + "mesh_position_z": { + "description": "Offset applied to the object in the z direction. With this you can perform what was used to be called 'Object Sink'.", + "type": "float", + "label": "Mesh position z", + "default_value": 0, + "enabled": false + }, + "mesh_rotation_matrix": { + "label": "Mesh Rotation Matrix", + "description": "Transformation matrix to be applied to the model when loading it from file.", + "type": "str", + "default_value": "[[1,0,0], [0,1,0], [0,0,1]]", + "enabled": false + } + } } } } diff --git a/resources/definitions/innovo_inventor.def.json b/resources/definitions/innovo_inventor.def.json index 48c3ace5d9..40a2849979 100644 --- a/resources/definitions/innovo_inventor.def.json +++ b/resources/definitions/innovo_inventor.def.json @@ -54,7 +54,7 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": "G28 ; Home extruder\nM107 ; Turn off fan\nG90 ; Absolute positioning\nM82 ; Extruder in absolute mode\n{IF_BED}M190 S{BED}\n{IF_EXT0}M104 T0 S{TEMP0}\n{IF_EXT0}M109 T0 S{TEMP0}\n{IF_EXT1}M104 T1 S{TEMP1}\n{IF_EXT1}M109 T1 S{TEMP1}\nG32 S3 ; auto level\nG92 E0 ; Reset extruder position" + "default_value": "G28 ; Home extruder\nM107 ; Turn off fan\nG90 ; Absolute positioning\nM82 ; Extruder in absolute mode\nM190 S{material_bed_temperature}\nM104 T0 S{material_print_temperature}\nM109 T0 S{material_print_temperature}\nM104 T1 S{material_print_temperature}\nM109 T1 S{material_print_temperature}\nG32 S3 ; auto level\nG92 E0 ; Reset extruder position" }, "machine_end_gcode": { "default_value": "M104 S0 ; turn off extruders\nM140 S0 ; heated bed heater off\nG91 ; relative positioning\nG1 E-2 F5000; retract 2mm\nG28 Z; move bed down\nG90 ; absolute positioning\nM84 ; disable motors" diff --git a/resources/definitions/julia.def.json b/resources/definitions/julia.def.json new file mode 100644 index 0000000000..a0eda65bb7 --- /dev/null +++ b/resources/definitions/julia.def.json @@ -0,0 +1,52 @@ +{ + "id": "julia", + "name": "Julia", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "Fracktal", + "category": "Other", + "file_formats": "text/x-gcode", + "platform_offset": [ 0, 0, 0] + }, + + "overrides": { + "machine_start_gcode": { + "default_value": " ;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_sparse_density}\n ;metric values\n M107\n G28\n G29\n G90 ;absolute positioning\n G92 E0; reset extruder distance\n G1 Z5 F300 ;move nozzle up 5mm for safe homing\n G1 X0 Y0 Z0 F5000; move nozzle to home\n M300 S600P200\n M300 S800 P200\n M190 S{material_bed_temperature} ;Uncomment to add your own bed temperature line\n M109 S{material_print_temperature} ;Uncomment to add your own temperature line\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E3 ;extrude 3mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel}\n ;Put printing message on LCD screen\n M117 Printing...\n" + }, + "machine_end_gcode": { + "default_value": " M104 S0 ;extruder heater off\n M140 S0 ;heated bed heater off (if you have it)\n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning\n" + }, + "material_bed_temperature": { "default_value": 100 }, + "layer_height": { "default_value": 0.2 }, + "support_angle": { "default_value": 30 }, + "infill_overlap": { "default_value": 30 }, + "layer_height_0": { "default_value": 0.2 }, + "speed_print": { "default_value": 80 }, + "speed_wall_0": { "default_value": 30 }, + "speed_travel": { "default_value": 150 }, + "brim_line_count": { "default_value": 15 }, + "skin_overlap": { "default_value": 30 }, + "prime_tower_size": { "default_value": 8.660254037844387 }, + "material_diameter": { "default_value": 1.75 }, + "bottom_thickness": { "default_value": 0.8 }, + "retraction_amount": { "default_value": 3 }, + "speed_topbottom": { "default_value": 80 }, + "material_print_temperature": { "default_value": 230 }, + "support_pattern": { "default_value": "grid" }, + "speed_infill": { "default_value": 80 }, + "infill_sparse_density": { "default_value": 10 }, + "top_thickness": { "default_value": 0.8 }, + "machine_extruder_count": { "default_value": 1 }, + "retraction_combing": { "default_value": "off" }, + "machine_heated_bed": { "default_value": true }, + "machine_center_is_zero": { "default_value": false }, + "machine_height": { "default_value": 260 }, + "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_depth": { "default_value": 250 }, + "machine_width": { "default_value": 210 }, + "machine_name": { "default_value": "Julia V2" } + } +} diff --git a/resources/definitions/kupido.def.json b/resources/definitions/kupido.def.json new file mode 100644 index 0000000000..97be1a0152 --- /dev/null +++ b/resources/definitions/kupido.def.json @@ -0,0 +1,38 @@ +{ + "id": "kupido", + "name": "Kupido", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "Kupido", + "category": "Other", + "file_formats": "text/x-gcode", + "platform_offset": [ 0, 0, 0] + }, + + "overrides": { + "machine_name": { "default_value": "Kupido" }, + "machine_start_gcode": { + "default_value": " ;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_sparse_density}\n ;M190 S{material_bed_temperature} ;Uncomment to add your own bed temperature line\n ;M109 S{material_print_temperature} ;Uncomment to add your own temperature line\n G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X Y to endstops\n G28 Z0 ;move Z to endstops\n G1 Z20.0 F40 ;move the platform down 20mm\n G1 Y0 X170 F{speed_travel}\n G92 E0 ;zero the extruded length\n G1 F200 E10 ;extrude 3mm of feed stock\n G92 E0 ;zero the extruded length again\n G4 P7000\n G1 F{speed_travel}\n ;Put printing message on LCD screen\n M117 Printing...\n" + }, + "machine_end_gcode": { + "default_value": " M104 S0 ;extruder heater off\n M140 S0 ;heated bed heater off (if you have it)\n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning\n" + }, + "prime_tower_size": { "default_value": 8.660254037844387 }, + "retraction_speed": { "default_value": 60 }, + "material_bed_temperature": { "default_value": 60 }, + "speed_wall_x": { "default_value": 40 }, + "skirt_line_count": { "default_value": 2 }, + "retraction_min_travel": { "default_value": 2 }, + "speed_wall_0": { "default_value": 30 }, + "material_print_temperature": { "default_value": 220 }, + "brim_line_count": { "default_value": 15 }, + "retraction_amount": { "default_value": 3.6 }, + "speed_topbottom": { "default_value": 20 }, + "layer_height": { "default_value": 0.2 }, + "speed_print": { "default_value": 30 }, + "speed_infill": { "default_value": 30 } + } +} diff --git a/resources/definitions/makerbotreplicator.def.json b/resources/definitions/makerbotreplicator.def.json new file mode 100644 index 0000000000..d762db67fc --- /dev/null +++ b/resources/definitions/makerbotreplicator.def.json @@ -0,0 +1,31 @@ +{ + "id": "makerbotreplicator", + "name": "MakerBotReplicator", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "MakerBot", + "category": "Other", + "file_formats": "application/x3g", + "platform_offset": [ 0, 0, 0] + }, + + "overrides": { + "prime_tower_size": { "default_value": 10.0 }, + "infill_sparse_density": { "default_value": 10 }, + "speed_travel": { "default_value": 150 }, + "material_diameter": { "default_value": 1.75 }, + "layer_height": { "default_value": 0.15 }, + "material_print_temperature": { "default_value": 220 }, + "machine_extruder_count": { "default_value": 1 }, + "machine_heated_bed": { "default_value": true }, + "machine_center_is_zero": { "default_value": false }, + "machine_height": { "default_value": 150 }, + "machine_gcode_flavor": { "default_value": "MakerBot" }, + "machine_depth": { "default_value": 145 }, + "machine_width": { "default_value": 225 }, + "machine_name": { "default_value": "MakerBot Replicator" } + } +} diff --git a/resources/definitions/ord.def.json b/resources/definitions/ord.def.json new file mode 100644 index 0000000000..bedd8c3121 --- /dev/null +++ b/resources/definitions/ord.def.json @@ -0,0 +1,41 @@ +{ + "id": "ord", + "name": "RoVa3D", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "ORD Solutions", + "category": "Other", + "file_formats": "text/x-gcode", + "machine_extruder_trains": + { + "0": "ord_extruder_0", + "1": "ord_extruder_1", + "2": "ord_extruder_2", + "3": "ord_extruder_3", + "4": "ord_extruder_4" + }, + "platform_offset": [ 0, 0, 0] + }, + + "overrides": { + "material_bed_temperature": { "default_value": 60 }, + "prime_tower_size": { "default_value": 7.0710678118654755 }, + "infill_sparse_density": { "default_value": 15 }, + "speed_travel": { "default_value": 150 }, + "material_diameter": { "default_value": 1.75 }, + "layer_height": { "default_value": 0.3 }, + "machine_nozzle_size": { "default_value": 0.35 }, + "material_print_temperature": { "default_value": 240 }, + "machine_extruder_count": { "default_value": 5 }, + "machine_heated_bed": { "default_value": true }, + "machine_center_is_zero": { "default_value": false }, + "machine_height": { "default_value": 200 }, + "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_depth": { "default_value": 250 }, + "machine_width": { "default_value": 215 }, + "machine_name": { "default_value": "RoVa3D" } + } +} diff --git a/resources/definitions/punchtec_connect_xl.def.json b/resources/definitions/punchtec_connect_xl.def.json new file mode 100644 index 0000000000..ce4245a04f --- /dev/null +++ b/resources/definitions/punchtec_connect_xl.def.json @@ -0,0 +1,41 @@ +{ + "id": "punchtec_connect_xl", + "name": "Punchtec Connect XL", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "Punchtec", + "category": "Other", + "file_formats": "text/x-gcode", + "machine_extruder_trains": + { + "0": "punchtec_connect_xl_extruder_0", + "1": "punchtec_connect_xl_extruder_1" + }, + "platform_offset": [ 0, 0, 0] + }, + + "overrides": { + "machine_head_polygon": { "default_value": [[ 0, 0], [ 0, 0], [ 0, 0], [ 0, 0]] }, + "speed_travel": { "default_value": 150 }, + "prime_tower_size": { "default_value": 8.660254037844387 }, + "speed_wall_x": { "default_value": 40 }, + "speed_wall_0": { "default_value": 40 }, + "material_diameter": { "default_value": 1.75 }, + "speed_topbottom": { "default_value": 40 }, + "layer_height": { "default_value": 0.2 }, + "material_print_temperature": { "default_value": 195 }, + "speed_print": { "default_value": 40 }, + "speed_infill": { "default_value": 40 }, + "machine_extruder_count": { "default_value": 2 }, + "machine_heated_bed": { "default_value": true }, + "machine_center_is_zero": { "default_value": false }, + "machine_height": { "default_value": 200 }, + "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_depth": { "default_value": 304 }, + "machine_width": { "default_value": 304 }, + "machine_name": { "default_value": "Punchtec Connect XL" } + } +} diff --git a/resources/definitions/rigid3d.def.json b/resources/definitions/rigid3d.def.json new file mode 100644 index 0000000000..b167646f6e --- /dev/null +++ b/resources/definitions/rigid3d.def.json @@ -0,0 +1,53 @@ +{ + "id": "rigid3d", + "name": "Rigid3D", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "Rigid3D", + "category": "Other", + "file_formats": "text/x-gcode", + "platform_offset": [ 0, 0, 0] + }, + + "overrides": { + "machine_start_gcode": { + "default_value": " ; -- START GCODE --\n G21\n G28 ; Home extruder\n G29 ; Autolevel bed\n M107 ; Turn off fan\n G90 ; Absolute positioning\n M82 ; Extruder in absolute mode\n G92 E0 ; Reset extruder position\n ; -- end of START GCODE --\n\n" + }, + "machine_end_gcode": { + "default_value": " ; -- END GCODE --\n G1 X0 Y230 ; Get extruder out of way.\n M107 ; Turn off fan\n G91 ; Relative positioning\n G0 Z20 ; Lift extruder up\n T0\n G1 E-1 ; Reduce filament pressure\n M104 T0 S0 ; Turn ectruder heater off\n G90 ; Absolute positioning\n G92 E0 ; Reset extruder position\n M140 S0 ; Disable heated bed\n M84 ; Turn steppers off\n ; -- end of END GCODE --\n" + }, + "machine_head_polygon": { "default_value": [[ 22, 67], [ 22, 51], [ 36, 51], [ 36, 67]] }, + "skirt_gap": { "default_value": 5.0 }, + "cool_min_layer_time": { "default_value": 10 }, + "prime_tower_size": { "default_value": 7.745966692414834 }, + "speed_wall_x": { "default_value": 40 }, + "speed_travel": { "default_value": 100 }, + "bottom_thickness": { "default_value": 0.75 }, + "material_diameter": { "default_value": 1.75 }, + "layer_height_0": { "default_value": 0.25 }, + "support_angle": { "default_value": 45 }, + "material_bed_temperature": { "default_value": 100 }, + "top_thickness": { "default_value": 0.75 }, + "material_print_temperature": { "default_value": 235 }, + "retraction_speed": { "default_value": 60.0 }, + "wall_thickness": { "default_value": 0.8 }, + "retraction_min_travel": { "default_value": 2 }, + "speed_wall_0": { "default_value": 30 }, + "retraction_amount": { "default_value": 1 }, + "speed_topbottom": { "default_value": 30 }, + "layer_height": { "default_value": 0.25 }, + "speed_print": { "default_value": 40 }, + "speed_infill": { "default_value": 40 }, + "machine_extruder_count": { "default_value": 1 }, + "machine_heated_bed": { "default_value": true }, + "machine_center_is_zero": { "default_value": false }, + "machine_height": { "default_value": 210 }, + "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_depth": { "default_value": 250 }, + "machine_width": { "default_value": 250 }, + "machine_name": { "default_value": "Rigid3D" } + } +} diff --git a/resources/definitions/rigid3d_3rdgen.def.json b/resources/definitions/rigid3d_3rdgen.def.json new file mode 100644 index 0000000000..e7c73ed54d --- /dev/null +++ b/resources/definitions/rigid3d_3rdgen.def.json @@ -0,0 +1,50 @@ +{ + "id": "rigid3d_3rdgen", + "name": "Rigid3D 3rdGen", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "Rigid3D", + "category": "Other", + "file_formats": "text/x-gcode", + "platform_offset": [ 0, 0, 0] + }, + + "overrides": { + "machine_start_gcode": { + "default_value": " ; -- START GCODE --\n G21\n G28 ; Home extruder\n G29 ; Autolevel bed\n M107 ; Turn off fan\n G90 ; Absolute positioning\n M82 ; Extruder in absolute mode\n G92 E0 ; Reset extruder position\n ; -- end of START GCODE --\n\n" + }, + "machine_end_gcode": { + "default_value": " ; -- END GCODE --\n G1 X0 Y230 ; Get extruder out of way.\n M107 ; Turn off fan\n G91 ; Relative positioning\n G0 Z20 ; Lift extruder up\n T0\n G1 E-1 ; Reduce filament pressure\n M104 T0 S0 ; Turn extruder heater off\n G90 ; Absolute positioning\n G92 E0 ; Reset extruder position\n M140 S0 ; Disable heated bed\n M84 ; Turn steppers off\n ; -- end of END GCODE --\n" + }, + "machine_head_polygon": { "default_value": [[ 18, 0], [ 18, 65], [ 32, 65], [ 32, 0]] }, + "cool_min_layer_time": { "default_value": 10 }, + "prime_tower_size": { "default_value": 7.745966692414834 }, + "skirt_gap": { "default_value": 5.0 }, + "speed_travel": { "default_value": 120 }, + "bottom_thickness": { "default_value": 0.75 }, + "material_diameter": { "default_value": 1.75 }, + "layer_height_0": { "default_value": 0.25 }, + "support_angle": { "default_value": 45 }, + "material_bed_temperature": { "default_value": 100 }, + "retraction_min_travel": { "default_value": 2 }, + "speed_wall_0": { "default_value": 30 }, + "retraction_speed": { "default_value": 60.0 }, + "wall_thickness": { "default_value": 0.8 }, + "material_print_temperature": { "default_value": 235 }, + "retraction_amount": { "default_value": 1 }, + "speed_topbottom": { "default_value": 25 }, + "layer_height": { "default_value": 0.25 }, + "top_thickness": { "default_value": 0.75 }, + "machine_extruder_count": { "default_value": 1 }, + "machine_heated_bed": { "default_value": true }, + "machine_center_is_zero": { "default_value": false }, + "machine_height": { "default_value": 240 }, + "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_depth": { "default_value": 290 }, + "machine_width": { "default_value": 275 }, + "machine_name": { "default_value": "Rigid3D 3rd Geneartion" } + } +} diff --git a/resources/definitions/rigid3d_hobby.def.json b/resources/definitions/rigid3d_hobby.def.json new file mode 100644 index 0000000000..09ba77f63c --- /dev/null +++ b/resources/definitions/rigid3d_hobby.def.json @@ -0,0 +1,47 @@ +{ + "id": "rigid3d_hobby", + "name": "Rigid3D Hobby", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "Rigid3D", + "category": "Other", + "file_formats": "text/x-gcode", + "platform_offset": [ 0, 0, 0] + }, + + "overrides": { + "machine_head_polygon": { "default_value": [[ 16, 30], [ 16, 45], [ 16, 45], [ 16, 30]] }, + "prime_tower_size": { "default_value": 8.660254037844387 }, + "speed_travel": { "default_value": 40 }, + "skirt_gap": { "default_value": 5.0 }, + "cool_min_layer_time": { "default_value": 15 }, + "support_pattern": { "default_value": "grid" }, + "material_diameter": { "default_value": 1.75 }, + "layer_height_0": { "default_value": 0.25 }, + "speed_wall_x": { "default_value": 30 }, + "skirt_line_count": { "default_value": 2 }, + "support_angle": { "default_value": 45 }, + "speed_topbottom": { "default_value": 20 }, + "material_print_temperature": { "default_value": 205 }, + "retraction_speed": { "default_value": 80 }, + "wall_thickness": { "default_value": 0.8 }, + "retraction_min_travel": { "default_value": 2 }, + "speed_wall_0": { "default_value": 20 }, + "retraction_amount": { "default_value": 2 }, + "speed_layer_0": { "default_value": 15 }, + "layer_height": { "default_value": 0.2 }, + "speed_print": { "default_value": 30 }, + "speed_infill": { "default_value": 30 }, + "machine_extruder_count": { "default_value": 1 }, + "machine_heated_bed": { "default_value": false }, + "machine_center_is_zero": { "default_value": false }, + "machine_height": { "default_value": 150 }, + "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_depth": { "default_value": 150 }, + "machine_width": { "default_value": 150 }, + "machine_name": { "default_value": "Rigid3D Hobby" } + } +} diff --git a/resources/definitions/rigid3d_zero.def.json b/resources/definitions/rigid3d_zero.def.json new file mode 100644 index 0000000000..d5f8c1ef6a --- /dev/null +++ b/resources/definitions/rigid3d_zero.def.json @@ -0,0 +1,53 @@ +{ + "id": "rigid3d_zero", + "name": "Rigid3D Zero", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "Rigid3D", + "category": "Other", + "file_formats": "text/x-gcode", + "platform_offset": [ 0, 0, 0] + }, + + "overrides": { + "machine_start_gcode": { + "default_value": " ; -- START GCODE --\n G21\n G28 ; Home extruder\n G29 ; Autolevel bed\n M107 ; Turn off fan\n G90 ; Absolute positioning\n M82 ; Extruder in absolute mode\n G92 E0 ; Reset extruder position\n ; -- end of START GCODE --\n\n" + }, + "machine_end_gcode": { + "default_value": " ; -- END GCODE --\n G1 X0 Y230 ; Get extruder out of way.\n M107 ; Turn off fan\n G91 ; Relative positioning\n G0 Z20 ; Lift extruder up\n T0\n G1 E-1 ; Reduce filament pressure\n M104 T0 S0 ; Turn ectruder heater off\n G90 ; Absolute positioning\n G92 E0 ; Reset extruder position\n M140 S0 ; Disable heated bed\n M84 ; Turn steppers off\n ; -- end of END GCODE --\n" + }, + "machine_head_polygon": { "default_value": [[ 40, 15], [ 40, 60], [ 30, 60], [ 30, 15]] }, + "support_pattern": { "default_value": "grid" }, + "cool_min_layer_time": { "default_value": 10 }, + "speed_travel": { "default_value": 80 }, + "support_angle": { "default_value": 45 }, + "retraction_min_travel": { "default_value": 2 }, + "speed_wall_0": { "default_value": 20 }, + "speed_layer_0": { "default_value": 15 }, + "speed_infill": { "default_value": 30 }, + "speed_topbottom": { "default_value": 30 }, + "prime_tower_size": { "default_value": 7.745966692414834 }, + "skirt_line_count": { "default_value": 2 }, + "speed_wall_x": { "default_value": 30 }, + "material_diameter": { "default_value": 1.75 }, + "bottom_thickness": { "default_value": 0.75 }, + "layer_height_0": { "default_value": 0.25 }, + "top_thickness": { "default_value": 0.75 }, + "wall_thickness": { "default_value": 0.8 }, + "material_print_temperature": { "default_value": 195 }, + "retraction_amount": { "default_value": 1.5 }, + "skirt_gap": { "default_value": 5.0 }, + "layer_height": { "default_value": 0.25 }, + "speed_print": { "default_value": 30 }, + "machine_extruder_count": { "default_value": 1 }, + "machine_center_is_zero": { "default_value": false }, + "machine_height": { "default_value": 190 }, + "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_depth": { "default_value": 250 }, + "machine_width": { "default_value": 250 }, + "machine_name": { "default_value": "Rigid3D Zero" } + } +} diff --git a/resources/definitions/rigidbot.def.json b/resources/definitions/rigidbot.def.json index 8b268410f9..9dbde35921 100644 --- a/resources/definitions/rigidbot.def.json +++ b/resources/definitions/rigidbot.def.json @@ -39,10 +39,10 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": ";Sliced at: {day} {date} {time}\n;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_sparse_density}\n;Print time: {print_time}\n;Filament used: {filament_amount}m {filament_weight}g\n;Filament cost: {filament_cost}\n;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{print_temperature} ;Uncomment to add your own temperature line\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nM205 X8 ;X/Y Jerk settings\nG1 Z15.0 F{travel_speed} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E7 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{travel_speed}\n;Put printing message on LCD screen\nM117 Rigibot Printing..." + "default_value": ";Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_sparse_density}\n;M190 S{material_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{material_print_temperature} ;Uncomment to add your own temperature line\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nM205 X8 ;X/Y Jerk settings\nG1 Z15.0 F{travel_speed} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E7 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{speed_travel}\n;Put printing message on LCD screen\nM117 Rigibot Printing..." }, "machine_end_gcode": { - "default_value": ";End GCode\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+10 E-1 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}" + "default_value": ";End GCode\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+10 E-1 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning" }, "layer_height": { "default_value": 0.2 diff --git a/resources/definitions/rigidbot_big.def.json b/resources/definitions/rigidbot_big.def.json index f8cf1e1da0..6ac84cbde1 100644 --- a/resources/definitions/rigidbot_big.def.json +++ b/resources/definitions/rigidbot_big.def.json @@ -42,10 +42,10 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": ";Sliced at: {day} {date} {time}\n;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_sparse_density}\n;Print time: {print_time}\n;Filament used: {filament_amount}m {filament_weight}g\n;Filament cost: {filament_cost}\n;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{print_temperature} ;Uncomment to add your own temperature line\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nM205 X8 ;X/Y Jerk settings\nG1 Z15.0 F{travel_speed} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E7 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{travel_speed}\n;Put printing message on LCD screen\nM117 Rigibot Printing..." + "default_value": ";Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {infill_sparse_density}\n;Print time: {print_time}\n;M190 S{material_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{material_print_temperature} ;Uncomment to add your own temperature line\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nM205 X8 ;X/Y Jerk settings\nG1 Z15.0 F{speed_travel} ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E7 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{speed_travel}\n;Put printing message on LCD screen\nM117 Rigibot Printing..." }, "machine_end_gcode": { - "default_value": ";End GCode\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+10 E-1 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}" + "default_value": ";End GCode\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+10 E-1 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning" }, "layer_height": { "default_value": 0.2 diff --git a/resources/definitions/robo_3d_r1.def.json b/resources/definitions/robo_3d_r1.def.json new file mode 100644 index 0000000000..b137b2054b --- /dev/null +++ b/resources/definitions/robo_3d_r1.def.json @@ -0,0 +1,61 @@ +{ + "id": "robo_3d_r1", + "name": "Robo 3D R1", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "Robo 3D", + "category": "Other", + "file_formats": "text/x-gcode", + "platform_offset": [ 0, 0, 0] + }, + + "overrides": { + "machine_start_gcode": { + "default_value": " G92 E0 ;\n M565 Z-1 ;\n G1 Z5 F5000 ;\n G29 ;\n" + }, + "machine_end_gcode": { + "default_value": " M104 S0 ;extruder heater off\n M140 S0 ;heated bed heater off (if you have it)\n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning\n" + }, + "cool_min_layer_time": { "default_value": 7 }, + "speed_topbottom": { "default_value": 40 }, + "retraction_speed": { "default_value": 50 }, + "layer_0_z_overlap": { "default_value": 0.2 }, + "cool_min_speed": { "default_value": 19 }, + "material_bed_temperature": { "default_value": 60 }, + "support_angle": { "default_value": 50 }, + "speed_layer_0": { "default_value": 30 }, + "line_width": { "default_value": 0.4 }, + "speed_infill": { "default_value": 60 }, + "prime_tower_size": { "default_value": 8.660254037844387 }, + "support_enable": { "default_value": true }, + "cool_fan_full_at_height": { "default_value": 0.1 }, + "material_diameter": { "default_value": 1.75 }, + "bottom_thickness": { "default_value": 1.2 }, + "raft_airgap": { "default_value": 0.2 }, + "layer_height_0": { "default_value": 0.15 }, + "top_thickness": { "default_value": 1.2 }, + "speed_wall_0": { "default_value": 40 }, + "retraction_min_travel": { "default_value": 5 }, + "material_flow": { "default_value": 100 }, + "infill_sparse_density": { "default_value": 10 }, + "wall_thickness": { "default_value": 1.2 }, + "material_print_temperature": { "default_value": 190 }, + "retraction_amount": { "default_value": 3 }, + "layer_height": { "default_value": 0.2 }, + "speed_print": { "default_value": 40 }, + "machine_extruder_count": { "default_value": 1 }, + "retraction_combing": { "default_value": "off" }, + "machine_heated_bed": { "default_value": true }, + "machine_center_is_zero": { "default_value": false }, + "machine_height": { "default_value": 210 }, + "adhesion_type": { "default_value": "raft" }, + "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_depth": { "default_value": 245 }, + "machine_width": { "default_value": 225 }, + "support_z_distance": { "default_value": 0.22 }, + "machine_name": { "default_value": "ROBO 3D R1" } + } +} diff --git a/resources/definitions/ultimaker2.def.json b/resources/definitions/ultimaker2.def.json index c26a3a8856..84e09113f3 100644 --- a/resources/definitions/ultimaker2.def.json +++ b/resources/definitions/ultimaker2.def.json @@ -86,12 +86,6 @@ "machine_nozzle_expansion_angle": { "default_value": 45 }, - "material_print_temperature": { - "enabled": "not (material_flow_dependent_temperature) and machine_gcode_flavor != \"UltiGCode\"" - }, - "material_bed_temperature": { - "enabled": "machine_heated_bed and machine_gcode_flavor != \"UltiGCode\"" - }, "machine_max_feedrate_x": { "default_value": 300 }, @@ -106,24 +100,6 @@ }, "machine_acceleration": { "default_value": 3000 - }, - "material_diameter": { - "enabled": "machine_gcode_flavor != \"UltiGCode\"" - }, - "material_flow": { - "enabled": "machine_gcode_flavor != \"UltiGCode\"" - }, - "retraction_amount": { - "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\"" - }, - "retraction_speed": { - "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\"" - }, - "retraction_retract_speed": { - "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\"" - }, - "retraction_prime_speed": { - "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\"" } } } diff --git a/resources/definitions/ultimaker3.def.json b/resources/definitions/ultimaker3.def.json index 8be8751d68..1356efff91 100644 --- a/resources/definitions/ultimaker3.def.json +++ b/resources/definitions/ultimaker3.def.json @@ -34,7 +34,7 @@ "overrides": { "machine_name": { "default_value": "Ultimaker 3" }, - "machine_width": { "default_value": 215 }, + "machine_width": { "default_value": 233 }, "machine_depth": { "default_value": 215 }, "machine_height": { "default_value": 200 }, "machine_heated_bed": { "default_value": true }, @@ -44,10 +44,10 @@ { "default_value": [ - [ -40, 10 ], - [ -40, -30 ], - [ 60, 10 ], - [ 60, -30 ] + [ -29, 6.1 ], + [ -29, -33.9 ], + [ 71, 6.1 ], + [ 71, -33.9 ] ] }, "machine_gcode_flavor": { "default_value": "Griffin" }, @@ -57,15 +57,12 @@ "machine_acceleration": { "default_value": 3000 }, "gantry_height": { "default_value": 60 }, "machine_disallowed_areas": { "default_value": [ - [[-91.5, -115], [-115, -115], [-115, -104.6], [-91.5, -104.6]], - [[-99.5, -104.6], [-115, -104.6], [-115, 104.6], [-99.5, 104.6]], - [[-94.5, 104.6], [-115, 104.6], [-115, 105.5], [-94.5, 105.5]], - [[-91.4, 105.5], [-115, 105.5], [-115, 115], [-91.4, 115]], - - [[77.3, -115], [77.3, -98.6], [115, -98.6], [115, -115]], - [[97.2, -98.6], [97.2, -54.5], [113, -54.5], [113, -98.6]], - [[100.5, -54.5], [100.5, 99.3], [115, 99.3], [115, -54.5]], - [[77, 99.3], [77, 115], [115, 115], [115, 99.3]] + [[92.8, -53.4], [92.8, -97.5], [116.5, -97.5], [116.5, -53.4]], + [[73.8, 107.5], [73.8, 100.5], [116.5, 100.5], [116.5, 107.5]], + [[74.6, 107.5], [74.6, 100.5], [116.5, 100.5], [116.5, 107.5]], + [[74.9, -97.5], [74.9, -107.5], [116.5, -107.5], [116.5, -97.5]], + [[-116.5, -103.5], [-116.5, -107.5], [-100.9, -107.5], [-100.9, -103.5]], + [[-116.5, 105.8], [-96.9, 105.8], [-96.9, 107.5], [-116.5, 107.5]] ]}, "machine_extruder_count": { "default_value": 2 }, "extruder_prime_pos_abs": { "default_value": true }, @@ -74,10 +71,6 @@ "prime_tower_position_x": { "default_value": 175 }, "prime_tower_position_y": { "default_value": 179 }, - "extruder_prime_pos_x": { "enabled": false }, - "extruder_prime_pos_y": { "enabled": false }, - "print_sequence": {"enabled": false}, - "acceleration_enabled": { "value": "True" }, "acceleration_layer_0": { "value": "acceleration_topbottom" }, "acceleration_prime_tower": { "value": "math.ceil(acceleration_print * 2000 / 4000)" }, @@ -106,6 +99,8 @@ "jerk_wall": { "value": "math.ceil(jerk_print * 10 / 25)" }, "jerk_wall_0": { "value": "math.ceil(jerk_wall * 5 / 10)" }, "layer_height_0": { "value": "round(machine_nozzle_size / 1.5, 2)" }, + "layer_start_x": { "value": "sum(extruderValues('machine_extruder_start_pos_x')) / len(extruderValues('machine_extruder_start_pos_x'))" }, + "layer_start_y": { "value": "sum(extruderValues('machine_extruder_start_pos_y')) / len(extruderValues('machine_extruder_start_pos_y'))" }, "line_width": { "value": "machine_nozzle_size * 0.875" }, "machine_min_cool_heat_time_window": { "value": "15" }, "material_print_temperature": { "value": "200" }, diff --git a/resources/definitions/ultimaker3_extended.def.json b/resources/definitions/ultimaker3_extended.def.json index 13ce93b05a..31bf2417e4 100644 --- a/resources/definitions/ultimaker3_extended.def.json +++ b/resources/definitions/ultimaker3_extended.def.json @@ -14,7 +14,7 @@ "platform_texture": "Ultimaker3Extendedbackplate.png", "platform_offset": [0, 0, 0], "has_machine_quality": true, - "has_machine_materials": true, + "has_machine_materials": true, "has_variant_materials": true, "has_materials": true, "has_variants": true, diff --git a/resources/definitions/ultimaker_original_dual.def.json b/resources/definitions/ultimaker_original_dual.def.json index 48d2436b92..ab2f3ddf46 100644 --- a/resources/definitions/ultimaker_original_dual.def.json +++ b/resources/definitions/ultimaker_original_dual.def.json @@ -72,9 +72,6 @@ "machine_extruder_count": { "default_value": 2 }, - "print_sequence": { - "enabled": false - }, "prime_tower_position_x": { "default_value": 185 }, diff --git a/resources/definitions/zone3d_printer.def.json b/resources/definitions/zone3d_printer.def.json new file mode 100644 index 0000000000..1663ffdf2b --- /dev/null +++ b/resources/definitions/zone3d_printer.def.json @@ -0,0 +1,30 @@ +{ + "id": "zone3d_printer", + "name": "Zone3d Printer", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Ultimaker", + "manufacturer": "Unknown", + "category": "Other", + "file_formats": "text/x-gcode", + "platform_offset": [ 0, 0, 0] + }, + + "overrides": { + "material_diameter": { "default_value": 1.75 }, + "prime_tower_size": { "default_value": 10.350983390135314 }, + "material_print_temperature": { "default_value": 260 }, + "layer_height": { "default_value": 0.14 }, + "speed_travel": { "default_value": 150 }, + "machine_extruder_count": { "default_value": 1 }, + "machine_heated_bed": { "default_value": true }, + "machine_center_is_zero": { "default_value": false }, + "machine_height": { "default_value": 210 }, + "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_depth": { "default_value": 220 }, + "machine_width": { "default_value": 240 }, + "machine_name": { "default_value": "Zone3D Printer" } + } +} diff --git a/resources/extruders/ord_extruder_0.def.json b/resources/extruders/ord_extruder_0.def.json new file mode 100644 index 0000000000..c3f8d75cc0 --- /dev/null +++ b/resources/extruders/ord_extruder_0.def.json @@ -0,0 +1,19 @@ +{ + "id": "ord_extruder_0", + "version": 2, + "name": "0", + "inherits": "fdmextruder", + "metadata": { + "machine": "ord", + "position": "0" + }, + + "overrides": { + "extruder_nr": { + "default_value": 0, + "maximum_value": "4" + }, + "machine_nozzle_offset_x": { "default_value": 0.0 }, + "machine_nozzle_offset_y": { "default_value": 0.0 } + } +} diff --git a/resources/extruders/ord_extruder_1.def.json b/resources/extruders/ord_extruder_1.def.json new file mode 100644 index 0000000000..9b7990605d --- /dev/null +++ b/resources/extruders/ord_extruder_1.def.json @@ -0,0 +1,19 @@ +{ + "id": "ord_extruder_1", + "version": 2, + "name": "1", + "inherits": "fdmextruder", + "metadata": { + "machine": "ord", + "position": "1" + }, + + "overrides": { + "extruder_nr": { + "default_value": 1, + "maximum_value": "4" + }, + "machine_nozzle_offset_x": { "default_value": 62.95 }, + "machine_nozzle_offset_y": { "default_value": 2.05 } + } +} diff --git a/resources/extruders/ord_extruder_2.def.json b/resources/extruders/ord_extruder_2.def.json new file mode 100644 index 0000000000..4e8a9f6c15 --- /dev/null +++ b/resources/extruders/ord_extruder_2.def.json @@ -0,0 +1,19 @@ +{ + "id": "ord_extruder_2", + "version": 2, + "name": "2", + "inherits": "fdmextruder", + "metadata": { + "machine": "ord", + "position": "2" + }, + + "overrides": { + "extruder_nr": { + "default_value": 2, + "maximum_value": "4" + }, + "machine_nozzle_offset_x": { "default_value": 0.0 }, + "machine_nozzle_offset_y": { "default_value": 27.7 } + } +} diff --git a/resources/extruders/ord_extruder_3.def.json b/resources/extruders/ord_extruder_3.def.json new file mode 100644 index 0000000000..e8da77a6ec --- /dev/null +++ b/resources/extruders/ord_extruder_3.def.json @@ -0,0 +1,19 @@ +{ + "id": "ord_extruder_3", + "version": 2, + "name": "3", + "inherits": "fdmextruder", + "metadata": { + "machine": "ord", + "position": "3" + }, + + "overrides": { + "extruder_nr": { + "default_value": 3, + "maximum_value": "4" + }, + "machine_nozzle_offset_x": { "default_value": 63.18 }, + "machine_nozzle_offset_y": { "default_value": 28.65 } + } +} diff --git a/resources/extruders/ord_extruder_4.def.json b/resources/extruders/ord_extruder_4.def.json new file mode 100644 index 0000000000..aa6c8052bf --- /dev/null +++ b/resources/extruders/ord_extruder_4.def.json @@ -0,0 +1,19 @@ +{ + "id": "ord_extruder_4", + "version": 2, + "name": "4", + "inherits": "fdmextruder", + "metadata": { + "machine": "ord", + "position": "4" + }, + + "overrides": { + "extruder_nr": { + "default_value": 4, + "maximum_value": "4" + }, + "machine_nozzle_offset_x": { "default_value": 31.6 }, + "machine_nozzle_offset_y": { "default_value": 28.2 } + } +} diff --git a/resources/extruders/punchtec_connect_xl_extruder_left.def.json b/resources/extruders/punchtec_connect_xl_extruder_left.def.json new file mode 100644 index 0000000000..b618999fc2 --- /dev/null +++ b/resources/extruders/punchtec_connect_xl_extruder_left.def.json @@ -0,0 +1,19 @@ +{ + "id": "punchtec_connect_xl_extruder_0", + "version": 2, + "name": "0", + "inherits": "fdmextruder", + "metadata": { + "machine": "punchtec_connect_xl", + "position": "0" + }, + + "overrides": { + "extruder_nr": { + "default_value": 0, + "maximum_value": "1" + }, + "machine_nozzle_offset_x": { "default_value": 0.0 }, + "machine_nozzle_offset_y": { "default_value": 0.0 } + } +} diff --git a/resources/extruders/punchtec_connect_xl_extruder_right.def.json b/resources/extruders/punchtec_connect_xl_extruder_right.def.json new file mode 100644 index 0000000000..1173bb5584 --- /dev/null +++ b/resources/extruders/punchtec_connect_xl_extruder_right.def.json @@ -0,0 +1,19 @@ +{ + "id": "punchtec_connect_xl_extruder_1", + "version": 2, + "name": "1", + "inherits": "fdmextruder", + "metadata": { + "machine": "punchtec_connect_xl", + "position": "1" + }, + + "overrides": { + "extruder_nr": { + "default_value": 1, + "maximum_value": "1" + }, + "machine_nozzle_offset_x": { "default_value": 0.0 }, + "machine_nozzle_offset_y": { "default_value": 0.0 } + } +} diff --git a/resources/extruders/ultimaker3_extended_extruder_left.def.json b/resources/extruders/ultimaker3_extended_extruder_left.def.json index 918d544fd9..202272b096 100644 --- a/resources/extruders/ultimaker3_extended_extruder_left.def.json +++ b/resources/extruders/ultimaker3_extended_extruder_left.def.json @@ -13,8 +13,8 @@ "default_value": 0, "maximum_value": "1" }, - "machine_nozzle_offset_x": { "default_value": -11.0 }, - "machine_nozzle_offset_y": { "default_value": 3.9 }, + "machine_nozzle_offset_x": { "default_value": 0 }, + "machine_nozzle_offset_y": { "default_value": 0 }, "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "default_value": 213 }, @@ -23,8 +23,8 @@ "machine_extruder_end_pos_x": { "default_value": 213 }, "machine_extruder_end_pos_y": { "default_value": 207 }, "machine_nozzle_head_distance": { "default_value": 2.7 }, - "extruder_prime_pos_x": { "default_value": 170, "enabled": false }, - "extruder_prime_pos_y": { "default_value": 6, "enabled": false }, - "extruder_prime_pos_z": { "default_value": 2, "enabled": false } + "extruder_prime_pos_x": { "default_value": 170 }, + "extruder_prime_pos_y": { "default_value": 6 }, + "extruder_prime_pos_z": { "default_value": 2 } } } diff --git a/resources/extruders/ultimaker3_extended_extruder_right.def.json b/resources/extruders/ultimaker3_extended_extruder_right.def.json index 130f31deaa..0f85b2dd09 100644 --- a/resources/extruders/ultimaker3_extended_extruder_right.def.json +++ b/resources/extruders/ultimaker3_extended_extruder_right.def.json @@ -13,8 +13,8 @@ "default_value": 1, "maximum_value": "1" }, - "machine_nozzle_offset_x": { "default_value": 7.0 }, - "machine_nozzle_offset_y": { "default_value": 3.9 }, + "machine_nozzle_offset_x": { "default_value": 18 }, + "machine_nozzle_offset_y": { "default_value": 0 }, "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "default_value": 213 }, @@ -23,8 +23,8 @@ "machine_extruder_end_pos_x": { "default_value": 213 }, "machine_extruder_end_pos_y": { "default_value": 189 }, "machine_nozzle_head_distance": { "default_value": 4.2 }, - "extruder_prime_pos_x": { "default_value": 182, "enabled": false }, - "extruder_prime_pos_y": { "default_value": 6, "enabled": false }, - "extruder_prime_pos_z": { "default_value": 2, "enabled": false } + "extruder_prime_pos_x": { "default_value": 182 }, + "extruder_prime_pos_y": { "default_value": 6 }, + "extruder_prime_pos_z": { "default_value": 2 } } } diff --git a/resources/extruders/ultimaker3_extruder_left.def.json b/resources/extruders/ultimaker3_extruder_left.def.json index 7e7b77fcb6..83efa25dbb 100644 --- a/resources/extruders/ultimaker3_extruder_left.def.json +++ b/resources/extruders/ultimaker3_extruder_left.def.json @@ -13,8 +13,8 @@ "default_value": 0, "maximum_value": "1" }, - "machine_nozzle_offset_x": { "default_value": -11.0 }, - "machine_nozzle_offset_y": { "default_value": 3.9 }, + "machine_nozzle_offset_x": { "default_value": 0 }, + "machine_nozzle_offset_y": { "default_value": 0 }, "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "default_value": 213 }, @@ -23,8 +23,8 @@ "machine_extruder_end_pos_x": { "default_value": 213 }, "machine_extruder_end_pos_y": { "default_value": 207 }, "machine_nozzle_head_distance": { "default_value": 2.7 }, - "extruder_prime_pos_x": { "default_value": 170, "enabled": false }, - "extruder_prime_pos_y": { "default_value": 6, "enabled": false }, - "extruder_prime_pos_z": { "default_value": 2, "enabled": false } + "extruder_prime_pos_x": { "default_value": 170 }, + "extruder_prime_pos_y": { "default_value": 6 }, + "extruder_prime_pos_z": { "default_value": 2 } } } diff --git a/resources/extruders/ultimaker3_extruder_right.def.json b/resources/extruders/ultimaker3_extruder_right.def.json index ff616572ee..4a75059c40 100644 --- a/resources/extruders/ultimaker3_extruder_right.def.json +++ b/resources/extruders/ultimaker3_extruder_right.def.json @@ -13,8 +13,8 @@ "default_value": 1, "maximum_value": "1" }, - "machine_nozzle_offset_x": { "default_value": 7.0 }, - "machine_nozzle_offset_y": { "default_value": 3.9 }, + "machine_nozzle_offset_x": { "default_value": 18 }, + "machine_nozzle_offset_y": { "default_value": 0 }, "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "default_value": 213 }, @@ -23,8 +23,8 @@ "machine_extruder_end_pos_x": { "default_value": 213 }, "machine_extruder_end_pos_y": { "default_value": 189 }, "machine_nozzle_head_distance": { "default_value": 4.2 }, - "extruder_prime_pos_x": { "default_value": 182, "enabled": false }, - "extruder_prime_pos_y": { "default_value": 6, "enabled": false }, - "extruder_prime_pos_z": { "default_value": 2, "enabled": false } + "extruder_prime_pos_x": { "default_value": 182 }, + "extruder_prime_pos_y": { "default_value": 6 }, + "extruder_prime_pos_z": { "default_value": 2 } } } diff --git a/resources/i18n/it/cura.po b/resources/i18n/it/cura.po index bd8b392323..e69f09a9c9 100644 --- a/resources/i18n/it/cura.po +++ b/resources/i18n/it/cura.po @@ -160,13 +160,13 @@ msgstr "Salvato su unità rimovibile {0} come {1}" #: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:103 msgctxt "@action:button" msgid "Eject" -msgstr "Espelli" +msgstr "Rimuovi" #: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:103 #, python-brace-format msgctxt "@action" msgid "Eject removable device {0}" -msgstr "Espelli il dispositivo rimovibile {0}" +msgstr "Rimuovi il dispositivo rimovibile {0}" #: /home/ruben/Projects/Cura/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:108 #, python-brace-format @@ -2008,7 +2008,7 @@ msgstr "Sel&eziona tutti i modelli" #: /home/ruben/Projects/Cura/resources/qml/Actions.qml:248 msgctxt "@action:inmenu menubar:edit" msgid "&Clear Build Plate" -msgstr "&Cancella piano di stampa" +msgstr "&Cancellare piano di stampa" #: /home/ruben/Projects/Cura/resources/qml/Actions.qml:258 msgctxt "@action:inmenu menubar:file" diff --git a/resources/i18n/it/fdmprinter.def.json.po b/resources/i18n/it/fdmprinter.def.json.po index 311d2292de..dc83a99030 100644 --- a/resources/i18n/it/fdmprinter.def.json.po +++ b/resources/i18n/it/fdmprinter.def.json.po @@ -2257,12 +2257,12 @@ msgstr "Regola il posizionamento delle strutture di supporto. Il posizionamento #: fdmprinter.def.json msgctxt "support_type option buildplate" msgid "Touching Buildplate" -msgstr "Sostegno solo delle pareti esterne" +msgstr "Contatto con il Piano di Stampa" #: fdmprinter.def.json msgctxt "support_type option everywhere" msgid "Everywhere" -msgstr "In tutti i possibili punti" +msgstr "In Tutti i Possibili Punti" #: fdmprinter.def.json msgctxt "support_angle label" diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 3ee8630183..043552d768 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -121,7 +121,7 @@ Item Action { id: updateProfileAction; - enabled: Cura.MachineManager.isActiveStackValid && Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId) + enabled: !Cura.MachineManager.stacksHaveErrors && Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId) text: catalog.i18nc("@action:inmenu menubar:profile","&Update profile with current settings"); onTriggered: Cura.ContainerManager.updateQualityChanges(); } @@ -130,14 +130,18 @@ Item { id: resetProfileAction; enabled: Cura.MachineManager.hasUserSettings - text: catalog.i18nc("@action:inmenu menubar:profile","&Discard current settings"); - onTriggered: Cura.ContainerManager.clearUserContainers(); + text: catalog.i18nc("@action:inmenu menubar:profile","&Discard current changes"); + onTriggered: + { + forceActiveFocus(); + Cura.ContainerManager.clearUserContainers(); + } } Action { id: addProfileAction; - enabled: Cura.MachineManager.isActiveStackValid && Cura.MachineManager.hasUserSettings + enabled: !Cura.MachineManager.stacksHaveErrors && Cura.MachineManager.hasUserSettings text: catalog.i18nc("@action:inmenu menubar:profile","&Create profile from current settings..."); } @@ -228,7 +232,7 @@ Item Action { id: multiplyObjectAction; - text: catalog.i18nc("@action:inmenu","&Duplicate Model"); + text: catalog.i18nc("@action:inmenu","&Multiply Model..."); iconName: "edit-duplicate" } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 59e4848851..ab4d7bfd06 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -246,15 +246,35 @@ UM.MainWindow { if(drop.urls.length > 0) { + // Import models for(var i in drop.urls) { - UM.MeshFileHandler.readLocalFile(drop.urls[i]); - if (i == drop.urls.length - 1) - { - var meshName = backgroundItem.getMeshName(drop.urls[i].toString()) - backgroundItem.hasMesh(decodeURIComponent(meshName)) + // There is no endsWith in this version of JS... + if ((drop.urls[i].length <= 12) || (drop.urls[i].substring(drop.urls[i].length-12) !== ".curaprofile")) { + // Drop an object + UM.MeshFileHandler.readLocalFile(drop.urls[i]); + if (i == drop.urls.length - 1) + { + var meshName = backgroundItem.getMeshName(drop.urls[i].toString()); + backgroundItem.hasMesh(decodeURIComponent(meshName)); + } } } + + // Import profiles + var import_result = Cura.ContainerManager.importProfiles(drop.urls); + if (import_result.message !== "") { + messageDialog.text = import_result.message + if(import_result.status == "ok") + { + messageDialog.icon = StandardIcon.Information + } + else + { + messageDialog.icon = StandardIcon.Critical + } + messageDialog.open() + } } } } @@ -402,6 +422,20 @@ UM.MainWindow anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenterOffset: - UM.Theme.getSize("sidebar").width / 2 visible: base.monitoringPrint + onVisibleChanged: + { + if(Cura.MachineManager.printerOutputDevices.length == 0 ) + { + return; + } + if(visible) + { + Cura.MachineManager.printerOutputDevices[0].startCamera() + } else + { + Cura.MachineManager.printerOutputDevices[0].stopCamera() + } + } source: { if(!base.monitoringPrint) @@ -539,7 +573,7 @@ UM.MainWindow target: Cura.MachineManager onBlurSettings: { - contentItem.focus = true + contentItem.forceActiveFocus() } } @@ -575,6 +609,11 @@ UM.MainWindow } } + MultiplyObjectOptions + { + id: multiplyObjectOptions + } + Connections { target: Cura.Actions.multiplyObject @@ -582,7 +621,9 @@ UM.MainWindow { if(objectContextMenu.objectId != 0) { - Printer.multiplyObject(objectContextMenu.objectId, 1); + multiplyObjectOptions.objectId = objectContextMenu.objectId; + multiplyObjectOptions.visible = true; + multiplyObjectOptions.reset(); objectContextMenu.objectId = 0; } } diff --git a/resources/qml/Menus/MaterialMenu.qml b/resources/qml/Menus/MaterialMenu.qml index 534be456ed..573dff89dd 100644 --- a/resources/qml/Menus/MaterialMenu.qml +++ b/resources/qml/Menus/MaterialMenu.qml @@ -153,6 +153,7 @@ Menu else { result.definition = "fdmprinter"; + result.compatible = true; //NB: Only checks for compatibility in global version of material, but we don't have machine-specific materials anyway. } return result; } diff --git a/resources/qml/MultiplyObjectOptions.qml b/resources/qml/MultiplyObjectOptions.qml new file mode 100644 index 0000000000..4b22a96644 --- /dev/null +++ b/resources/qml/MultiplyObjectOptions.qml @@ -0,0 +1,66 @@ +// Copyright (c) 2015 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Window 2.1 + +import UM 1.1 as UM + +UM.Dialog +{ + id: base + + //: Dialog title + title: catalog.i18nc("@title:window", "Multiply Model") + + minimumWidth: 400 * Screen.devicePixelRatio + minimumHeight: 80 * Screen.devicePixelRatio + width: minimumWidth + height: minimumHeight + + property var objectId: 0; + onAccepted: Printer.multiplyObject(base.objectId, parseInt(copiesField.text)) + + property variant catalog: UM.I18nCatalog { name: "cura" } + + signal reset() + onReset: { + copiesField.text = "1"; + copiesField.selectAll(); + copiesField.focus = true; + } + + Row + { + spacing: UM.Theme.getSize("default_margin").width + + Label { + text: "Number of copies:" + anchors.verticalCenter: copiesField.verticalCenter + } + + TextField { + id: copiesField + validator: RegExpValidator { regExp: /^\d{0,2}/ } + maximumLength: 2 + } + } + + + rightButtons: + [ + Button + { + text: catalog.i18nc("@action:button","OK") + onClicked: base.accept() + enabled: base.objectId != 0 && parseInt(copiesField.text) > 0 + }, + Button + { + text: catalog.i18nc("@action:button","Cancel") + onClicked: base.reject() + } + ] +} + diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index 78553bb37f..e6ddef7979 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -251,6 +251,8 @@ UM.ManagementPage { id: renameDialog; object: base.currentItem && base.currentItem.name ? base.currentItem.name : ""; + property var machine_name_validator: Cura.MachineNameValidator { } + validName: renameDialog.newName.match(renameDialog.machine_name_validator.machineNameRegex) != null; onAccepted: { Cura.MachineManager.renameMachine(base.currentItem.id, newName.trim()); diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index 6115671563..7ea363454b 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -22,7 +22,7 @@ TabView Tab { - title: "Information" + title: catalog.i18nc("@title","Information") anchors { leftMargin: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index 8ba26b6ac3..83f980bf86 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -29,7 +29,8 @@ UM.ManagementPage } else { - result.definition = "fdmprinter" + result.definition = "fdmprinter"; + result.compatible = true; //NB: Only checks for compatibility in global version of material, but we don't have machine-specific materials anyway. } return result } diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index 621ecb3184..b1f06af3a9 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -62,7 +62,7 @@ UM.ManagementPage Button { text: catalog.i18nc("@label", "Create") - enabled: base.canCreateProfile() + enabled: base.canCreateProfile() && !Cura.MachineManager.stacksHaveErrors visible: base.canCreateProfile() iconName: "list-add"; @@ -170,7 +170,7 @@ UM.ManagementPage Button { - text: catalog.i18nc("@action:button", "Discard current settings"); + text: catalog.i18nc("@action:button", "Discard current changes"); enabled: Cura.MachineManager.hasUserSettings onClicked: Cura.ContainerManager.clearUserContainers(); } @@ -310,7 +310,7 @@ UM.ManagementPage folder: CuraApplication.getDefaultPath("dialog_profile_path") onAccepted: { - var result = base.model.importProfile(fileUrl) + var result = Cura.ContainerManager.importProfile(fileUrl); messageDialog.text = result.message if(result.status == "ok") { @@ -339,7 +339,7 @@ UM.ManagementPage onAccepted: { var containers = Cura.ContainerManager.findInstanceContainers({"type": "quality_changes", "name": base.currentItem.name}) - var result = base.model.exportProfile(containers, fileUrl, selectedNameFilter) + var result = Cura.ContainerManager.exportProfile(containers, fileUrl, selectedNameFilter) if(result && result.status == "error") { diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index cada8adfda..282f366994 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -116,7 +116,8 @@ UM.PreferencesPage id: definitionsModel containerId: Cura.MachineManager.activeDefinitionId showAll: true - exclude: ["machine_settings"] + exclude: ["machine_settings", "command_line_settings"] + showAncestors: true expanded: ["*"] visibilityHandler: UM.SettingPreferenceVisibilityHandler { } } diff --git a/resources/qml/Settings/SettingCheckBox.qml b/resources/qml/Settings/SettingCheckBox.qml index 1fcd24ccf6..97a72a026e 100644 --- a/resources/qml/Settings/SettingCheckBox.qml +++ b/resources/qml/Settings/SettingCheckBox.qml @@ -29,11 +29,11 @@ SettingItem // 4: variant // 5: machine var value; - if ((propertyProvider.properties.resolve != "None") && (stackLevel != 0) && (stackLevel != 1)) { + if ((base.resolve != "None") && (stackLevel != 0) && (stackLevel != 1)) { // We have a resolve function. Indicates that the setting is not settable per extruder and that // we have to choose between the resolved value (default) and the global value // (if user has explicitly set this). - value = propertyProvider.properties.resolve; + value = base.resolve; } else { value = propertyProvider.properties.value; } diff --git a/resources/qml/Settings/SettingComboBox.qml b/resources/qml/Settings/SettingComboBox.qml index c4ca637506..dfa070667a 100644 --- a/resources/qml/Settings/SettingComboBox.qml +++ b/resources/qml/Settings/SettingComboBox.qml @@ -96,11 +96,11 @@ SettingItem { // FIXME this needs to go away once 'resolve' is combined with 'value' in our data model. var value; - if ((propertyProvider.properties.resolve != "None") && (base.stackLevel != 0) && (base.stackLevel != 1)) { + if ((base.resolve != "None") && (base.stackLevel != 0) && (base.stackLevel != 1)) { // We have a resolve function. Indicates that the setting is not settable per extruder and that // we have to choose between the resolved value (default) and the global value // (if user has explicitly set this). - value = propertyProvider.properties.resolve; + value = base.resolve; } else { value = propertyProvider.properties.value; } diff --git a/resources/qml/Settings/SettingExtruder.qml b/resources/qml/Settings/SettingExtruder.qml index ff30a7146e..82d7def5ce 100644 --- a/resources/qml/Settings/SettingExtruder.qml +++ b/resources/qml/Settings/SettingExtruder.qml @@ -21,7 +21,11 @@ SettingItem id: extruders_model onModelChanged: control.color = extruders_model.getItem(control.currentIndex).color } - property string color: extruders_model.getItem(control.currentIndex).color + property string color: + { + var model_color = extruders_model.getItem(control.currentIndex).color; + return (model_color) ? model_color : ""; + } textRole: "name" diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index 7fa2856e27..2aa15e9244 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -27,7 +27,8 @@ Item { // Create properties to put property provider stuff in (bindings break in qt 5.5.1 otherwise) property var state: propertyProvider.properties.state - property var resolve: propertyProvider.properties.resolve + // There is no resolve property if there is only one stack. + property var resolve: Cura.MachineManager.activeStackId != Cura.MachineManager.activeMachineId ? propertyProvider.properties.resolve : "None" property var stackLevels: propertyProvider.stackLevels property var stackLevel: stackLevels[0] diff --git a/resources/qml/Settings/SettingTextField.qml b/resources/qml/Settings/SettingTextField.qml index d02c8854b4..d89f540aa3 100644 --- a/resources/qml/Settings/SettingTextField.qml +++ b/resources/qml/Settings/SettingTextField.qml @@ -114,11 +114,11 @@ SettingItem // 3: material -> user changed material in materialspage // 4: variant // 5: machine - if ((propertyProvider.properties.resolve != "None" && propertyProvider.properties.resolve) && (stackLevel != 0) && (stackLevel != 1)) { + if ((base.resolve != "None" && base.resolve) && (stackLevel != 0) && (stackLevel != 1)) { // We have a resolve function. Indicates that the setting is not settable per extruder and that // we have to choose between the resolved value (default) and the global value // (if user has explicitly set this). - return propertyProvider.properties.resolve; + return base.resolve; } else { return propertyProvider.properties.value; } diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 16b5dada72..c47abf3ee2 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -30,7 +30,7 @@ ScrollView id: definitionsModel; containerId: Cura.MachineManager.activeDefinitionId visibilityHandler: UM.SettingPreferenceVisibilityHandler { } - exclude: ["machine_settings", "infill_mesh", "infill_mesh_order"] + exclude: ["machine_settings", "command_line_settings", "infill_mesh", "infill_mesh_order"] // TODO: infill_mesh settigns are excluded hardcoded, but should be based on the fact that settable_globally, settable_per_meshgroup and settable_per_extruder are false. expanded: Printer.expandedCategories onExpandedChanged: Printer.setExpandedCategories(expanded) onVisibilityChanged: Cura.SettingInheritanceManager.forceUpdate() diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 16c6a6a8ce..700384c394 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -222,6 +222,7 @@ Column width: materialSelection.visible ? (parent.width - UM.Theme.getSize("default_margin").width) / 2 : parent.width anchors.left: parent.left style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true; menu: NozzleMenu { extruderIndex: base.currentExtruderIndex } } @@ -252,6 +253,7 @@ Column width: variantSelection.visible ? (parent.width - UM.Theme.getSize("default_margin").width) / 2 : parent.width anchors.right: parent.right style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true; menu: MaterialMenu { extruderIndex: base.currentExtruderIndex } } @@ -301,6 +303,7 @@ Column height: UM.Theme.getSize("setting_control").height tooltip: Cura.MachineManager.activeQualityName style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true; property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported menu: ProfileMenu { } @@ -319,7 +322,11 @@ Column color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); iconSource: UM.Theme.getIcon("star"); - onClicked: Cura.Actions.manageProfiles.trigger() + onClicked: + { + forceActiveFocus(); + Cura.Actions.manageProfiles.trigger() + } onEntered: { var content = catalog.i18nc("@tooltip","Some setting values are different from the values stored in the profile.\n\nClick to open the profile manager.") diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 37d72d189d..e353742c72 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -126,7 +126,7 @@ Item { return UM.Theme.getColor("setting_control_disabled_text") } - return UM.Theme.getColor("text") + return UM.Theme.getColor("setting_control_disabled_text") } } @@ -327,7 +327,7 @@ Item anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.left: supportHelperLabel.right anchors.leftMargin: UM.Theme.getSize("default_margin").width - width: parent.width / 100 * 45 + width: parent.width / 100 * 65 style: UM.Theme.styles.combobox enabled: base.settingsEnabled