From f184baadf0a396743268b29027a1d4031d73991d Mon Sep 17 00:00:00 2001 From: Johan K Date: Tue, 14 Jun 2016 18:08:35 +0200 Subject: [PATCH 001/102] Formulate layerview logic using numpy to speed up. Also changed layer data packets from engine to make it possible. --- cura/Layer.py | 102 ++++++++------ cura/LayerDataBuilder.py | 12 +- cura/LayerPolygon.py | 128 ++++++++++++++---- plugins/CuraEngineBackend/Cura.proto | 20 +-- .../ProcessSlicedLayersJob.py | 20 ++- plugins/LayerView/LayerView.py | 12 +- 6 files changed, 202 insertions(+), 92 deletions(-) diff --git a/cura/Layer.py b/cura/Layer.py index 904e5528a3..cb64d77c2d 100644 --- a/cura/Layer.py +++ b/cura/Layer.py @@ -35,24 +35,31 @@ class Layer: def setThickness(self, thickness): self._thickness = thickness - def vertexCount(self): + def lineMeshVertexCount(self): result = 0 for polygon in self._polygons: - result += polygon.vertexCount() + result += polygon.lineMeshVertexCount() return result - def build(self, offset, vertices, colors, indices): - result = offset + def lineMeshElementCount(self): + result = 0 for polygon in self._polygons: - if polygon.type == LayerPolygon.InfillType or polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType: - continue + result += polygon.lineMeshElementCount() - polygon.build(result, vertices, colors, indices) - result += polygon.vertexCount() + return result + + def build(self, vertex_offset, index_offset, vertices, colors, indices): + result_vertex_offset = vertex_offset + result_index_offset = index_offset + self._element_count = 0 + for polygon in self._polygons: + polygon.build(result_vertex_offset, result_index_offset, vertices, colors, indices) + result_vertex_offset += polygon.lineMeshVertexCount() + result_index_offset += polygon.lineMeshElementCount() self._element_count += polygon.elementCount - return result + return (result_vertex_offset,result_index_offset) def createMesh(self): return self.createMeshOrJumps(True) @@ -61,39 +68,58 @@ class Layer: return self.createMeshOrJumps(False) def createMeshOrJumps(self, make_mesh): - builder = MeshBuilder() - + builder = MeshBuilder() # This is never really used, only the mesh_data inside + index_pattern = numpy.array([[0,3,2,0,1,3]],dtype = numpy.int32 ) + + line_count = 0 + if make_mesh: + for polygon in self._polygons: + line_count += polygon._mesh_line_count + else: + for polygon in self._polygons: + line_count += polygon._jump_count + + + # Reserve the neccesary space for the data upfront + builder.reserveFaceAndVerticeCount( 2*line_count, 4*line_count ) + for polygon in self._polygons: - if make_mesh and (polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType): - continue - if not make_mesh and not (polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType): - continue + #if make_mesh and (polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType): + # continue + #if not make_mesh and not (polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType): + # continue + + index_mask = numpy.logical_not(polygon._jump_mask) if make_mesh else polygon._jump_mask - poly_color = polygon.getColor() + # Create an array with rows [p p+1] and only save those we whant to draw based on make_mesh + points = numpy.concatenate((polygon.data[:-1],polygon.data[1:]),1)[index_mask.ravel()] + # Line types of the points we want to draw + line_types = polygon._types[index_mask] + + + #if polygon.type == LayerPolygon.InfillType or polygon.type == LayerPolygon.SkinType or polygon.type == LayerPolygon.SupportInfillType: + # points[:,1] -= 0.01 + #if polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType: + # points[:,1] += 0.01 + # Shift the z-axis according to previous implementation. + if make_mesh: + points[polygon._orInfillSkin[line_types],1::3] -= 0.01 + else: + points[:,1::3] += 0.01 - points = numpy.copy(polygon.data) - if polygon.type == LayerPolygon.InfillType or polygon.type == LayerPolygon.SkinType or polygon.type == LayerPolygon.SupportInfillType: - points[:,1] -= 0.01 - if polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType: - points[:,1] += 0.01 + # Create an array with normals and tile 2 copies to match size of points variable + normals = numpy.tile( polygon.getNormals()[index_mask.ravel()], (1,2)) - normals = polygon.getNormals() + # Scale all normals by the line width of the current line so we can easily offset. + normals *= (polygon.lineWidths[index_mask.ravel()] / 2) - # Scale all by the line width of the polygon so we can easily offset. - normals *= (polygon.lineWidth / 2) + # Create 4 points to draw each line segment, points +- normals results in 2 points each. Reshape to one point per line + f_points = numpy.concatenate((points-normals,points+normals),1).reshape((-1,3)) + # index_pattern defines which points to use to draw the two faces for each lines egment, the following linesegment is offset by 4 + f_indices = ( index_pattern + numpy.arange(0,4*len(normals),4,dtype=numpy.int32).reshape((-1,1)) ).reshape((-1,3)) + f_colors = numpy.repeat(polygon._color_map[line_types], 4, 0) - #TODO: Use numpy magic to perform the vertex creation to speed up things. - for i in range(len(points)): - start = points[i - 1] - end = points[i] + builder.addFacesWithColor(f_points, f_indices, f_colors) - normal = normals[i - 1] - - point1 = Vector(data = start - normal) - point2 = Vector(data = start + normal) - point3 = Vector(data = end + normal) - point4 = Vector(data = end - normal) - - builder.addQuad(point1, point2, point3, point4, color = poly_color) - - return builder.build() + + return builder.build() \ No newline at end of file diff --git a/cura/LayerDataBuilder.py b/cura/LayerDataBuilder.py index 7e8e0e636b..85f9a23e7b 100644 --- a/cura/LayerDataBuilder.py +++ b/cura/LayerDataBuilder.py @@ -50,18 +50,22 @@ class LayerDataBuilder(MeshBuilder): def build(self): vertex_count = 0 + index_count = 0 for layer, data in self._layers.items(): - vertex_count += data.vertexCount() + vertex_count += data.lineMeshVertexCount() + index_count += data.lineMeshElementCount() vertices = numpy.empty((vertex_count, 3), numpy.float32) colors = numpy.empty((vertex_count, 4), numpy.float32) - indices = numpy.empty((vertex_count, 2), numpy.int32) + indices = numpy.empty((index_count, 2), numpy.int32) - offset = 0 + vertex_offset = 0 + index_offset = 0 for layer, data in self._layers.items(): - offset = data.build(offset, vertices, colors, indices) + ( vertex_offset, index_offset ) = data.build( vertex_offset, index_offset, vertices, colors, indices) self._element_counts[layer] = data.elementCount + self.clear() self.addVertices(vertices) self.addColors(colors) self.addIndices(indices.flatten()) diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index c4dc5d4954..f2c84e0d57 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -14,40 +14,96 @@ class LayerPolygon: SupportInfillType = 7 MoveCombingType = 8 MoveRetractionType = 9 - - def __init__(self, mesh, polygon_type, data, line_width): + + __jump_map = numpy.logical_or( numpy.arange(10) == NoneType, numpy.arange(10) >= MoveCombingType ) + + def __init__(self, mesh, line_types, data, line_widths): self._mesh = mesh - self._type = polygon_type + self._types = line_types self._data = data - self._line_width = line_width / 1000 - self._begin = 0 - self._end = 0 + self._line_widths = line_widths / 1000 + + self._vertex_begin = 0 + self._vertex_end = 0 + self._index_begin = 0 + self._index_end = 0 + + self._jump_mask = self.__jump_map[self._types] + self._jump_count = numpy.sum(self._jump_mask) + self._mesh_line_count = len(self._types)-self._jump_count + self._vertex_count = self._mesh_line_count + numpy.sum( self._types[1:] == self._types[:-1]) - self._color = self.__color_map[polygon_type] + # Buffering the colors shouldn't be necessary as it is not + # re-used and can save alot of memory usage. + self._colors = self.__color_map[self._types] + self._color_map = self.__color_map + + # type == LayerPolygon.InfillType or type == LayerPolygon.SkinType or type == LayerPolygon.SupportInfillType + # Should be generated in better way, not hardcoded. + self._orInfillSkin = numpy.array([0,0,0,1,0,0,1,1,0,0],dtype=numpy.bool) + + self._build_cache_line_mesh_mask = None + self._build_cache_needed_points = None + + def build_cache(self): + #if polygon.type == LayerPolygon.InfillType or polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType: + # continue + self._build_cache_line_mesh_mask = numpy.logical_not(numpy.logical_or(self._jump_mask,self._types == LayerPolygon.InfillType )) + mesh_line_count = numpy.sum(self._build_cache_line_mesh_mask) + self._index_begin = 0 + self._index_end = mesh_line_count + + self._build_cache_needed_points = numpy.ones((len(self._types),2), dtype=numpy.bool) + # Only if the type of line segment changes do we need to add an extra vertex to change colors + self._build_cache_needed_points[1:,0][:,numpy.newaxis] = self._types[1:] != self._types[:-1] + # Remove points of types we don't want in the line mesh + numpy.logical_and(self._build_cache_needed_points, self._build_cache_line_mesh_mask, self._build_cache_needed_points ) + + self._vertex_begin = 0 + self._vertex_end = numpy.sum( self._build_cache_needed_points ) + - def build(self, offset, vertices, colors, indices): - self._begin = offset - self._end = self._begin + len(self._data) - 1 + def build(self, vertex_offset, index_offset, vertices, colors, indices): + if (self._build_cache_line_mesh_mask == None) or (self._build_cache_needed_points == None ): + self.build_cache() + + line_mesh_mask = self._build_cache_line_mesh_mask + needed_points_list = self._build_cache_needed_points + + index_list = ( numpy.arange(len(self._types)).reshape((-1,1)) + numpy.array([[0,1]]) ).reshape((-1,1))[needed_points_list.reshape((-1,1))] + + self._vertex_begin += vertex_offset + self._vertex_end += vertex_offset + + vertices[self._vertex_begin:self._vertex_end, :] = self._data[index_list, :] + colors[self._vertex_begin:self._vertex_end, :] = numpy.tile(self._colors,(1,2)).reshape((-1,4))[needed_points_list.ravel()] + colors[self._vertex_begin:self._vertex_end, :] *= numpy.array([[0.5, 0.5, 0.5, 1.0]], numpy.float32) - vertices[self._begin:self._end + 1, :] = self._data[:, :] - colors[self._begin:self._end + 1, :] = numpy.array([self._color.r * 0.5, self._color.g * 0.5, self._color.b * 0.5, self._color.a], numpy.float32) + self._index_begin += index_offset + self._index_end += index_offset + + indices[self._index_begin:self._index_end,:] = numpy.arange(self._index_end-self._index_begin, dtype=numpy.int32).reshape((-1,1)) + # When the line type changes the index needs to be increased by 2. + indices[self._index_begin:self._index_end,:] += numpy.cumsum(needed_points_list[line_mesh_mask.ravel(),0],dtype=numpy.int32).reshape((-1,1)) + # Each line segment goes from it's starting point p to p+1, offset by the vertex index. + # The -1 is to compensate for the neccecarily True value of needed_points_list[0,0] which causes an unwanted +1 in cumsum above. + indices[self._index_begin:self._index_end,:] += numpy.array([self._vertex_begin - 1,self._vertex_begin]) + + self._build_cache_line_mesh_mask = None + self._build_cache_needed_points = None - for i in range(self._begin, self._end): - indices[i, 0] = i - indices[i, 1] = i + 1 + def getColors(self): + return self._colors - indices[self._end, 0] = self._end - indices[self._end, 1] = self._begin + def lineMeshVertexCount(self): + return (self._vertex_end - self._vertex_begin) - def getColor(self): - return self._color - - def vertexCount(self): - return len(self._data) + def lineMeshElementCount(self): + return (self._index_end - self._index_begin) @property - def type(self): - return self._type + def types(self): + return self._types @property def data(self): @@ -55,11 +111,11 @@ class LayerPolygon: @property def elementCount(self): - return ((self._end - self._begin) + 1) * 2 # The range of vertices multiplied by 2 since each vertex is used twice + return (self._index_end - self._index_begin) * 2 # The range of vertices multiplied by 2 since each vertex is used twice @property - def lineWidth(self): - return self._line_width + def lineWidths(self): + return self._line_widths # Calculate normals for the entire polygon using numpy. def getNormals(self): @@ -71,7 +127,8 @@ class LayerPolygon: # we end up subtracting each next point from the current, wrapping # around. This gives us the edges from the next point to the current # point. - normals[:] = normals[:] - numpy.roll(normals, -1, axis = 0) + normals = numpy.diff(normals, 1, 0) + # Calculate the length of each edge using standard Pythagoras lengths = numpy.sqrt(normals[:, 0] ** 2 + normals[:, 2] ** 2) # The normal of a 2D vector is equal to its x and y coordinates swapped @@ -85,7 +142,7 @@ class LayerPolygon: return normals - __color_map = { + __color_mapping = { NoneType: Color(1.0, 1.0, 1.0, 1.0), Inset0Type: Color(1.0, 0.0, 0.0, 1.0), InsetXType: Color(0.0, 1.0, 0.0, 1.0), @@ -97,3 +154,16 @@ class LayerPolygon: MoveCombingType: Color(0.0, 0.0, 1.0, 1.0), MoveRetractionType: Color(0.5, 0.5, 1.0, 1.0), } + + # Should be generated in better way, not hardcoded. + __color_map = numpy.array([ + [1.0, 1.0, 1.0, 1.0], + [1.0, 0.0, 0.0, 1.0], + [0.0, 1.0, 0.0, 1.0], + [1.0, 1.0, 0.0, 1.0], + [0.0, 1.0, 1.0, 1.0], + [0.0, 1.0, 1.0, 1.0], + [1.0, 0.74, 0.0, 1.0], + [0.0, 1.0, 1.0, 1.0], + [0.0, 0.0, 1.0, 1.0], + [0.5, 0.5, 1.0, 1.0]]) diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto index 38753fd804..e40678c6a5 100644 --- a/plugins/CuraEngineBackend/Cura.proto +++ b/plugins/CuraEngineBackend/Cura.proto @@ -44,21 +44,9 @@ message Layer { } message Polygon { - enum Type { - NoneType = 0; - Inset0Type = 1; - InsetXType = 2; - SkinType = 3; - SupportType = 4; - SkirtType = 5; - InfillType = 6; - SupportInfillType = 7; - MoveCombingType = 8; - MoveRetractionType = 9; - } - Type type = 1; // Type of move - bytes points = 2; // The points of the polygon, or two points if only a line segment (Currently only line segments are used) - float line_width = 3; // The width of the line being laid down + bytes line_type = 1; + bytes points = 2; + bytes line_width = 3; } message GCodeLayer { @@ -86,4 +74,4 @@ message GCodePrefix { } message SlicingFinished { -} \ No newline at end of file +} diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index d23f71e874..0aa34b8f3f 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -14,6 +14,7 @@ from UM.Math.Vector import Vector from cura import LayerDataBuilder from cura import LayerDataDecorator +from cura import LayerPolygon import numpy @@ -38,6 +39,12 @@ class ProcessSlicedLayersJob(Job): self._abort_requested = True def run(self): + # This is to prevent small models layer data to be cleared by extra invocation of engine + # Possibly adds an extra bug of layerdata not being removed if platform is cleared. + #TODO: remove need for this check + if len(self._layers) == 0: + return + if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView": self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1) self._progress.show() @@ -80,15 +87,22 @@ class ProcessSlicedLayersJob(Job): abs_layer_number = layer.id + abs(min_layer_number) layer_data.addLayer(abs_layer_number) + this_layer = layer_data.getLayer(abs_layer_number) layer_data.setLayerHeight(abs_layer_number, layer.height) layer_data.setLayerThickness(abs_layer_number, layer.thickness) for p in range(layer.repeatedMessageCount("polygons")): polygon = layer.getRepeatedMessage("polygons", p) + line_types = numpy.fromstring(polygon.line_type, dtype="u1") # Convert bytearray to numpy array + line_types = line_types.reshape((-1,1)) + points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly. + line_widths = numpy.fromstring(polygon.line_width, dtype="i4") # Convert bytearray to numpy array + line_widths = line_widths.reshape((-1,1)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly. + # Create a new 3D-array, copy the 2D points over and insert the right height. # This uses manual array creation + copy rather than numpy.insert since this is # faster. @@ -99,7 +113,11 @@ class ProcessSlicedLayersJob(Job): new_points /= 1000 - layer_data.addPolygon(abs_layer_number, polygon.type, new_points, polygon.line_width) + this_poly = LayerPolygon.LayerPolygon(layer_data, line_types, new_points, line_widths) + this_poly.build_cache() + + this_layer.polygons.append(this_poly) + Job.yieldThread() Job.yieldThread() current_layer += 1 diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index cd7a17a357..90927ff39d 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -25,6 +25,8 @@ from . import LayerViewProxy from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") +import numpy + ## View used to display g-code paths. class LayerView(View): def __init__(self): @@ -42,7 +44,7 @@ class LayerView(View): self._top_layers_job = None self._activity = False - Preferences.getInstance().addPreference("view/top_layer_count", 1) + Preferences.getInstance().addPreference("view/top_layer_count", 5) Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) @@ -255,12 +257,14 @@ class _CreateTopLayersJob(Job): if not layer or layer.getVertices() is None: continue + layer_mesh.addIndices(layer_mesh._vertex_count+layer.getIndices()) layer_mesh.addVertices(layer.getVertices()) - + # Scale layer color by a brightness factor based on the current layer number # This will result in a range of 0.5 - 1.0 to multiply colors by. - brightness = (2.0 - (i / self._solid_layers)) / 2.0 - layer_mesh.addColors(layer.getColors() * brightness) + brightness = numpy.ones((1,4),dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0 + brightness[0,3] = 1.0; + layer_mesh.addColors(layer.getColors() * brightness ) if self._cancel: return From 788f79f5ba5091dac9e119184321441fc5f602e1 Mon Sep 17 00:00:00 2001 From: Johan K Date: Wed, 15 Jun 2016 23:23:12 +0200 Subject: [PATCH 002/102] Changing the message used from the engine from Layer to LayerOptimized. --- plugins/CuraEngineBackend/Cura.proto | 37 +++++++++++++++++-- .../CuraEngineBackend/CuraEngineBackend.py | 20 +++++++--- .../ProcessSlicedLayersJob.py | 4 +- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto index e40678c6a5..48dee7c9c1 100644 --- a/plugins/CuraEngineBackend/Cura.proto +++ b/plugins/CuraEngineBackend/Cura.proto @@ -44,11 +44,40 @@ message Layer { } message Polygon { - bytes line_type = 1; - bytes points = 2; - bytes line_width = 3; + enum Type { + NoneType = 0; + Inset0Type = 1; + InsetXType = 2; + SkinType = 3; + SupportType = 4; + SkirtType = 5; + InfillType = 6; + SupportInfillType = 7; + MoveCombingType = 8; + MoveRetractionType = 9; + } + Type type = 1; // Type of move + bytes points = 2; // The points of the polygon, or two points if only a line segment (Currently only line segments are used) + float line_width = 3; // The width of the line being laid down } +message LayerOptimized { + int32 id = 1; + float height = 2; // Z position + float thickness = 3; // height of a single layer + + repeated PathSegment path_segment = 4; // layer data +} + + +message PathSegment { + int32 ordinal = 1; // The absolute ordinal of this line segment + bytes line_type = 2; // Type of line segment as an unsigned char array of length 1 or N, where N is the number of line segments in this path + bytes points = 3; // The points defining the line segments, bytes of int64[2]? array of length N+1 + bytes line_width = 4; // The widths of the line segments as bytes of a float?/int32? array of length 1 or N +} + + message GCodeLayer { bytes data = 2; } @@ -74,4 +103,4 @@ message GCodePrefix { } message SlicingFinished { -} +} \ No newline at end of file diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index c91e414a13..2edc558ba6 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -56,6 +56,7 @@ class CuraEngineBackend(Backend): Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged) self._onActiveViewChanged() self._stored_layer_data = [] + self._stored_optimized_layer_data = [] #Triggers for when to (re)start slicing: self._global_container_stack = None @@ -76,6 +77,7 @@ class CuraEngineBackend(Backend): #Listeners for receiving messages from the back-end. self._message_handlers["cura.proto.Layer"] = self._onLayerMessage + self._message_handlers["cura.proto.LayerOptimized"] = self._onOptimizedLayerMessage self._message_handlers["cura.proto.Progress"] = self._onProgressMessage self._message_handlers["cura.proto.GCodeLayer"] = self._onGCodeLayerMessage self._message_handlers["cura.proto.GCodePrefix"] = self._onGCodePrefixMessage @@ -127,6 +129,7 @@ class CuraEngineBackend(Backend): ## Perform a slice of the scene. def slice(self): self._stored_layer_data = [] + self._stored_optimized_layer_data = [] if not self._enabled or not self._global_container_stack: #We shouldn't be slicing. return @@ -158,6 +161,7 @@ class CuraEngineBackend(Backend): self._slicing = False self._restart = True self._stored_layer_data = [] + self._stored_optimized_layer_data = [] if self._start_slice_job is not None: self._start_slice_job.cancel() @@ -257,6 +261,12 @@ class CuraEngineBackend(Backend): def _onLayerMessage(self, message): self._stored_layer_data.append(message) + ## Called when an optimized sliced layer data message is received from the engine. + # + # \param message The protobuf message containing sliced layer data. + def _onOptimizedLayerMessage(self, message): + self._stored_optimized_layer_data.append(message) + ## Called when a progress message is received from the engine. # # \param message The protobuf message containing the slicing progress. @@ -274,9 +284,9 @@ class CuraEngineBackend(Backend): self._slicing = False if self._layer_view_active and (self._process_layers_job is None or not self._process_layers_job.isRunning()): - self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_layer_data) + self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_optimized_layer_data) self._process_layers_job.start() - self._stored_layer_data = [] + self._stored_optimized_layer_data = [] ## Called when a g-code message is received from the engine. # @@ -344,10 +354,10 @@ class CuraEngineBackend(Backend): self._layer_view_active = True # There is data and we're not slicing at the moment # if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment. - if self._stored_layer_data and not self._slicing: - self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_layer_data) + if self._stored_optimized_layer_data and not self._slicing: + self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_optimized_layer_data) self._process_layers_job.start() - self._stored_layer_data = [] + self._stored_optimized_layer_data = [] else: self._layer_view_active = False diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 0aa34b8f3f..9f235c14be 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -91,8 +91,8 @@ class ProcessSlicedLayersJob(Job): layer_data.setLayerHeight(abs_layer_number, layer.height) layer_data.setLayerThickness(abs_layer_number, layer.thickness) - for p in range(layer.repeatedMessageCount("polygons")): - polygon = layer.getRepeatedMessage("polygons", p) + for p in range(layer.repeatedMessageCount("path_segment")): + polygon = layer.getRepeatedMessage("path_segment", p) line_types = numpy.fromstring(polygon.line_type, dtype="u1") # Convert bytearray to numpy array line_types = line_types.reshape((-1,1)) From 41503d7d5b57a04d18857f4fd539b3978d2a357c Mon Sep 17 00:00:00 2001 From: Johan Kristensen Date: Tue, 28 Jun 2016 23:50:00 +0200 Subject: [PATCH 003/102] Adapting code to merge of simple_convex_hull and code cleanup. --- cura/Layer.py | 45 ++++++-------- cura/LayerDataBuilder.py | 1 - cura/LayerPolygon.py | 59 +++++++++++++------ .../ProcessSlicedLayersJob.py | 2 +- plugins/LayerView/LayerView.py | 8 +-- 5 files changed, 65 insertions(+), 50 deletions(-) diff --git a/cura/Layer.py b/cura/Layer.py index cb64d77c2d..03b96f4f3a 100644 --- a/cura/Layer.py +++ b/cura/Layer.py @@ -59,7 +59,7 @@ class Layer: result_index_offset += polygon.lineMeshElementCount() self._element_count += polygon.elementCount - return (result_vertex_offset,result_index_offset) + return (result_vertex_offset, result_index_offset) def createMesh(self): return self.createMeshOrJumps(True) @@ -68,56 +68,47 @@ class Layer: return self.createMeshOrJumps(False) def createMeshOrJumps(self, make_mesh): - builder = MeshBuilder() # This is never really used, only the mesh_data inside - index_pattern = numpy.array([[0,3,2,0,1,3]],dtype = numpy.int32 ) + builder = MeshBuilder() + index_pattern = numpy.array([[0, 3, 2, 0, 1, 3]], dtype = numpy.int32 ) line_count = 0 if make_mesh: for polygon in self._polygons: - line_count += polygon._mesh_line_count + line_count += polygon.meshLineCount else: for polygon in self._polygons: - line_count += polygon._jump_count + line_count += polygon.jumpCount # Reserve the neccesary space for the data upfront - builder.reserveFaceAndVerticeCount( 2*line_count, 4*line_count ) + builder.reserveFaceAndVertexCount(2 * line_count, 4 * line_count) for polygon in self._polygons: - #if make_mesh and (polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType): - # continue - #if not make_mesh and not (polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType): - # continue - - index_mask = numpy.logical_not(polygon._jump_mask) if make_mesh else polygon._jump_mask + # Filter out the types of lines we are not interesed in depending on whether we are drawing the mesh or the jumps. + index_mask = numpy.logical_not(polygon.jumpMask) if make_mesh else polygon.jumpMask - # Create an array with rows [p p+1] and only save those we whant to draw based on make_mesh - points = numpy.concatenate((polygon.data[:-1],polygon.data[1:]),1)[index_mask.ravel()] + # Create an array with rows [p p+1] and only keep those we whant to draw based on make_mesh + points = numpy.concatenate((polygon.data[:-1], polygon.data[1:]), 1)[index_mask.ravel()] # Line types of the points we want to draw - line_types = polygon._types[index_mask] + line_types = polygon.types[index_mask] - - #if polygon.type == LayerPolygon.InfillType or polygon.type == LayerPolygon.SkinType or polygon.type == LayerPolygon.SupportInfillType: - # points[:,1] -= 0.01 - #if polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType: - # points[:,1] += 0.01 - # Shift the z-axis according to previous implementation. + # Shift the z-axis according to previous implementation. if make_mesh: - points[polygon._orInfillSkin[line_types],1::3] -= 0.01 + points[polygon.isInfillOrSkinType(line_types), 1::3] -= 0.01 else: - points[:,1::3] += 0.01 + points[:, 1::3] += 0.01 # Create an array with normals and tile 2 copies to match size of points variable - normals = numpy.tile( polygon.getNormals()[index_mask.ravel()], (1,2)) + normals = numpy.tile( polygon.getNormals()[index_mask.ravel()], (1, 2)) # Scale all normals by the line width of the current line so we can easily offset. normals *= (polygon.lineWidths[index_mask.ravel()] / 2) # Create 4 points to draw each line segment, points +- normals results in 2 points each. Reshape to one point per line - f_points = numpy.concatenate((points-normals,points+normals),1).reshape((-1,3)) + f_points = numpy.concatenate((points-normals, points+normals), 1).reshape((-1, 3)) # index_pattern defines which points to use to draw the two faces for each lines egment, the following linesegment is offset by 4 - f_indices = ( index_pattern + numpy.arange(0,4*len(normals),4,dtype=numpy.int32).reshape((-1,1)) ).reshape((-1,3)) - f_colors = numpy.repeat(polygon._color_map[line_types], 4, 0) + f_indices = ( index_pattern + numpy.arange(0, 4 * len(normals), 4, dtype=numpy.int32).reshape((-1, 1)) ).reshape((-1, 3)) + f_colors = numpy.repeat(polygon.mapLineTypeToColor(line_types), 4, 0) builder.addFacesWithColor(f_points, f_indices, f_colors) diff --git a/cura/LayerDataBuilder.py b/cura/LayerDataBuilder.py index 85f9a23e7b..2215ed5f27 100644 --- a/cura/LayerDataBuilder.py +++ b/cura/LayerDataBuilder.py @@ -65,7 +65,6 @@ class LayerDataBuilder(MeshBuilder): ( vertex_offset, index_offset ) = data.build( vertex_offset, index_offset, vertices, colors, indices) self._element_counts[layer] = data.elementCount - self.clear() self.addVertices(vertices) self.addColors(colors) self.addIndices(indices.flatten()) diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index f2c84e0d57..b063660198 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -38,25 +38,24 @@ class LayerPolygon: self._colors = self.__color_map[self._types] self._color_map = self.__color_map - # type == LayerPolygon.InfillType or type == LayerPolygon.SkinType or type == LayerPolygon.SupportInfillType + # When type is used as index returns true if type == LayerPolygon.InfillType or type == LayerPolygon.SkinType or type == LayerPolygon.SupportInfillType # Should be generated in better way, not hardcoded. - self._orInfillSkin = numpy.array([0,0,0,1,0,0,1,1,0,0],dtype=numpy.bool) + self._isInfillOrSkinTypeMap = numpy.array([0, 0, 0, 1, 0, 0, 1, 1, 0, 0], dtype=numpy.bool) self._build_cache_line_mesh_mask = None self._build_cache_needed_points = None - def build_cache(self): - #if polygon.type == LayerPolygon.InfillType or polygon.type == LayerPolygon.MoveCombingType or polygon.type == LayerPolygon.MoveRetractionType: - # continue - self._build_cache_line_mesh_mask = numpy.logical_not(numpy.logical_or(self._jump_mask,self._types == LayerPolygon.InfillType )) + def buildCache(self): + # For the line mesh we do not draw Infill or Jumps. Therefore those lines are filtered out. + self._build_cache_line_mesh_mask = numpy.logical_not(numpy.logical_or(self._jump_mask, self._types == LayerPolygon.InfillType )) mesh_line_count = numpy.sum(self._build_cache_line_mesh_mask) self._index_begin = 0 self._index_end = mesh_line_count - self._build_cache_needed_points = numpy.ones((len(self._types),2), dtype=numpy.bool) + self._build_cache_needed_points = numpy.ones((len(self._types), 2), dtype=numpy.bool) # Only if the type of line segment changes do we need to add an extra vertex to change colors - self._build_cache_needed_points[1:,0][:,numpy.newaxis] = self._types[1:] != self._types[:-1] - # Remove points of types we don't want in the line mesh + self._build_cache_needed_points[1:, 0][:, numpy.newaxis] = self._types[1:] != self._types[:-1] + # Mark points as unneeded if they are of types we don't want in the line mesh according to the calculated mask numpy.logical_and(self._build_cache_needed_points, self._build_cache_line_mesh_mask, self._build_cache_needed_points ) self._vertex_begin = 0 @@ -65,29 +64,36 @@ class LayerPolygon: def build(self, vertex_offset, index_offset, vertices, colors, indices): if (self._build_cache_line_mesh_mask == None) or (self._build_cache_needed_points == None ): - self.build_cache() + self.buildCache() line_mesh_mask = self._build_cache_line_mesh_mask needed_points_list = self._build_cache_needed_points - - index_list = ( numpy.arange(len(self._types)).reshape((-1,1)) + numpy.array([[0,1]]) ).reshape((-1,1))[needed_points_list.reshape((-1,1))] + # Index to the points we need to represent the line mesh. This is constructed by generating simple + # start and end points for each line. For line segment n these are points n and n+1. Row n reads [n n+1] + # Then then the indices for the points we don't need are thrown away based on the pre-calculated list. + index_list = ( numpy.arange(len(self._types)).reshape((-1, 1)) + numpy.array([[0, 1]]) ).reshape((-1, 1))[needed_points_list.reshape((-1, 1))] + + # The relative values of begin and end indices have already been set in buildCache, so we only need to offset them to the parents offset. self._vertex_begin += vertex_offset self._vertex_end += vertex_offset + # Points are picked based on the index list to get the vertices needed. vertices[self._vertex_begin:self._vertex_end, :] = self._data[index_list, :] - colors[self._vertex_begin:self._vertex_end, :] = numpy.tile(self._colors,(1,2)).reshape((-1,4))[needed_points_list.ravel()] + # Create an array with colors for each vertex and remove the color data for the points that has been thrown away. + colors[self._vertex_begin:self._vertex_end, :] = numpy.tile(self._colors, (1, 2)).reshape((-1, 4))[needed_points_list.ravel()] colors[self._vertex_begin:self._vertex_end, :] *= numpy.array([[0.5, 0.5, 0.5, 1.0]], numpy.float32) + # The relative values of begin and end indices have already been set in buildCache, so we only need to offset them to the parents offset. self._index_begin += index_offset self._index_end += index_offset - indices[self._index_begin:self._index_end,:] = numpy.arange(self._index_end-self._index_begin, dtype=numpy.int32).reshape((-1,1)) + indices[self._index_begin:self._index_end, :] = numpy.arange(self._index_end-self._index_begin, dtype=numpy.int32).reshape((-1, 1)) # When the line type changes the index needs to be increased by 2. - indices[self._index_begin:self._index_end,:] += numpy.cumsum(needed_points_list[line_mesh_mask.ravel(),0],dtype=numpy.int32).reshape((-1,1)) + indices[self._index_begin:self._index_end, :] += numpy.cumsum(needed_points_list[line_mesh_mask.ravel(), 0], dtype=numpy.int32).reshape((-1, 1)) # Each line segment goes from it's starting point p to p+1, offset by the vertex index. # The -1 is to compensate for the neccecarily True value of needed_points_list[0,0] which causes an unwanted +1 in cumsum above. - indices[self._index_begin:self._index_end,:] += numpy.array([self._vertex_begin - 1,self._vertex_begin]) + indices[self._index_begin:self._index_end, :] += numpy.array([self._vertex_begin - 1, self._vertex_begin]) self._build_cache_line_mesh_mask = None self._build_cache_needed_points = None @@ -95,6 +101,12 @@ class LayerPolygon: def getColors(self): return self._colors + def mapLineTypeToColor(self, line_types): + return self._color_map[line_types] + + def isInfillOrSkinType(self, line_types): + return self._isInfillOrSkinTypeMap[line_types] + def lineMeshVertexCount(self): return (self._vertex_end - self._vertex_begin) @@ -116,6 +128,18 @@ class LayerPolygon: @property def lineWidths(self): return self._line_widths + + @property + def jumpMask(self): + return self._jump_mask + + @property + def meshLineCount(self): + return self._mesh_line_count + + @property + def jumpCount(self): + return self._jump_count # Calculate normals for the entire polygon using numpy. def getNormals(self): @@ -166,4 +190,5 @@ class LayerPolygon: [1.0, 0.74, 0.0, 1.0], [0.0, 1.0, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0], - [0.5, 0.5, 1.0, 1.0]]) + [0.5, 0.5, 1.0, 1.0] + ]) \ No newline at end of file diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 9f235c14be..44e7de7599 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -114,7 +114,7 @@ class ProcessSlicedLayersJob(Job): new_points /= 1000 this_poly = LayerPolygon.LayerPolygon(layer_data, line_types, new_points, line_widths) - this_poly.build_cache() + this_poly.buildCache() this_layer.polygons.append(this_poly) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 90927ff39d..a993dc3f61 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -259,12 +259,12 @@ class _CreateTopLayersJob(Job): layer_mesh.addIndices(layer_mesh._vertex_count+layer.getIndices()) layer_mesh.addVertices(layer.getVertices()) - + # Scale layer color by a brightness factor based on the current layer number # This will result in a range of 0.5 - 1.0 to multiply colors by. - brightness = numpy.ones((1,4),dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0 - brightness[0,3] = 1.0; - layer_mesh.addColors(layer.getColors() * brightness ) + brightness = numpy.ones((1,4), dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0 + brightness[0, 3] = 1.0; + layer_mesh.addColors(layer.getColors() * brightness) if self._cancel: return From bea67f0536b066494e15cae0b29aaf177f044654 Mon Sep 17 00:00:00 2001 From: Thomas-Karl Pietrowski Date: Fri, 8 Jul 2016 13:07:24 +0200 Subject: [PATCH 004/102] CURA-1445: Using getDurationInISOFormat in SliceInfo I think this is the way YouMagine expects the data to be sent. --- plugins/SliceInfoPlugin/SliceInfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index d3b93aacac..85d26a0b93 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -90,7 +90,7 @@ class SliceInfo(Extension): "settings": global_container_stack.serialize(), # global_container with references on used containers "version": Application.getInstance().getVersion(), "modelhash": "None", - "printtime": print_information.currentPrintTime.getDisplayString(), + "printtime": print_information.currentPrintTime.getDurationInISOFormat(), "filament": material_used, "language": Preferences.getInstance().getValue("general/language"), "materials_profiles ": {} From d31516bbb12d98e11c9b78317a10210a6401caa1 Mon Sep 17 00:00:00 2001 From: Johan K Date: Tue, 12 Jul 2016 01:32:46 +0200 Subject: [PATCH 005/102] Code cleanup Changed to "is None" insted of "== None" to test if numpy.array was initialized Added extruder to PathSegment in Cura.proto Changed index_pattern to class variable in Layer.py --- cura/Layer.py | 8 +++++--- cura/LayerPolygon.py | 2 +- plugins/CuraEngineBackend/Cura.proto | 15 ++++++++++----- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cura/Layer.py b/cura/Layer.py index 03b96f4f3a..4e38a6eba9 100644 --- a/cura/Layer.py +++ b/cura/Layer.py @@ -67,9 +67,11 @@ class Layer: def createJumps(self): return self.createMeshOrJumps(False) + # Defines the two triplets of local point indices to use to draw the two faces for each line segment in createMeshOrJump + __index_pattern = numpy.array([[0, 3, 2, 0, 1, 3]], dtype = numpy.int32 ) + def createMeshOrJumps(self, make_mesh): builder = MeshBuilder() - index_pattern = numpy.array([[0, 3, 2, 0, 1, 3]], dtype = numpy.int32 ) line_count = 0 if make_mesh: @@ -106,8 +108,8 @@ class Layer: # Create 4 points to draw each line segment, points +- normals results in 2 points each. Reshape to one point per line f_points = numpy.concatenate((points-normals, points+normals), 1).reshape((-1, 3)) - # index_pattern defines which points to use to draw the two faces for each lines egment, the following linesegment is offset by 4 - f_indices = ( index_pattern + numpy.arange(0, 4 * len(normals), 4, dtype=numpy.int32).reshape((-1, 1)) ).reshape((-1, 3)) + # __index_pattern defines which points to use to draw the two faces for each lines egment, the following linesegment is offset by 4 + f_indices = ( self.__index_pattern + numpy.arange(0, 4 * len(normals), 4, dtype=numpy.int32).reshape((-1, 1)) ).reshape((-1, 3)) f_colors = numpy.repeat(polygon.mapLineTypeToColor(line_types), 4, 0) builder.addFacesWithColor(f_points, f_indices, f_colors) diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index b063660198..c534592757 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -63,7 +63,7 @@ class LayerPolygon: def build(self, vertex_offset, index_offset, vertices, colors, indices): - if (self._build_cache_line_mesh_mask == None) or (self._build_cache_needed_points == None ): + if (self._build_cache_line_mesh_mask is None) or (self._build_cache_needed_points is None ): self.buildCache() line_mesh_mask = self._build_cache_line_mesh_mask diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto index af4b40859d..d38f95035f 100644 --- a/plugins/CuraEngineBackend/Cura.proto +++ b/plugins/CuraEngineBackend/Cura.proto @@ -61,7 +61,7 @@ message Polygon { float line_width = 3; // The width of the line being laid down } -message LayerOptimized { +message LayerOptimized { int32 id = 1; float height = 2; // Z position float thickness = 3; // height of a single layer @@ -71,10 +71,15 @@ message LayerOptimized { message PathSegment { - int32 ordinal = 1; // The absolute ordinal of this line segment - bytes line_type = 2; // Type of line segment as an unsigned char array of length 1 or N, where N is the number of line segments in this path - bytes points = 3; // The points defining the line segments, bytes of int64[2]? array of length N+1 - bytes line_width = 4; // The widths of the line segments as bytes of a float?/int32? array of length 1 or N + int32 extruder = 1; // The extruder used for this path segment + enum PointType { + Point2D = 0; + Point3D = 1; + } + PointType point_type = 2; + bytes points = 3; // The points defining the line segments, bytes of int64[2/3]? array of length N+1 + bytes line_type = 4; // Type of line segment as an unsigned char array of length 1 or N, where N is the number of line segments in this path + bytes line_width = 5; // The widths of the line segments as bytes of a float? array of length 1 or N } From 3d413df215ae5cd042b1d9ebaa2096224a46d0ac Mon Sep 17 00:00:00 2001 From: Johan K Date: Thu, 14 Jul 2016 11:08:05 +0200 Subject: [PATCH 006/102] Changed the point data type sent from the backend from int64 to float. Added extruder information to LayerPolygon --- cura/LayerPolygon.py | 9 +++++-- plugins/CuraEngineBackend/Cura.proto | 4 +-- .../ProcessSlicedLayersJob.py | 27 ++++++++++++------- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index c534592757..c62113916d 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -17,11 +17,12 @@ class LayerPolygon: __jump_map = numpy.logical_or( numpy.arange(10) == NoneType, numpy.arange(10) >= MoveCombingType ) - def __init__(self, mesh, line_types, data, line_widths): + def __init__(self, mesh, extruder, line_types, data, line_widths): self._mesh = mesh + self._extruder = extruder self._types = line_types self._data = data - self._line_widths = line_widths / 1000 + self._line_widths = line_widths self._vertex_begin = 0 self._vertex_end = 0 @@ -113,6 +114,10 @@ class LayerPolygon: def lineMeshElementCount(self): return (self._index_end - self._index_begin) + @property + def extruder(self): + return self._extruder + @property def types(self): return self._types diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto index d38f95035f..0c4803cc19 100644 --- a/plugins/CuraEngineBackend/Cura.proto +++ b/plugins/CuraEngineBackend/Cura.proto @@ -77,9 +77,9 @@ message PathSegment { Point3D = 1; } PointType point_type = 2; - bytes points = 3; // The points defining the line segments, bytes of int64[2/3]? array of length N+1 + bytes points = 3; // The points defining the line segments, bytes of float[2/3] array of length N+1 bytes line_type = 4; // Type of line segment as an unsigned char array of length 1 or N, where N is the number of line segments in this path - bytes line_width = 5; // The widths of the line segments as bytes of a float? array of length 1 or N + bytes line_width = 5; // The widths of the line segments as bytes of a float array of length 1 or N } diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 44e7de7599..5101940c84 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -94,26 +94,35 @@ class ProcessSlicedLayersJob(Job): for p in range(layer.repeatedMessageCount("path_segment")): polygon = layer.getRepeatedMessage("path_segment", p) + extruder = polygon.extruder + line_types = numpy.fromstring(polygon.line_type, dtype="u1") # Convert bytearray to numpy array line_types = line_types.reshape((-1,1)) - points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array - points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly. + points = numpy.fromstring(polygon.points, dtype="f4") # Convert bytearray to numpy array + if polygon.point_type == 0: # Point2D + points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly. + else: # Point3D + points = points.reshape((-1,3)) - line_widths = numpy.fromstring(polygon.line_width, dtype="i4") # Convert bytearray to numpy array + line_widths = numpy.fromstring(polygon.line_width, dtype="f4") # Convert bytearray to numpy array line_widths = line_widths.reshape((-1,1)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly. # Create a new 3D-array, copy the 2D points over and insert the right height. # This uses manual array creation + copy rather than numpy.insert since this is # faster. new_points = numpy.empty((len(points), 3), numpy.float32) - new_points[:,0] = points[:,0] - new_points[:,1] = layer.height - new_points[:,2] = -points[:,1] + if polygon.point_type == 0: # Point2D + new_points[:,0] = points[:,0] + new_points[:,1] = layer.height/1000 # layer height value is in backend representation + new_points[:,2] = -points[:,1] + else: # Point3D + new_points[:,0] = points[:,0] + new_points[:,1] = points[:,2] + new_points[:,2] = -points[:,1] + - new_points /= 1000 - - this_poly = LayerPolygon.LayerPolygon(layer_data, line_types, new_points, line_widths) + this_poly = LayerPolygon.LayerPolygon(layer_data, extruder, line_types, new_points, line_widths) this_poly.buildCache() this_layer.polygons.append(this_poly) From 7731f9406c1807f9e63f6dab154a09910739525e Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 19 Jul 2016 15:59:44 +0200 Subject: [PATCH 007/102] Dialog_profile/material/load_path is now saved and used. CURA-1900 --- cura/CuraApplication.py | 22 +++++++++++++++++---- resources/qml/Cura.qml | 3 ++- resources/qml/Preferences/MaterialsPage.qml | 6 ++++-- resources/qml/Preferences/ProfilesPage.qml | 6 ++++-- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 1164c57008..29c1f086b4 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -194,6 +194,14 @@ class CuraApplication(QtApplication): Preferences.getInstance().addPreference("view/center_on_select", True) Preferences.getInstance().addPreference("mesh/scale_to_fit", True) Preferences.getInstance().addPreference("mesh/scale_tiny_meshes", True) + + for key in [ + "dialog_load_path", # dialog_save_path is in LocalFileOutputDevicePlugin + "dialog_profile_path", + "dialog_material_path"]: + + Preferences.getInstance().addPreference("local_file/%s" % key, "~/") + Preferences.getInstance().setDefault("local_file/last_used_type", "text/x-gcode") Preferences.getInstance().setDefault("general/visible_settings", """ @@ -332,10 +340,16 @@ class CuraApplication(QtApplication): f.write(data) - @pyqtSlot(result = QUrl) - def getDefaultPath(self): - return QUrl.fromLocalFile(os.path.expanduser("~/")) - + @pyqtSlot(str, result = QUrl) + def getDefaultPath(self, key): + #return QUrl.fromLocalFile(os.path.expanduser("~/")) + default_path = Preferences.getInstance().getValue("local_file/%s" % key) + return QUrl.fromLocalFile(default_path) + + @pyqtSlot(str, str) + def setDefaultPath(self, key, default_path): + Preferences.getInstance().setValue("local_file/%s" % key, default_path) + ## Handle loading of all plugin types (and the backend explicitly) # \sa PluginRegistery def _loadPlugins(self): diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 8404db01fa..c449b4c83e 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -638,7 +638,7 @@ UM.MainWindow //TODO: Support multiple file selection, workaround bug in KDE file dialog //selectMultiple: true nameFilters: UM.MeshFileHandler.supportedReadFileTypes; - folder: Printer.getDefaultPath() + folder: CuraApplication.getDefaultPath("dialog_load_path") onAccepted: { //Because several implementations of the file dialog only update the folder @@ -646,6 +646,7 @@ UM.MainWindow var f = folder; folder = f; + CuraApplication.setDefaultPath("dialog_load_path", folder); UM.MeshFileHandler.readLocalFile(fileUrl) var meshName = backgroundItem.getMeshName(fileUrl.toString()) backgroundItem.hasMesh(decodeURIComponent(meshName)) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index 18059545e2..aaaa4f5e9d 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -200,7 +200,7 @@ UM.ManagementPage title: catalog.i18nc("@title:window", "Import Material"); selectExisting: true; nameFilters: Cura.ContainerManager.getContainerNameFilters("material") - folder: CuraApplication.getDefaultPath() + folder: CuraApplication.getDefaultPath("dialog_material_path") onAccepted: { var result = Cura.ContainerManager.importContainer(fileUrl) @@ -221,6 +221,7 @@ UM.ManagementPage messageDialog.icon = StandardIcon.Critical } messageDialog.open() + CuraApplication.setDefaultPath("dialog_material_path", folder) } } @@ -230,7 +231,7 @@ UM.ManagementPage title: catalog.i18nc("@title:window", "Export Material"); selectExisting: false; nameFilters: Cura.ContainerManager.getContainerNameFilters("material") - folder: CuraApplication.getDefaultPath() + folder: CuraApplication.getDefaultPath("dialog_material_path") onAccepted: { if(base.currentItem.metadata.base_file) @@ -255,6 +256,7 @@ UM.ManagementPage messageDialog.text = catalog.i18nc("@info:status", "Successfully exported material to %1").arg(fileUrl) messageDialog.open() } + CuraApplication.setDefaultPath("dialog_material_path", folder) } } diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index 5a56ac8dc3..119a16facc 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -291,7 +291,7 @@ UM.ManagementPage title: catalog.i18nc("@title:window", "Import Profile"); selectExisting: true; nameFilters: base.model.getFileNameFilters("profile_reader") - folder: base.model.getDefaultPath() + folder: CuraApplication.getDefaultPath("dialog_profile_path") onAccepted: { var result = base.model.importProfile(fileUrl) @@ -309,6 +309,7 @@ UM.ManagementPage messageDialog.icon = StandardIcon.Critical } messageDialog.open() + CuraApplication.setDefaultPath("dialog_profile_path", folder) } } @@ -318,7 +319,7 @@ UM.ManagementPage title: catalog.i18nc("@title:window", "Export Profile"); selectExisting: false; nameFilters: base.model.getFileNameFilters("profile_writer") - folder: base.model.getDefaultPath() + folder: CuraApplication.getDefaultPath("dialog_profile_path") onAccepted: { var result = base.model.exportProfile(base.currentItem.id, fileUrl, selectedNameFilter) @@ -329,6 +330,7 @@ UM.ManagementPage messageDialog.open() } // else pop-up Message thing from python code + CuraApplication.setDefaultPath("dialog_profile_path", folder) } } } From da047c6f908ed92f7e1476894bff2f4f14bbf4ed Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 19 Jul 2016 16:56:26 +0200 Subject: [PATCH 008/102] Added benchmark time logs --- plugins/CuraEngineBackend/CuraEngineBackend.py | 7 ++++++- plugins/CuraEngineBackend/ProcessSlicedLayersJob.py | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index d2f34da9a3..eca6d1fdba 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -22,6 +22,7 @@ from . import StartSliceJob import os import sys +from time import time from PyQt5.QtCore import QTimer @@ -99,6 +100,8 @@ class CuraEngineBackend(Backend): Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted) Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped) + self._slice_start_time = None + ## Called when closing the application. # # This function should terminate the engine process. @@ -127,6 +130,7 @@ class CuraEngineBackend(Backend): ## Perform a slice of the scene. def slice(self): + self._slice_start_time = time() if not self._enabled or not self._global_container_stack: #We shouldn't be slicing. # try again in a short time self._change_timer.start() @@ -214,6 +218,7 @@ class CuraEngineBackend(Backend): # Preparation completed, send it to the backend. self._socket.sendMessage(job.getSliceMessage()) + Logger.log("d", "Sending slice message took %s seconds", time() - self._slice_start_time ) ## Listener for when the scene has changed. # @@ -277,7 +282,7 @@ class CuraEngineBackend(Backend): self.processingProgress.emit(1.0) self._slicing = False - + Logger.log("d", "Slicing took %s seconds", time() - self._slice_start_time ) if self._layer_view_active and (self._process_layers_job is None or not self._process_layers_job.isRunning()): self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_layer_data) self._process_layers_job.start() diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index d23f71e874..599ed52fc1 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -9,6 +9,7 @@ from UM.Mesh.MeshData import MeshData from UM.Message import Message from UM.i18n import i18nCatalog +from UM.Logger import Logger from UM.Math.Vector import Vector @@ -16,7 +17,7 @@ from cura import LayerDataBuilder from cura import LayerDataDecorator import numpy - +from time import time catalog = i18nCatalog("cura") @@ -38,6 +39,7 @@ class ProcessSlicedLayersJob(Job): self._abort_requested = True def run(self): + start_time = time() if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView": self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1) self._progress.show() @@ -147,6 +149,8 @@ class ProcessSlicedLayersJob(Job): # Clear the unparsed layers. This saves us a bunch of memory if the Job does not get destroyed. self._layers = None + Logger.log("d", "Processing layers took %s seconds", time() - start_time) + def _onActiveViewChanged(self): if self.isRunning(): if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView": From 6e7c1759d34c18ddb20d2ecd8459b3326d89fb39 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 19 Jul 2016 17:04:42 +0200 Subject: [PATCH 009/102] Remove commented out code CURA-1900 --- cura/CuraApplication.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 29c1f086b4..32a7391159 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -342,7 +342,6 @@ class CuraApplication(QtApplication): @pyqtSlot(str, result = QUrl) def getDefaultPath(self, key): - #return QUrl.fromLocalFile(os.path.expanduser("~/")) default_path = Preferences.getInstance().getValue("local_file/%s" % key) return QUrl.fromLocalFile(default_path) From 19c60ee5844bcf8e120f82432f89d2625773ed0c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 20 Jul 2016 09:34:03 +0200 Subject: [PATCH 010/102] Group delet checking is more explicit CURA-1891 --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 29c1f086b4..689ef2c783 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -583,7 +583,7 @@ class CuraApplication(QtApplication): op.push() if group_node: - if len(group_node.getChildren()) == 1: + if len(group_node.getChildren()) == 1 and group_node.callDecoration("isGroup"): group_node.getChildren()[0].setParent(group_node.getParent()) op = RemoveSceneNodeOperation(group_node) op.push() From aed033674f54e555d992848eb2205664dbf06745 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 20 Jul 2016 09:39:19 +0200 Subject: [PATCH 011/102] Changed some warnings into errors CURA-1706 --- resources/definitions/fdmextruder.def.json | 4 ++-- resources/definitions/fdmprinter.def.json | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/definitions/fdmextruder.def.json b/resources/definitions/fdmextruder.def.json index 7ce44b77b0..bde24ee684 100644 --- a/resources/definitions/fdmextruder.def.json +++ b/resources/definitions/fdmextruder.def.json @@ -153,7 +153,7 @@ "unit": "mm", "default_value": 0, "minimum_value_warning": "0", - "maximum_value_warning": "machine_height", + "maximum_value": "machine_height", "settable_per_mesh": false, "settable_per_extruder": true } @@ -175,7 +175,7 @@ "unit": "mm", "default_value": 0, "minimum_value_warning": "machine_nozzle_offset_x", - "maximum_value_warning": "machine_width", + "maximum_value": "machine_width", "settable_per_mesh": false, "settable_per_extruder": true, "enabled": false diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index d4933be5bb..8f435c1e5c 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -342,7 +342,7 @@ "unit": "mm", "default_value": 0, "minimum_value_warning": "0", - "maximum_value_warning": "machine_height", + "maximum_value": "machine_height", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -706,7 +706,7 @@ "default_value": 0.8, "minimum_value": "0", "minimum_value_warning": "0.6", - "maximum_value_warning": "machine_height", + "maximum_value": "machine_height", "type": "float", "settable_per_mesh": true, "children": @@ -718,7 +718,7 @@ "unit": "mm", "default_value": 0.8, "minimum_value": "0", - "maximum_value_warning": "machine_height", + "maximum_value": "machine_height", "type": "float", "value": "top_bottom_thickness", "settable_per_mesh": true, @@ -746,7 +746,7 @@ "minimum_value": "0", "type": "float", "value": "top_bottom_thickness", - "maximum_value_warning": "machine_height", + "maximum_value": "machine_height", "settable_per_mesh": true, "children": { From bb4d4e78077c4ff07251e5e6203d1585e107e0b0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 20 Jul 2016 11:01:38 +0200 Subject: [PATCH 012/102] Moved viewmode in order so the tooltip is displayed over per-object settings panel CURA-1651 --- resources/qml/Cura.qml | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index c449b4c83e..d0870991d2 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -321,23 +321,7 @@ UM.MainWindow sourceSize.height: height; } - Button - { - id: viewModeButton - anchors - { - top: toolbar.bottom; - topMargin: UM.Theme.getSize("window_margin").height; - left: parent.left; - } - text: catalog.i18nc("@action:button","View Mode"); - iconSource: UM.Theme.getIcon("viewmode"); - - style: UM.Theme.styles.tool_button; - tooltip: ''; - menu: ViewMenu { } - } Toolbar { @@ -367,6 +351,24 @@ UM.MainWindow width: UM.Theme.getSize("sidebar").width; } + Button + { + id: viewModeButton + + anchors + { + top: toolbar.bottom; + topMargin: UM.Theme.getSize("window_margin").height; + left: parent.left; + } + text: catalog.i18nc("@action:button","View Mode"); + iconSource: UM.Theme.getIcon("viewmode"); + + style: UM.Theme.styles.tool_button; + tooltip: ''; + menu: ViewMenu { } + } + Rectangle { id: viewportOverlay From b69388525f2f6efc8670873c1536463ba642b872 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 20 Jul 2016 11:06:26 +0200 Subject: [PATCH 013/102] Fixed uncaught exception on first run --- cura/PlatformPhysics.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 191f7b0e27..23de74b80e 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -48,7 +48,13 @@ class PlatformPhysics: bbox = node.getBoundingBox() # Ignore intersections with the bottom - build_volume_bounding_box = self._build_volume.getBoundingBox().set(bottom=-9001) + build_volume_bounding_box = self._build_volume.getBoundingBox() + if build_volume_bounding_box: + build_volume_bounding_box.set(bottom=-9001) + else: + # No bounding box. This is triggered when running Cura from command line with a model for the first time + # In that situation there is a model, but no machine (and therefore no build volume. + return node._outside_buildarea = False # Mark the node as outside the build volume if the bounding box test fails. From 79f640e344967714839c30c4c028d764f360a34c Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 20 Jul 2016 11:12:44 +0200 Subject: [PATCH 014/102] Fixed simple sidebar infill label font. CURA-1824 --- resources/qml/SidebarSimple.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index a393500fb7..04b93e7d2a 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -125,6 +125,7 @@ Item } Label{ id: infillLabel + font: UM.Theme.getFont("default") anchors.top: infillIconLining.bottom anchors.horizontalCenter: infillIconLining.horizontalCenter color: infillListView.activeIndex == index ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_border") From 923e4de4ed95236a09e41e9b3effd024861fd37e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 20 Jul 2016 11:13:24 +0200 Subject: [PATCH 015/102] Stack changes now also ensure that validation state change signal is emitted CURA-1890 --- cura/Settings/MachineManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 2b2135e4a3..0c435ba2b9 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -233,7 +233,7 @@ class MachineManager(QObject): self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged) self._global_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged) self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack) - + self.globalValidationChanged.emit() material = self._global_container_stack.findContainer({"type": "material"}) material.nameChanged.connect(self._onMaterialNameChanged) From 612bb39680bf763931236e09c0d352a2003812c1 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 20 Jul 2016 11:36:32 +0200 Subject: [PATCH 016/102] Compensated build volume bbox and scale_to_max_bounds with raft_thickness. Rework of CURA-1707 --- cura/BuildVolume.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 1709a67dd5..e5bd037f1b 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -191,7 +191,9 @@ class BuildVolume(SceneNode): else: self._disallowed_area_mesh = None - self._volume_aabb = AxisAlignedBox(minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h, max_d)) + self._volume_aabb = AxisAlignedBox( + minimum = Vector(min_w, min_h - 1.0, min_d), + maximum = Vector(max_w, max_h - self._raft_thickness, max_d)) skirt_size = 0.0 @@ -204,7 +206,7 @@ class BuildVolume(SceneNode): # The +1 and -1 is added as there is always a bit of extra room required to work properly. scale_to_max_bounds = AxisAlignedBox( minimum = Vector(min_w + skirt_size + 1, min_h, min_d + disallowed_area_size - skirt_size + 1), - maximum = Vector(max_w - skirt_size - 1, max_h, max_d - disallowed_area_size + skirt_size - 1) + maximum = Vector(max_w - skirt_size - 1, max_h - self._raft_thickness, max_d - disallowed_area_size + skirt_size - 1) ) Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds From 6ec69d523b2bea433f5fc94d7a21669bc9744d25 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 20 Jul 2016 13:05:45 +0200 Subject: [PATCH 017/102] Fixed reloading meshes for 3mf CURA-1622 --- cura/CuraApplication.py | 6 +++++- plugins/3MFReader/ThreeMFReader.py | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 4715ff6009..361facc30c 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -851,7 +851,11 @@ class CuraApplication(QtApplication): def _reloadMeshFinished(self, job): # TODO; This needs to be fixed properly. We now make the assumption that we only load a single mesh! - job._node.setMeshData(job.getResult().getMeshData()) + mesh_data = job.getResult().getMeshData() + if mesh_data: + job._node.setMeshData(job.getResult().getMeshData()) + else: + Logger.log("w", "Could not find a mesh in reloaded node.") def _openFile(self, file): job = ReadMeshJob(os.path.abspath(file)) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 2a51e442da..57d76b2783 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -111,6 +111,8 @@ class ThreeMFReader(MeshReader): if len(objects) > 1: group_decorator = GroupDecorator() result.addDecorator(group_decorator) + elif len(objects) == 1: + result = result.getChildren()[0] # Only one object found, return that. except Exception as e: Logger.log("e", "exception occured in 3mf reader: %s", e) From cbbf62106d90ac154384f62d3eb45797cf93c9d3 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 20 Jul 2016 13:25:05 +0200 Subject: [PATCH 018/102] Fix moving objects through bottom. --- cura/PlatformPhysics.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 23de74b80e..d2a848dd72 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -50,7 +50,8 @@ class PlatformPhysics: # Ignore intersections with the bottom build_volume_bounding_box = self._build_volume.getBoundingBox() if build_volume_bounding_box: - build_volume_bounding_box.set(bottom=-9001) + # It's over 9000! + build_volume_bounding_box = build_volume_bounding_box.set(bottom=-9001) else: # No bounding box. This is triggered when running Cura from command line with a model for the first time # In that situation there is a model, but no machine (and therefore no build volume. From 77c5d25e70f42c403d6b271d61f34c172d290e35 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 20 Jul 2016 14:11:16 +0200 Subject: [PATCH 019/102] Cutoff point for convex hull is now a bit below 0 to prevent rounding issues. CURA-1608 --- cura/ConvexHullDecorator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py index f8d1a1e12e..3ccb9481f9 100644 --- a/cura/ConvexHullDecorator.py +++ b/cura/ConvexHullDecorator.py @@ -157,7 +157,7 @@ class ConvexHullDecorator(SceneNodeDecorator): vertex_data = mesh.getConvexHullTransformedVertices(world_transform) # Don't use data below 0. # TODO; We need a better check for this as this gives poor results for meshes with long edges. - vertex_data = vertex_data[vertex_data[:,1] >= 0] + vertex_data = vertex_data[vertex_data[:,1] >= -0.01] if len(vertex_data) >= 4: # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices From f8a9fa1a660279e7c35fcaea158771830345074a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 20 Jul 2016 14:44:44 +0200 Subject: [PATCH 020/102] Deleting objects from groups no longer cause position changes CURA-1891 --- cura/CuraApplication.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 361facc30c..79bb21cd15 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -583,6 +583,7 @@ class CuraApplication(QtApplication): op.push() if group_node: if len(group_node.getChildren()) == 1 and group_node.callDecoration("isGroup"): + group_node.getChildren()[0].translate(group_node.getPosition()) group_node.getChildren()[0].setParent(group_node.getParent()) op = RemoveSceneNodeOperation(group_node) op.push() From 33313ddb707b6f83a0e14c3589c1c84ecd6d5308 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 20 Jul 2016 15:15:16 +0200 Subject: [PATCH 021/102] Fixed some imports for USB printing --- plugins/USBPrinting/USBPrinterOutputDevice.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index da3dfa2bde..6b5fe22fcf 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -8,14 +8,12 @@ import time import queue import re import functools -import os.path from UM.Application import Application from UM.Logger import Logger -from UM.PluginRegistry import PluginRegistry from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState +from UM.Message import Message -from PyQt5.QtQml import QQmlComponent, QQmlContext from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal from UM.i18n import i18nCatalog @@ -137,7 +135,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): # \param gcode_list List with gcode (strings). def printGCode(self, gcode_list): if self._progress or self._connection_state != ConnectionState.connected: - self._error_message = Message(i18n_catalog.i18nc("@info:status", "Printer is busy or not connected. Unable to start a new job.")) + self._error_message = Message(catalog.i18nc("@info:status", "Printer is busy or not connected. Unable to start a new job.")) self._error_message.show() Logger.log("d", "Printer is busy or not connected, aborting print") self.writeError.emit(self) From 8eef34ac4587630cfecbe6efee5f5f8123150c86 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 21 Jul 2016 10:30:22 +0200 Subject: [PATCH 022/102] USB printer connection is now reset when print is completed CURA-1822 --- plugins/USBPrinting/USBPrinterOutputDevice.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 6b5fe22fcf..7bd5b66cc2 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -502,6 +502,13 @@ class USBPrinterOutputDevice(PrinterOutputDevice): # It will be normalized (based on max_progress) to range 0 - 100 def setProgress(self, progress, max_progress = 100): self._progress = (progress / max_progress) * 100 # Convert to scale of 0-100 + if self._progress == 100: + # Printing is done, reset progress + self._gcode_position = 0 + self.setProgress(0) + self._is_printing = False + self._is_paused = False + self._updateJobState("ready") self.progressChanged.emit() ## Cancel the current print. Printer connection wil continue to listen. From 6602177102ab3a0b10c3155b4f584b767dded342 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 21 Jul 2016 13:20:37 +0200 Subject: [PATCH 023/102] Removed unneeded getMeshData call CURA-1622 --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 79bb21cd15..82cc769172 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -854,7 +854,7 @@ class CuraApplication(QtApplication): # TODO; This needs to be fixed properly. We now make the assumption that we only load a single mesh! mesh_data = job.getResult().getMeshData() if mesh_data: - job._node.setMeshData(job.getResult().getMeshData()) + job._node.setMeshData(mesh_data) else: Logger.log("w", "Could not find a mesh in reloaded node.") From 121cd19e83be480d590d5fef48bfbed3087124f0 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Sat, 16 Jul 2016 13:10:50 +0200 Subject: [PATCH 024/102] Disable the settings in the global tab CURA-1758 --- cura/Settings/ExtruderManager.py | 6 ++++++ resources/qml/Settings/SettingView.qml | 19 ++++++++++--------- resources/qml/SidebarHeader.qml | 2 ++ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 744a6811c5..987b161776 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -41,6 +41,12 @@ class ExtruderManager(QObject): except KeyError: # Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong. return None + @pyqtProperty(int, notify = extrudersChanged) + def extruderCount(self): + if not UM.Application.getInstance().getGlobalContainerStack(): + return 0 # No active machine, so no extruders. + return len(self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()]) + ## The instance of the singleton pattern. # # It's None if the extruder manager hasn't been created yet. diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 39f0f833b8..faa7bf94cb 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -34,14 +34,7 @@ ScrollView expanded: Printer.expandedCategories onExpandedChanged: Printer.setExpandedCategories(expanded) - filter: - { - if(ExtruderManager.activeExtruderStackId) - { - return { "settable_per_extruder": true } - } - return { } - } + filter: {} } delegate: Loader @@ -53,7 +46,15 @@ ScrollView Behavior on height { NumberAnimation { duration: 100 } } opacity: provider.properties.enabled == "True" ? 1 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } - enabled: provider.properties.enabled == "True" + enabled: + { + if(!ExtruderManager.activeExtruderStackId && ExtruderManager.extruderCount > 0) + { + // disable all controls on the global tab, except categories + return model.type == "category" + } + return provider.properties.enabled == "True" + } property var definition: model property var settingDefinitionsModel: definitionsModel diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index aa6f2c0067..e6c3e8a86c 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -258,6 +258,8 @@ Column { id: globalProfileSelection text: Cura.MachineManager.activeQualityName + enabled: !extrudersList.visible || base.currentExtruderIndex > -1 + width: parent.width * 0.55 + UM.Theme.getSize("default_margin").width height: UM.Theme.getSize("setting_control").height tooltip: Cura.MachineManager.activeQualityName From 8537c5a0e21ada157e1dd8483d6b7b1df4738956 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Sat, 16 Jul 2016 13:14:04 +0200 Subject: [PATCH 025/102] Introduce a property that says which extruder the global stack should get its value from. CURA-1758 --- cura/CuraApplication.py | 1 + resources/definitions/fdmprinter.def.json | 37 +++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 82cc769172..5ba22002bb 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -97,6 +97,7 @@ class CuraApplication(QtApplication): SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True) + SettingDefinition.addSupportedProperty("global_inherits_stack", DefinitionPropertyType.Function, default = "0") SettingDefinition.addSettingType("extruder", int, str, Validator) ## Add the 4 types of profiles to storage. diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 8f435c1e5c..e6f7766600 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -629,6 +629,7 @@ "type": "float", "enabled": "support_enable", "value": "line_width", + "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, "settable_per_extruder": false }, @@ -643,6 +644,7 @@ "type": "float", "enabled": "support_roof_enable", "value": "line_width", + "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, "settable_per_extruder": false }, @@ -1442,6 +1444,7 @@ "maximum_value_warning": "150", "default_value": 60, "value": "speed_print", + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, "settable_per_extruder": false, @@ -1458,6 +1461,7 @@ "maximum_value": "299792458000", "maximum_value_warning": "150", "value": "speed_support", + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, "settable_per_extruder": false @@ -1474,6 +1478,7 @@ "maximum_value_warning": "150", "enabled": "support_roof_enable and support_enable", "value": "speed_support / 1.5", + "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, "settable_per_extruder": false } @@ -1644,6 +1649,7 @@ "maximum_value_warning": "10000", "default_value": 3000, "value": "acceleration_print", + "global_inherits_stack": "support_extruder_nr", "enabled": "acceleration_enabled and support_enable", "settable_per_mesh": false, "settable_per_extruder": false, @@ -1655,6 +1661,7 @@ "type": "float", "default_value": 3000, "value": "acceleration_support", + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "100", "maximum_value_warning": "10000", @@ -1669,6 +1676,7 @@ "type": "float", "default_value": 3000, "value": "acceleration_support", + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "100", "maximum_value_warning": "10000", @@ -1832,6 +1840,7 @@ "maximum_value_warning": "50", "default_value": 20, "value": "jerk_print", + "global_inherits_stack": "support_extruder_nr", "enabled": "jerk_enabled and support_enable", "settable_per_mesh": false, "settable_per_extruder": false, @@ -1843,6 +1852,7 @@ "type": "float", "default_value": 20, "value": "jerk_support", + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "5", "maximum_value_warning": "50", @@ -1857,6 +1867,7 @@ "type": "float", "default_value": 20, "value": "jerk_support", + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "5", "maximum_value_warning": "50", @@ -2121,6 +2132,7 @@ "description": "Enable support structures. These structures support parts of the model with severe overhangs.", "type": "bool", "default_value": false, + "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": true }, "support_type": @@ -2134,6 +2146,7 @@ "everywhere": "Everywhere" }, "default_value": "everywhere", + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, "settable_per_extruder": false @@ -2147,6 +2160,7 @@ "minimum_value": "0", "maximum_value": "90", "default_value": 50, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": true }, @@ -2164,6 +2178,7 @@ "zigzag": "Zig Zag" }, "default_value": "zigzag", + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, "settable_per_extruder": false @@ -2174,6 +2189,7 @@ "description": "Connect the ZigZags. This will increase the strength of the zig zag support structure.", "type": "bool", "default_value": true, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable and (support_pattern == \"zigzag\")", "settable_per_mesh": false, "settable_per_extruder": false @@ -2187,6 +2203,7 @@ "minimum_value": "0", "maximum_value_warning": "100", "default_value": 15, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, "settable_per_extruder": false, @@ -2201,6 +2218,7 @@ "default_value": 2.66, "enabled": "support_enable", "value": "(support_line_width * 100) / support_infill_rate * (2 if support_pattern == \"grid\" else (3 if support_pattern == \"triangles\" else 1))", + "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, "settable_per_extruder": false } @@ -2215,6 +2233,7 @@ "minimum_value": "0", "maximum_value_warning": "10", "default_value": 0.15, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": true, "children": @@ -2230,6 +2249,7 @@ "type": "float", "enabled": "support_enable", "value": "support_z_distance", + "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": true }, "support_bottom_distance": @@ -2241,6 +2261,7 @@ "maximum_value_warning": "10", "default_value": 0.1, "value": "0.1 if support_type == 'everywhere' else 0", + "global_inherits_stack": "support_extruder_nr", "type": "float", "enabled": "support_enable and support_type == 'everywhere'", "settable_per_mesh": true @@ -2256,6 +2277,7 @@ "minimum_value": "0", "maximum_value_warning": "10", "default_value": 0.7, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": true }, @@ -2268,6 +2290,7 @@ "z_overrides_xy": "Z overrides X/Y" }, "default_value": "z_overrides_xy", + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": true }, @@ -2280,6 +2303,7 @@ "maximum_value_warning": "10", "default_value": 0.2, "value": "machine_nozzle_size / 2", + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable and support_xy_overrides_z=='z_overrides_xy'", "settable_per_mesh": true }, @@ -2290,6 +2314,7 @@ "unit": "mm", "type": "float", "default_value": 0.3, + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0", "maximum_value_warning": "1.0", "enabled": "support_enable", @@ -2302,6 +2327,7 @@ "unit": "mm", "type": "float", "default_value": 2.0, + "global_inherits_stack": "support_extruder_nr", "minimum_value_warning": "0", "maximum_value_warning": "10", "enabled": "support_enable", @@ -2314,6 +2340,7 @@ "unit": "mm", "type": "float", "default_value": 0.2, + "global_inherits_stack": "support_extruder_nr", "minimum_value_warning": "-0.5", "maximum_value_warning": "5.0", "enabled": "support_enable", @@ -2326,6 +2353,7 @@ "unit": "mm", "type": "float", "default_value": 0.6, + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0", "maximum_value_warning": "1.0", "enabled": "support_enable", @@ -2337,6 +2365,7 @@ "description": "Generate a dense top skin at the top of the support on which the model is printed.", "type": "bool", "default_value": false, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": true }, @@ -2348,6 +2377,7 @@ "type": "float", "default_value": 1, "minimum_value": "0", + "global_inherits_stack": "support_extruder_nr", "maximum_value_warning": "10", "enabled": "support_roof_enable and support_enable", "settable_per_mesh": true @@ -2359,6 +2389,7 @@ "unit": "%", "type": "float", "default_value": 100, + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0", "maximum_value_warning": "100", "enabled":"support_roof_enable and support_enable", @@ -2375,6 +2406,7 @@ "default_value": 0.4, "minimum_value": "0", "value": "0 if support_roof_density == 0 else (support_roof_line_width * 100) / support_roof_density * (2 if support_roof_pattern == \"grid\" else (3 if support_roof_pattern == \"triangles\" else 1))", + "global_inherits_stack": "support_extruder_nr", "enabled": "support_roof_enable and support_enable", "settable_per_mesh": false, "settable_per_extruder": false @@ -2395,6 +2427,7 @@ "zigzag": "Zig Zag" }, "default_value": "concentric", + "global_inherits_stack": "support_extruder_nr", "enabled": "support_roof_enable and support_enable", "settable_per_mesh": false, "settable_per_extruder": false @@ -2405,6 +2438,7 @@ "description": "Use specialized towers to support tiny overhang areas. These towers have a larger diameter than the region they support. Near the overhang the towers' diameter decreases, forming a roof.", "type": "bool", "default_value": true, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": true }, @@ -2415,6 +2449,7 @@ "unit": "mm", "type": "float", "default_value": 3.0, + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0", "maximum_value_warning": "10", "enabled": "support_enable and support_use_towers", @@ -2427,6 +2462,7 @@ "unit": "mm", "type": "float", "default_value": 3.0, + "global_inherits_stack": "support_extruder_nr", "minimum_value": "0", "maximum_value_warning": "10", "maximum_value": "support_tower_diameter", @@ -2442,6 +2478,7 @@ "minimum_value": "0", "maximum_value": "90", "default_value": 65, + "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable and support_use_towers", "settable_per_mesh": true } From 2387da1a28fb874a947be7a7967c14e249f026c4 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Sat, 16 Jul 2016 13:16:27 +0200 Subject: [PATCH 026/102] Make the global stack value mirror the stack value appropriate for this setting CURA-1758 --- cura/Settings/MachineManager.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 0c435ba2b9..f1b093ba27 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -198,6 +198,12 @@ class MachineManager(QObject): def _onGlobalPropertyChanged(self, key, property_name): if property_name == "value": self.globalValueChanged.emit() + + if self._active_container_stack and self._active_container_stack != self._global_container_stack: + # Make the global stack value mirror the stack value appropriate for this setting + if self._active_container_stack.getProperty("extruder_nr", "value") == int(self._active_container_stack.getProperty(key, "global_inherits_stack")): + self._global_container_stack.getTop().setProperty(key, "value", self._active_container_stack.getProperty(key, "value")) + if property_name == "validationState": if self._global_stack_valid: changed_validation_state = self._active_container_stack.getProperty(key, property_name) From 97b3dd8f706559faee20a6e91310457d89babecd Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 18 Jul 2016 10:35:36 +0200 Subject: [PATCH 027/102] Copy values of global-only setting values to all extruders CURA-1758 --- cura/Settings/ExtruderManager.py | 13 ++++++++++--- cura/Settings/MachineManager.py | 7 +++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 987b161776..78468acbcb 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -112,8 +112,11 @@ class ExtruderManager(QObject): for extruder_train in extruder_trains: self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train - # Ensure that the extruder train stacks are linked to global stack. - extruder_train.setNextStack(UM.Application.getInstance().getGlobalContainerStack()) + # Make sure the next stack is a stack that contains only the machine definition + if not extruder_train.getNextStack(): + shallowStack = UM.Settings.ContainerStack(machine_id + "_shallow") + shallowStack.addContainer(machine_definition) + extruder_train.setNextStack(shallowStack) changed = True if changed: self.extrudersChanged.emit(machine_id) @@ -226,7 +229,11 @@ class ExtruderManager(QObject): container_registry.addContainer(user_profile) container_stack.addContainer(user_profile) - container_stack.setNextStack(UM.Application.getInstance().getGlobalContainerStack()) + # Make sure the next stack is a stack that contains only the machine definition + if not container_stack.getNextStack(): + shallowStack = UM.Settings.ContainerStack(machine_id + "_shallow") + shallowStack.addContainer(machine_definition) + container_stack.setNextStack(shallowStack) container_registry.addContainer(container_stack) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index f1b093ba27..b20f9da843 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -204,6 +204,13 @@ class MachineManager(QObject): if self._active_container_stack.getProperty("extruder_nr", "value") == int(self._active_container_stack.getProperty(key, "global_inherits_stack")): self._global_container_stack.getTop().setProperty(key, "value", self._active_container_stack.getProperty(key, "value")) + # Global-only setting values should be set on all extruders at once + if not self._global_container_stack.getProperty(key, "settable_per_extruder"): + extruder_stacks = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()) + for extruder_stack in extruder_stacks: + if extruder_stacks != self._active_container_stack: + extruder_stack.getTop().setProperty(key, "value", self._active_container_stack.getProperty(key, "value")) + if property_name == "validationState": if self._global_stack_valid: changed_validation_state = self._active_container_stack.getProperty(key, property_name) From 7302baa7daf69ba0f96ad46da8f141dce7a4fb88 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 18 Jul 2016 13:20:37 +0200 Subject: [PATCH 028/102] Add icon to settings that can only be set globally (for all extruders) CURA-1758 --- .../PerObjectSettingsPanel.qml | 1 + resources/qml/Settings/SettingItem.qml | 23 +- resources/qml/Settings/SettingView.qml | 2 +- resources/themes/cura/icons/link.svg | 43 + resources/themes/cura/icons/reset.svg | 10365 ---------------- 5 files changed, 67 insertions(+), 10367 deletions(-) create mode 100644 resources/themes/cura/icons/link.svg diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 546b7086e6..54783f02b0 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -181,6 +181,7 @@ Item { onLoaded: { settingLoader.item.showRevertButton = false settingLoader.item.showInheritButton = false + settingLoader.item.showLinkedSettingIcon = false settingLoader.item.doDepthIndentation = false } diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index a7bdabb3c5..1609e30080 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -21,10 +21,12 @@ Item { property var showRevertButton: true property var showInheritButton: true + property var showLinkedSettingIcon: true property var doDepthIndentation: true // Create properties to put property provider stuff in (bindings break in qt 5.5.1 otherwise) property var state: propertyProvider.properties.state + property var settablePerExtruder: propertyProvider.properties.settable_per_extruder property var stackLevels: propertyProvider.stackLevels property var stackLevel: stackLevels[0] @@ -131,6 +133,26 @@ Item { verticalCenter: parent.verticalCenter } + UM.SimpleButton + { + id: linkedSettingIcon; + + visible: base.settablePerExtruder != "True" && base.showLinkedSettingIcon + + height: parent.height; + width: height; + + backgroundColor: UM.Theme.getColor("setting_control"); + hoverBackgroundColor: UM.Theme.getColor("setting_control") + color: UM.Theme.getColor("setting_control_button") + hoverColor: UM.Theme.getColor("setting_control_button") + + iconSource: UM.Theme.getIcon("link") + + onEntered: { hoverTimer.stop(); base.showTooltip(catalog.i18nc("@label", "This setting is always shared between all extruders. Changing it here will change the value for all extruders")) } + onExited: base.showTooltip(base.tooltipText); + } + UM.SimpleButton { id: revertButton; @@ -231,7 +253,6 @@ Item { onEntered: { hoverTimer.stop(); base.showTooltip(catalog.i18nc("@label", "This setting is normally calculated, but it currently has an absolute value set.\n\nClick to restore the calculated value.")) } onExited: base.showTooltip(base.tooltipText); } - } Item diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index faa7bf94cb..a2aa5be197 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -95,7 +95,7 @@ ScrollView containerStackId: ExtruderManager.activeExtruderStackId ? ExtruderManager.activeExtruderStackId : Cura.MachineManager.activeMachineId key: model.key ? model.key : "" - watchedProperties: [ "value", "enabled", "state", "validationState" ] + watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder" ] storeIndex: 0 } diff --git a/resources/themes/cura/icons/link.svg b/resources/themes/cura/icons/link.svg new file mode 100644 index 0000000000..7cc2778846 --- /dev/null +++ b/resources/themes/cura/icons/link.svg @@ -0,0 +1,43 @@ + +image/svg+xml \ No newline at end of file diff --git a/resources/themes/cura/icons/reset.svg b/resources/themes/cura/icons/reset.svg index fae303d2a1..4772d446d7 100644 --- a/resources/themes/cura/icons/reset.svg +++ b/resources/themes/cura/icons/reset.svg @@ -27,10369 +27,4 @@ c-1.128,0.65-2.448,0.967-3.679,1.439C-0.113,107.552-0.113,69.744-0.113,31.935z"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 3923d435672e3459c3bfd7d1befa1b305f33e0dd Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 18 Jul 2016 14:58:48 +0200 Subject: [PATCH 029/102] Disable simple mode controls on global tab CURA-1758 --- resources/qml/SidebarSimple.qml | 46 +++++++++++++++++++++++++++----- resources/themes/cura/theme.json | 2 +- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 04b93e7d2a..2b7ed7e3cd 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -19,6 +19,7 @@ Item property Action configureSettings; property variant minimumPrintTime: PrintInformation.minimumPrintTime; property variant maximumPrintTime: PrintInformation.maximumPrintTime; + property bool settingsEnabled: ExtruderManager.activeExtruderStackId || ExtruderManager.extruderCount == 0 Component.onCompleted: PrintInformation.enabled = true Component.onDestruction: PrintInformation.enabled = false @@ -81,7 +82,11 @@ Item height: width border.color: { - if(infillListView.activeIndex == index) + if(!base.settingsEnabled) + { + return UM.Theme.getColor("setting_control_disabled_border") + } + else if(infillListView.activeIndex == index) { return UM.Theme.getColor("setting_control_selected") } @@ -92,7 +97,17 @@ Item return UM.Theme.getColor("setting_control_border") } border.width: UM.Theme.getSize("default_lining").width - color: infillListView.activeIndex == index ? UM.Theme.getColor("setting_control_selected") : "transparent" + color: { + if(infillListView.activeIndex == index) + { + if(!base.settingsEnabled) + { + return UM.Theme.getColor("setting_control_disabled_text") + } + return UM.Theme.getColor("setting_control_selected") + } + return "transparent" + } UM.RecolorImage { id: infillIcon @@ -102,13 +117,24 @@ Item sourceSize.width: width sourceSize.height: width source: UM.Theme.getIcon(model.icon); - color: (infillListView.activeIndex == index) ? UM.Theme.getColor("text_white") : UM.Theme.getColor("text") + color: { + if(infillListView.activeIndex == index) + { + return UM.Theme.getColor("text_reversed") + } + if(!base.settingsEnabled) + { + return UM.Theme.getColor("setting_control_disabled_text") + } + return UM.Theme.getColor("text") + } } MouseArea { id: infillMouseArea anchors.fill: parent hoverEnabled: true + enabled: base.settingsEnabled onClicked: { if (infillListView.activeIndex != index) { @@ -206,13 +232,15 @@ Item //: Setting enable skirt adhesion checkbox text: catalog.i18nc("@option:check", "Print Brim"); style: UM.Theme.styles.checkbox; + enabled: base.settingsEnabled - checked: platformAdhesionType.properties.value == "brim" + checked: {print(platformAdhesionType.properties.value); return platformAdhesionType.properties.value == "brim"} MouseArea { id: brimMouseArea anchors.fill: parent hoverEnabled: true + enabled: base.settingsEnabled onClicked: { platformAdhesionType.setPropertyValue("value", !parent.checked ? "brim" : "skirt") @@ -254,12 +282,14 @@ Item //: Setting enable support checkbox text: catalog.i18nc("@option:check", "Print Support Structure"); style: UM.Theme.styles.checkbox; + enabled: base.settingsEnabled checked: supportEnabled.properties.value == "True" MouseArea { id: supportMouseArea anchors.fill: parent hoverEnabled: true + enabled: base.settingsEnabled onClicked: { supportEnabled.setPropertyValue("value", !parent.checked) @@ -288,6 +318,7 @@ Item width: parent.width / 100 * 45 style: UM.Theme.styles.combobox + enabled: base.settingsEnabled property alias _hovered: supportExtruderMouseArea.containsMouse currentIndex: supportEnabled.properties.value == "True" ? parseFloat(supportExtruderNr.properties.value) + 1 : 0 @@ -303,6 +334,7 @@ Item id: supportExtruderMouseArea anchors.fill: parent hoverEnabled: true + enabled: base.settingsEnabled acceptedButtons: Qt.NoButton onEntered: { @@ -382,7 +414,7 @@ Item { id: platformAdhesionType - containerStackId: Cura.MachineManager.activeMachineId + containerStackId: Cura.MachineManager.activeStackId key: "adhesion_type" watchedProperties: [ "value" ] storeIndex: 0 @@ -392,7 +424,7 @@ Item { id: supportEnabled - containerStackId: Cura.MachineManager.activeMachineId + containerStackId: Cura.MachineManager.activeStackId key: "support_enable" watchedProperties: [ "value" ] storeIndex: 0 @@ -412,7 +444,7 @@ Item { id: supportExtruderNr - containerStackId: Cura.MachineManager.activeMachineId + containerStackId: Cura.MachineManager.activeStackId key: "support_extruder_nr" watchedProperties: [ "value" ] storeIndex: 0 diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index 053e5e3d84..69fc2c2c71 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -56,7 +56,7 @@ "text_inactive": [174, 174, 174, 255], "text_hover": [70, 84, 113, 255], "text_pressed": [12, 169, 227, 255], - "text_white": [255, 255, 255, 255], + "text_reversed": [255, 255, 255, 255], "text_subtext": [70, 84, 113, 255], "error": [255, 140, 0, 255], From 64c870a3cf841822f9fe7eb3be394684b51e2614 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 18 Jul 2016 15:03:55 +0200 Subject: [PATCH 030/102] Disable setting support_enable per extruder This prevents a lot of confusion with setting the support_extruder_nr to an extruder which then has support_enable set to False (resulting in no support). It is still possible to print some objects with no support using the Per Object Settings tool. CURA-1758 --- resources/definitions/fdmprinter.def.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index e6f7766600..a4a3e93f44 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -2133,7 +2133,8 @@ "type": "bool", "default_value": false, "global_inherits_stack": "support_extruder_nr", - "settable_per_mesh": true + "settable_per_mesh": true, + "settable_per_extruder": false }, "support_type": { From efa2099912c72830fb5a7c4899aa78a095b29f7c Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 18 Jul 2016 15:07:48 +0200 Subject: [PATCH 031/102] Remove stray debug code CURA-1758 --- resources/qml/SidebarSimple.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 2b7ed7e3cd..0e1b04cdeb 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -234,7 +234,7 @@ Item style: UM.Theme.styles.checkbox; enabled: base.settingsEnabled - checked: {print(platformAdhesionType.properties.value); return platformAdhesionType.properties.value == "brim"} + checked: platformAdhesionType.properties.value == "brim" MouseArea { id: brimMouseArea From a48b3b73983524149d2a099ae6f2dd0a4a810874 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 18 Jul 2016 15:15:10 +0200 Subject: [PATCH 032/102] Select first extruder when switching to a printer The days of the global tab are numbered CURA-1758 --- cura/Settings/ExtruderManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 78468acbcb..bfbf8247f8 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -22,7 +22,7 @@ class ExtruderManager(QObject): def __init__(self, parent = None): super().__init__(parent) self._extruder_trains = { } #Per machine, a dictionary of extruder container stack IDs. - self._active_extruder_index = -1 + self._active_extruder_index = 0 UM.Application.getInstance().globalContainerStackChanged.connect(self.__globalContainerStackChanged) self._addCurrentMachineExtruders() From 8a6f41d7e784303773903e6cd2eb439081de4ef3 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 18 Jul 2016 15:50:01 +0200 Subject: [PATCH 033/102] Initialise multiextrusion global stacks to empty profiles CURA-1758 --- cura/Settings/MachineManager.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index b20f9da843..58cc722d5e 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -206,10 +206,11 @@ class MachineManager(QObject): # Global-only setting values should be set on all extruders at once if not self._global_container_stack.getProperty(key, "settable_per_extruder"): + new_value = self._active_container_stack.getProperty(key, "value") extruder_stacks = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()) for extruder_stack in extruder_stacks: - if extruder_stacks != self._active_container_stack: - extruder_stack.getTop().setProperty(key, "value", self._active_container_stack.getProperty(key, "value")) + if extruder_stack != self._active_container_stack: + extruder_stack.getTop().setProperty(key, "value", new_value) if property_name == "validationState": if self._global_stack_valid: @@ -290,9 +291,15 @@ class MachineManager(QObject): new_global_stack.addMetaDataEntry("type", "machine") UM.Settings.ContainerRegistry.getInstance().addContainer(new_global_stack) - variant_instance_container = self._updateVariantContainer(definition) - material_instance_container = self._updateMaterialContainer(definition, variant_instance_container) - quality_instance_container = self._updateQualityContainer(definition, material_instance_container) + if definition.getProperty("machine_extruder_count", "value") == 1: + variant_instance_container = self._updateVariantContainer(definition) + material_instance_container = self._updateMaterialContainer(definition, variant_instance_container) + quality_instance_container = self._updateQualityContainer(definition, material_instance_container) + else: + # Initialise multiextrusion global stacks to empty profiles; all settings go in the user profile + variant_instance_container = self._empty_variant_container + material_instance_container = self._empty_material_container + quality_instance_container = self._empty_quality_container current_settings_instance_container = UM.Settings.InstanceContainer(name + "_current_settings") current_settings_instance_container.addMetaDataEntry("machine", name) From a31ffc4ebf277965089b50a31d62cf9b1f6b739b Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 18 Jul 2016 16:19:37 +0200 Subject: [PATCH 034/102] Select first extruder when switching to a printer (continued) The days of the global tab are numbered CURA-1758 --- resources/qml/SidebarHeader.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index e6c3e8a86c..ba7c94b079 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -91,8 +91,8 @@ Column target: Cura.MachineManager onGlobalContainerChanged: { - base.currentExtruderIndex = -1; - forceActiveFocus() + forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. + base.currentExtruderIndex = (machineExtruderCount.properties.value == 1) ? -1 : 0; ExtruderManager.setActiveExtruderIndex(base.currentExtruderIndex); } } @@ -110,7 +110,7 @@ Column onClicked: { - forceActiveFocus() //Changing focus applies the currently-being-typed values so it can change the displayed setting values. + forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. base.currentExtruderIndex = index; ExtruderManager.setActiveExtruderIndex(index); } From 5d613bd22e1e237ab5ce00110f85c88b1756fc3e Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 18 Jul 2016 18:46:54 +0200 Subject: [PATCH 035/102] Make sure _active_container_stack is properly initiated CURA-1758 --- cura/Settings/MachineManager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 58cc722d5e..7abd5b102b 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -58,7 +58,9 @@ class MachineManager(QObject): if active_machine_id != "": # An active machine was saved, so restore it. self.setActiveMachine(active_machine_id) - pass + if self._global_container_stack.getProperty("machine_extruder_count", "value") > 1: + # Make sure _active_container_stack is properly initiated + ExtruderManager.getInstance().setActiveExtruderIndex(0) self._auto_change_material_hotend_flood_window = 10 # The minimum number of seconds between asking if the material or hotend on the machine should be used self._auto_change_material_hotend_flood_time = 0 # The last timestamp (in seconds) when the user was asked about changing the material or hotend to whatis loaded on the machine From 16315b6e1d5807a513cabb61fe753f2b27d27a80 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 19 Jul 2016 08:50:03 +0200 Subject: [PATCH 036/102] Fix reset value icon on non-per-extruder-settable settings CURA-1758 --- cura/Settings/MachineManager.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 7abd5b102b..644c7d7843 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -206,13 +206,22 @@ class MachineManager(QObject): if self._active_container_stack.getProperty("extruder_nr", "value") == int(self._active_container_stack.getProperty(key, "global_inherits_stack")): self._global_container_stack.getTop().setProperty(key, "value", self._active_container_stack.getProperty(key, "value")) - # Global-only setting values should be set on all extruders at once + # Global-only setting values should be set on all extruders and the global stack if not self._global_container_stack.getProperty(key, "settable_per_extruder"): new_value = self._active_container_stack.getProperty(key, "value") - extruder_stacks = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()) - for extruder_stack in extruder_stacks: + active_stack_has_user_value = self._active_container_stack.getTop().getInstance(key) != None + + for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()): if extruder_stack != self._active_container_stack: - extruder_stack.getTop().setProperty(key, "value", new_value) + if active_stack_has_user_value: + extruder_stack.getTop().setProperty(key, "value", new_value) + else: + # Remove from the value from the other stacks as well, unless the + # resulting value from the other stacklevels is different + extruder_stack.getTop().removeInstance(key) + if extruder_stack.getProperty(key, "value") != new_value: + extruder_stack.getTop().setProperty(key, "value", new_value) + self._global_container_stack.getTop().setProperty(key, "value", new_value) if property_name == "validationState": if self._global_stack_valid: From 81f9467ca0d14efc9e86c79f145c0d587be7ce99 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 19 Jul 2016 16:28:24 +0200 Subject: [PATCH 037/102] Update the global value when the "global_inherits_stack" value changes CURA-1758 --- cura/Settings/MachineManager.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 644c7d7843..d97748dfb3 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -223,6 +223,17 @@ class MachineManager(QObject): extruder_stack.getTop().setProperty(key, "value", new_value) self._global_container_stack.getTop().setProperty(key, "value", new_value) + if property_name == "global_inherits_stack": + if self._active_container_stack and self._active_container_stack != self._global_container_stack: + # Update the global user value when the "global_inherits_stack" function points to a different stack + stack_index = int(self._active_container_stack.getProperty(key, property_name)) + extruder_stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())] + + if len(extruder_stacks) > stack_index: + new_value = extruder_stacks[stack_index].getProperty(key, "value") + if self._global_container_stack.getProperty(key, "value") != new_value: + self._global_container_stack.getTop().setProperty(key, "value", new_value) + if property_name == "validationState": if self._global_stack_valid: changed_validation_state = self._active_container_stack.getProperty(key, property_name) From 2e75430d0ed804ab64ba2890ca373e652cc3e184 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 21 Jul 2016 08:18:49 +0200 Subject: [PATCH 038/102] Update the global container when switching variant/material/quality profiles CURA-1758 --- cura/Settings/MachineManager.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index d97748dfb3..8d907ad8af 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -221,7 +221,8 @@ class MachineManager(QObject): extruder_stack.getTop().removeInstance(key) if extruder_stack.getProperty(key, "value") != new_value: extruder_stack.getTop().setProperty(key, "value", new_value) - self._global_container_stack.getTop().setProperty(key, "value", new_value) + if self._global_container_stack.getProperty(key, "value") != new_value: + self._global_container_stack.getTop().setProperty(key, "value", new_value) if property_name == "global_inherits_stack": if self._active_container_stack and self._active_container_stack != self._global_container_stack: @@ -290,6 +291,31 @@ class MachineManager(QObject): def _onInstanceContainersChanged(self, container): container_type = container.getMetaDataEntry("type") + + if self._active_container_stack and self._active_container_stack != self._global_container_stack: + global_container = self._global_container_stack.findContainer({"type": container_type}) + if global_container: + # rebuild the global equivalent of the changed container + global_container.clear() + + # get the keys from the containers of this type from all stacks + stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())] + keys = [] + for extruder_stack in stacks: + if extruder_stack == self._active_container_stack: + extruder_container = container + else: + extruder_container = extruder_stack.findContainer({"type": container_type}) + if extruder_container: + keys += extruder_container.getAllKeys() + keys = list(set(keys)) + + # set the value of the global container to the value of the inherit stack - if any + for key in keys: + inherit_stack_index = int(self._active_container_stack.getProperty(key, "global_inherits_stack")) + if stacks[inherit_stack_index].hasProperty(key, "value"): + global_container.setProperty(key, "value", stacks[inherit_stack_index].getProperty(key, "value")) + if container_type == "material": self.activeMaterialChanged.emit() elif container_type == "variant": From fa8b58844d6fd9146b1fa9060bb451ce4c51d7d3 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 21 Jul 2016 09:42:33 +0200 Subject: [PATCH 039/102] Initialise multiextrusion global stacks to new empty profiles _EmptyInstanceContainer does not allow setting properties and does not emit signals CURA-1758 --- cura/Settings/MachineManager.py | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 8d907ad8af..6820cbcd2c 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -331,29 +331,48 @@ class MachineManager(QObject): @pyqtSlot(str, str) def addMachine(self, name, definition_id): - definitions = UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = definition_id) + container_registry = UM.Settings.ContainerRegistry.getInstance() + definitions = container_registry.findDefinitionContainers(id = definition_id) if definitions: definition = definitions[0] name = self._createUniqueName("machine", "", name, definition.getName()) new_global_stack = UM.Settings.ContainerStack(name) new_global_stack.addMetaDataEntry("type", "machine") - UM.Settings.ContainerRegistry.getInstance().addContainer(new_global_stack) + container_registry.addContainer(new_global_stack) if definition.getProperty("machine_extruder_count", "value") == 1: variant_instance_container = self._updateVariantContainer(definition) material_instance_container = self._updateMaterialContainer(definition, variant_instance_container) quality_instance_container = self._updateQualityContainer(definition, material_instance_container) else: - # Initialise multiextrusion global stacks to empty profiles; all settings go in the user profile - variant_instance_container = self._empty_variant_container - material_instance_container = self._empty_material_container - quality_instance_container = self._empty_quality_container + # Initialise multiextrusion global stacks to new empty profiles + # These will mirror values from the extruder stacks + variant_instance_container = UM.Settings.InstanceContainer(name + "_global_variant") + variant_instance_container.addMetaDataEntry("machine", name) + variant_instance_container.addMetaDataEntry("type", "variant") + variant_instance_container.setDefinition(definitions[0]) + variant_instance_container.setName("global") + container_registry.addContainer(variant_instance_container) + + material_instance_container = UM.Settings.InstanceContainer(name + "_global_material") + material_instance_container.addMetaDataEntry("machine", name) + material_instance_container.addMetaDataEntry("type", "material") + material_instance_container.setDefinition(definitions[0]) + material_instance_container.setName("global") + container_registry.addContainer(material_instance_container) + + quality_instance_container = UM.Settings.InstanceContainer(name + "_global_quality") + quality_instance_container.addMetaDataEntry("machine", name) + quality_instance_container.addMetaDataEntry("type", "quality") + quality_instance_container.setDefinition(definitions[0]) + quality_instance_container.setName("global") + container_registry.addContainer(quality_instance_container) current_settings_instance_container = UM.Settings.InstanceContainer(name + "_current_settings") current_settings_instance_container.addMetaDataEntry("machine", name) current_settings_instance_container.addMetaDataEntry("type", "user") current_settings_instance_container.setDefinition(definitions[0]) - UM.Settings.ContainerRegistry.getInstance().addContainer(current_settings_instance_container) + container_registry.addContainer(current_settings_instance_container) # If a definition is found, its a list. Should only have one item. new_global_stack.addContainer(definition) From 89b601791d106780c7655371a34d1ef70a5c53cc Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 21 Jul 2016 09:55:08 +0200 Subject: [PATCH 040/102] Fix possible error when a printer fails to load CURA-1758 --- cura/Settings/MachineManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 6820cbcd2c..9fe60f3ef1 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -58,7 +58,7 @@ class MachineManager(QObject): if active_machine_id != "": # An active machine was saved, so restore it. self.setActiveMachine(active_machine_id) - if self._global_container_stack.getProperty("machine_extruder_count", "value") > 1: + if self._global_container_stack and self._global_container_stack.getProperty("machine_extruder_count", "value") > 1: # Make sure _active_container_stack is properly initiated ExtruderManager.getInstance().setActiveExtruderIndex(0) From 0857017ac6730d26d8aac5a5f49f175f6e243c53 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 21 Jul 2016 11:21:35 +0200 Subject: [PATCH 041/102] Add a context-menu item to copy a value to all extruders. CURA-1758 --- cura/Settings/MachineManager.py | 13 ++++++++++ resources/qml/Settings/SettingView.qml | 36 +++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 9fe60f3ef1..50d390dabc 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -505,6 +505,19 @@ class MachineManager(QObject): return True return containers[0].isReadOnly() + ## Copy the value of the setting of the current extruder to all other extruders as well as the global container. + @pyqtSlot(str) + def copyValueToExtruders(self, key): + if not self._active_container_stack or self._global_container_stack.getProperty("machine_extruder_count", "value") <= 1: + return + + new_value = self._active_container_stack.getProperty(key, "value") + stacks = [stack for stack in self._extruder_manager.getMachineExtruders(self._global_container_stack.getId())] + stacks.append(self._global_container_stack) + for extruder_stack in stacks: + if extruder_stack != self._active_container_stack and extruder_stack.getProperty(key, "value") != new_value: + extruder_stack.getTop().setProperty(key, "value", new_value) + @pyqtSlot(result = str) def newQualityContainerFromQualityAndUser(self): new_container_id = self.duplicateContainer(self.activeQualityId) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index a2aa5be197..7551ec2345 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -102,7 +102,12 @@ ScrollView Connections { target: item - onContextMenuRequested: { contextMenu.key = model.key; contextMenu.popup() } + onContextMenuRequested: + { + contextMenu.key = model.key; + contextMenu.provider = provider + contextMenu.popup(); + } onShowTooltip: base.showTooltip(delegate, { x: 0, y: delegate.height / 2 }, text) onHideTooltip: base.hideTooltip() } @@ -134,9 +139,24 @@ ScrollView Menu { - id: contextMenu; + id: contextMenu - property string key; + property string key + property var provider + + MenuItem + { + //: Settings context menu action + text: catalog.i18nc("@action:menu", "Copy value to all extruders") + visible: machineExtruderCount.properties.value > 1 + enabled: contextMenu.provider.properties.settable_per_extruder != "False" + onTriggered: Cura.MachineManager.copyValueToExtruders(contextMenu.key) + } + + MenuSeparator + { + visible: machineExtruderCount.properties.value > 1 + } MenuItem { @@ -152,5 +172,15 @@ ScrollView onTriggered: Cura.Actions.configureSettingVisibility.trigger(contextMenu); } } + + UM.SettingPropertyProvider + { + id: machineExtruderCount + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_extruder_count" + watchedProperties: [ "value" ] + storeIndex: 0 + } } } From baf9d81e471e266dc2ea27c8d3c9da507c66f2a7 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 21 Jul 2016 14:09:49 +0200 Subject: [PATCH 042/102] Massively simplify the strategy This removes the *_global_variant, *_global_material and *_global_quality profiles and instead uses the same profiles for the global stack as for extrude 0. CURA-1758 --- cura/Settings/MachineManager.py | 76 +++++++++------------------------ 1 file changed, 21 insertions(+), 55 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 50d390dabc..b444be27a5 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -202,9 +202,10 @@ class MachineManager(QObject): self.globalValueChanged.emit() if self._active_container_stack and self._active_container_stack != self._global_container_stack: - # Make the global stack value mirror the stack value appropriate for this setting + # Make the global current settings mirror the stack values appropriate for this setting if self._active_container_stack.getProperty("extruder_nr", "value") == int(self._active_container_stack.getProperty(key, "global_inherits_stack")): - self._global_container_stack.getTop().setProperty(key, "value", self._active_container_stack.getProperty(key, "value")) + new_value = self._active_container_stack.getProperty(key, "value") + self._global_container_stack.getTop().setProperty(key, "value", new_value) # Global-only setting values should be set on all extruders and the global stack if not self._global_container_stack.getProperty(key, "settable_per_extruder"): @@ -217,10 +218,17 @@ class MachineManager(QObject): extruder_stack.getTop().setProperty(key, "value", new_value) else: # Remove from the value from the other stacks as well, unless the - # resulting value from the other stacklevels is different - extruder_stack.getTop().removeInstance(key) - if extruder_stack.getProperty(key, "value") != new_value: - extruder_stack.getTop().setProperty(key, "value", new_value) + # top value from the other stacklevels is different than the new value + for container in extruder_stack.getContainers(): + if container == extruder_stack.getTop(): + continue + if container.__class__ == UM.Settings.InstanceContainer and container.getInstance(key) != None: + if container.getProperty(key, "value") != new_value: + extruder_stack.getTop().setProperty(key, "value", new_value) + else: + extruder_stack.getTop().removeInstance(key) + break + if self._global_container_stack.getProperty(key, "value") != new_value: self._global_container_stack.getTop().setProperty(key, "value", new_value) @@ -292,29 +300,11 @@ class MachineManager(QObject): def _onInstanceContainersChanged(self, container): container_type = container.getMetaDataEntry("type") - if self._active_container_stack and self._active_container_stack != self._global_container_stack: + if self._active_container_stack and int(self._active_container_stack.getProperty("extruder_nr", "value")) == 0: global_container = self._global_container_stack.findContainer({"type": container_type}) - if global_container: - # rebuild the global equivalent of the changed container - global_container.clear() - - # get the keys from the containers of this type from all stacks - stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())] - keys = [] - for extruder_stack in stacks: - if extruder_stack == self._active_container_stack: - extruder_container = container - else: - extruder_container = extruder_stack.findContainer({"type": container_type}) - if extruder_container: - keys += extruder_container.getAllKeys() - keys = list(set(keys)) - - # set the value of the global container to the value of the inherit stack - if any - for key in keys: - inherit_stack_index = int(self._active_container_stack.getProperty(key, "global_inherits_stack")) - if stacks[inherit_stack_index].hasProperty(key, "value"): - global_container.setProperty(key, "value", stacks[inherit_stack_index].getProperty(key, "value")) + if global_container and global_container != container: + container_index = self._global_container_stack.getContainerIndex(global_container) + self._global_container_stack.replaceContainer(container_index, container) if container_type == "material": self.activeMaterialChanged.emit() @@ -340,33 +330,9 @@ class MachineManager(QObject): new_global_stack.addMetaDataEntry("type", "machine") container_registry.addContainer(new_global_stack) - if definition.getProperty("machine_extruder_count", "value") == 1: - variant_instance_container = self._updateVariantContainer(definition) - material_instance_container = self._updateMaterialContainer(definition, variant_instance_container) - quality_instance_container = self._updateQualityContainer(definition, material_instance_container) - else: - # Initialise multiextrusion global stacks to new empty profiles - # These will mirror values from the extruder stacks - variant_instance_container = UM.Settings.InstanceContainer(name + "_global_variant") - variant_instance_container.addMetaDataEntry("machine", name) - variant_instance_container.addMetaDataEntry("type", "variant") - variant_instance_container.setDefinition(definitions[0]) - variant_instance_container.setName("global") - container_registry.addContainer(variant_instance_container) - - material_instance_container = UM.Settings.InstanceContainer(name + "_global_material") - material_instance_container.addMetaDataEntry("machine", name) - material_instance_container.addMetaDataEntry("type", "material") - material_instance_container.setDefinition(definitions[0]) - material_instance_container.setName("global") - container_registry.addContainer(material_instance_container) - - quality_instance_container = UM.Settings.InstanceContainer(name + "_global_quality") - quality_instance_container.addMetaDataEntry("machine", name) - quality_instance_container.addMetaDataEntry("type", "quality") - quality_instance_container.setDefinition(definitions[0]) - quality_instance_container.setName("global") - container_registry.addContainer(quality_instance_container) + variant_instance_container = self._updateVariantContainer(definition) + material_instance_container = self._updateMaterialContainer(definition, variant_instance_container) + quality_instance_container = self._updateQualityContainer(definition, material_instance_container) current_settings_instance_container = UM.Settings.InstanceContainer(name + "_current_settings") current_settings_instance_container.addMetaDataEntry("machine", name) From a64e0fa8e8f6fe0e433b059be17e2c17bce0b635 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 21 Jul 2016 14:13:35 +0200 Subject: [PATCH 043/102] Fix copyValueToExtruders (rebase-accident) CURA-1758 --- cura/Settings/MachineManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index b444be27a5..3d59b7b1c7 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -478,7 +478,7 @@ class MachineManager(QObject): return new_value = self._active_container_stack.getProperty(key, "value") - stacks = [stack for stack in self._extruder_manager.getMachineExtruders(self._global_container_stack.getId())] + stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())] stacks.append(self._global_container_stack) for extruder_stack in stacks: if extruder_stack != self._active_container_stack and extruder_stack.getProperty(key, "value") != new_value: From caa202e3d62a27438160a90834d93b174939480f Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 21 Jul 2016 15:28:10 +0200 Subject: [PATCH 044/102] Make sure values from loaded profile permeate to other stacks CURA-1758 --- cura/Settings/MachineManager.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 3d59b7b1c7..73b350d75f 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -300,11 +300,16 @@ class MachineManager(QObject): def _onInstanceContainersChanged(self, container): container_type = container.getMetaDataEntry("type") - if self._active_container_stack and int(self._active_container_stack.getProperty("extruder_nr", "value")) == 0: - global_container = self._global_container_stack.findContainer({"type": container_type}) - if global_container and global_container != container: - container_index = self._global_container_stack.getContainerIndex(global_container) - self._global_container_stack.replaceContainer(container_index, container) + if self._active_container_stack and self._active_container_stack != self._global_container_stack: + if int(self._active_container_stack.getProperty("extruder_nr", "value")) == 0: + global_container = self._global_container_stack.findContainer({"type": container_type}) + if global_container and global_container != container: + container_index = self._global_container_stack.getContainerIndex(global_container) + self._global_container_stack.replaceContainer(container_index, container) + + for key in container.getAllKeys(): + # Make sure the values in this profile are distributed to other stacks if necessary + self._onGlobalPropertyChanged(key, "value") if container_type == "material": self.activeMaterialChanged.emit() From d4744735c6fd5352a966dc176ba3988050d72671 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 21 Jul 2016 15:33:35 +0200 Subject: [PATCH 045/102] Remove global inheritance for most settings CURA-1758 --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 5ba22002bb..a6d0a3b827 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -97,7 +97,7 @@ class CuraApplication(QtApplication): SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True) - SettingDefinition.addSupportedProperty("global_inherits_stack", DefinitionPropertyType.Function, default = "0") + SettingDefinition.addSupportedProperty("global_inherits_stack", DefinitionPropertyType.Function, default = "-1") SettingDefinition.addSettingType("extruder", int, str, Validator) ## Add the 4 types of profiles to storage. From dd78561e25cfb33fcbb2e196c96bedd84c8cc62e Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 21 Jul 2016 15:35:20 +0200 Subject: [PATCH 046/102] Remove global tab CURA-1758 --- resources/qml/SidebarHeader.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index ba7c94b079..1f7fa00b54 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -84,7 +84,7 @@ Column orientation: ListView.Horizontal - model: Cura.ExtrudersModel { id: extrudersModel; addGlobal: true } + model: Cura.ExtrudersModel { id: extrudersModel; addGlobal: false } Connections { From 4346d5a233f5cecb49ed682feaec38d405e059d1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 21 Jul 2016 20:54:02 +0200 Subject: [PATCH 047/102] Settings now point to the right extruder stack CURA-1758 --- cura/Settings/ExtruderManager.py | 4 +++ resources/qml/Settings/SettingView.qml | 38 +++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index bfbf8247f8..5afa8f0346 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -47,6 +47,10 @@ class ExtruderManager(QObject): return 0 # No active machine, so no extruders. return len(self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()]) + @pyqtProperty("QVariantList", notify=extrudersChanged) + def extruderIds(self): + return [self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()][extruder].getId() for extruder in self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()]] + ## The instance of the singleton pattern. # # It's None if the extruder manager hasn't been created yet. diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 7551ec2345..9f7a74be1d 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -60,6 +60,8 @@ ScrollView property var settingDefinitionsModel: definitionsModel property var propertyProvider: provider + property var stackId: ExtruderManager.activeExtruderStackId ? ExtruderManager.activeExtruderStackId : Cura.MachineManager.activeMachineId + //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989 //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes, //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely. @@ -89,11 +91,45 @@ ScrollView } } + // Binding to ensure that the right containerstack ID is set for the provider. + // This ensures that if a setting has a global_inherits_stack id (for instance; Support speed points to the + // extruder that actually prints the support, as that is the setting we need to use to calculate the value) + Binding + { + target: provider + property: "containerStackId" + value: + { + if(inheritStackProvider.properties.global_inherits_stack == -1 || inheritStackProvider.properties.global_inherits_stack == null) + { + if( ExtruderManager.activeExtruderStackId) + { + return ExtruderManager.activeExtruderStackId + } + else + { + return Cura.MachineManager.activeMachineId + } + } + return ExtruderManager.extruderIds[inheritStackProvider.properties.global_inherits_stack] + } + } + + // Specialty provider that only watches global_inherits (we cant filter on what property changed we get events + // so we bypass that to make a dedicated provider. + UM.SettingPropertyProvider + { + id: inheritStackProvider + containerStackId: Cura.MachineManager.activeMachineId + key: model.key + watchedProperties: [ "global_inherits_stack"] + } + UM.SettingPropertyProvider { id: provider - containerStackId: ExtruderManager.activeExtruderStackId ? ExtruderManager.activeExtruderStackId : Cura.MachineManager.activeMachineId + containerStackId: delegate.stackId key: model.key ? model.key : "" watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder" ] storeIndex: 0 From 568d30d17b85dc1fd5bd4ab784e750ac237cdb39 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 21 Jul 2016 21:19:25 +0200 Subject: [PATCH 048/102] Commented out 2 lines of code that caused issues with reset The documentation doesn't mention why the code is there, so i've commented them out for the time beeing CURA-1758 --- cura/Settings/MachineManager.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 73b350d75f..7d3efc2560 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -199,6 +199,7 @@ class MachineManager(QObject): def _onGlobalPropertyChanged(self, key, property_name): if property_name == "value": + self.globalValueChanged.emit() if self._active_container_stack and self._active_container_stack != self._global_container_stack: @@ -211,7 +212,6 @@ class MachineManager(QObject): if not self._global_container_stack.getProperty(key, "settable_per_extruder"): new_value = self._active_container_stack.getProperty(key, "value") active_stack_has_user_value = self._active_container_stack.getTop().getInstance(key) != None - for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()): if extruder_stack != self._active_container_stack: if active_stack_has_user_value: @@ -220,15 +220,16 @@ class MachineManager(QObject): # Remove from the value from the other stacks as well, unless the # top value from the other stacklevels is different than the new value for container in extruder_stack.getContainers(): - if container == extruder_stack.getTop(): - continue + # Commented out this line for a bit. Don't know why it's here, but it is causing + # issues with reset button. + #if container == extruder_stack.getTop(): + # continue if container.__class__ == UM.Settings.InstanceContainer and container.getInstance(key) != None: if container.getProperty(key, "value") != new_value: extruder_stack.getTop().setProperty(key, "value", new_value) else: extruder_stack.getTop().removeInstance(key) break - if self._global_container_stack.getProperty(key, "value") != new_value: self._global_container_stack.getTop().setProperty(key, "value", new_value) From 87b9b17f98f5eac19fa00843f9ce5c7441643bf4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 21 Jul 2016 21:42:02 +0200 Subject: [PATCH 049/102] Now the right extruder is actually used (instead of randomly mixing it up) CURA-1758 --- cura/Settings/ExtruderManager.py | 7 +++++-- resources/qml/Settings/SettingView.qml | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 5afa8f0346..47359a5e97 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -47,9 +47,12 @@ class ExtruderManager(QObject): return 0 # No active machine, so no extruders. return len(self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()]) - @pyqtProperty("QVariantList", notify=extrudersChanged) + @pyqtProperty("QVariantMap", notify=extrudersChanged) def extruderIds(self): - return [self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()][extruder].getId() for extruder in self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()]] + map = {} + for position in self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()]: + map[position] = self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()][position].getId() + return map ## The instance of the singleton pattern. # diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 9f7a74be1d..cef5f86b33 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -111,7 +111,7 @@ ScrollView return Cura.MachineManager.activeMachineId } } - return ExtruderManager.extruderIds[inheritStackProvider.properties.global_inherits_stack] + return ExtruderManager.extruderIds[String(inheritStackProvider.properties.global_inherits_stack)] } } From 7e48b6ad4c732fc0edecedd87f2ce7c6c29690c3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 22 Jul 2016 09:03:14 +0200 Subject: [PATCH 050/102] Removed global_inherit_stack from support enable and support type CURA-1758 --- resources/definitions/fdmprinter.def.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index a4a3e93f44..e7e5ffb5d3 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -2132,7 +2132,6 @@ "description": "Enable support structures. These structures support parts of the model with severe overhangs.", "type": "bool", "default_value": false, - "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": true, "settable_per_extruder": false }, @@ -2147,7 +2146,6 @@ "everywhere": "Everywhere" }, "default_value": "everywhere", - "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, "settable_per_extruder": false From 8307dba643c01efef8b37f9ce14d83da6981a950 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 22 Jul 2016 09:33:44 +0200 Subject: [PATCH 051/102] Revert "Commented out 2 lines of code that caused issues with reset" This reverts commit 568d30d17b85dc1fd5bd4ab784e750ac237cdb39. Turned out this causes recursion issue when changing materials --- cura/Settings/MachineManager.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 7d3efc2560..73b350d75f 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -199,7 +199,6 @@ class MachineManager(QObject): def _onGlobalPropertyChanged(self, key, property_name): if property_name == "value": - self.globalValueChanged.emit() if self._active_container_stack and self._active_container_stack != self._global_container_stack: @@ -212,6 +211,7 @@ class MachineManager(QObject): if not self._global_container_stack.getProperty(key, "settable_per_extruder"): new_value = self._active_container_stack.getProperty(key, "value") active_stack_has_user_value = self._active_container_stack.getTop().getInstance(key) != None + for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()): if extruder_stack != self._active_container_stack: if active_stack_has_user_value: @@ -220,16 +220,15 @@ class MachineManager(QObject): # Remove from the value from the other stacks as well, unless the # top value from the other stacklevels is different than the new value for container in extruder_stack.getContainers(): - # Commented out this line for a bit. Don't know why it's here, but it is causing - # issues with reset button. - #if container == extruder_stack.getTop(): - # continue + if container == extruder_stack.getTop(): + continue if container.__class__ == UM.Settings.InstanceContainer and container.getInstance(key) != None: if container.getProperty(key, "value") != new_value: extruder_stack.getTop().setProperty(key, "value", new_value) else: extruder_stack.getTop().removeInstance(key) break + if self._global_container_stack.getProperty(key, "value") != new_value: self._global_container_stack.getTop().setProperty(key, "value", new_value) From 4c402e5bb75954fd33c1ff153eee53fd31841a01 Mon Sep 17 00:00:00 2001 From: Johan K Date: Fri, 22 Jul 2016 10:22:07 +0200 Subject: [PATCH 052/102] Removed the layer data empty guard in ProcessSlicedLayers as the problem was solved in the backend. --- plugins/CuraEngineBackend/ProcessSlicedLayersJob.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 9eb2f3e5cd..c2f73cf5b7 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -40,12 +40,6 @@ class ProcessSlicedLayersJob(Job): self._abort_requested = True def run(self): - # This is to prevent small models layer data to be cleared by extra invocation of engine - # Possibly adds an extra bug of layerdata not being removed if platform is cleared. - #TODO: remove need for this check - if len(self._layers) == 0: - return - start_time = time() if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView": self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1) From cafb46cb1dbd485dca449976309d9a309a062ec4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 22 Jul 2016 10:43:26 +0200 Subject: [PATCH 053/102] First container is now updated withouth recursion possible We now use a set of keys that we are handling in order to prevent the lock CURA-1758 --- cura/Settings/MachineManager.py | 10 +++++++--- resources/qml/Settings/SettingView.qml | 2 -- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 73b350d75f..58906be9d6 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -50,6 +50,8 @@ class MachineManager(QObject): Preferences.getInstance().addPreference("cura/active_machine", "") + self._global_event_keys = set() + active_machine_id = Preferences.getInstance().getValue("cura/active_machine") self._printer_output_devices = [] @@ -199,6 +201,10 @@ class MachineManager(QObject): def _onGlobalPropertyChanged(self, key, property_name): if property_name == "value": + ## We can get recursion issues. So we store a list of keys that we are still handling to prevent this. + if key in self._global_event_keys: + return + self._global_event_keys.add(key) self.globalValueChanged.emit() if self._active_container_stack and self._active_container_stack != self._global_container_stack: @@ -220,8 +226,6 @@ class MachineManager(QObject): # Remove from the value from the other stacks as well, unless the # top value from the other stacklevels is different than the new value for container in extruder_stack.getContainers(): - if container == extruder_stack.getTop(): - continue if container.__class__ == UM.Settings.InstanceContainer and container.getInstance(key) != None: if container.getProperty(key, "value") != new_value: extruder_stack.getTop().setProperty(key, "value", new_value) @@ -231,6 +235,7 @@ class MachineManager(QObject): if self._global_container_stack.getProperty(key, "value") != new_value: self._global_container_stack.getTop().setProperty(key, "value", new_value) + self._global_event_keys.remove(key) if property_name == "global_inherits_stack": if self._active_container_stack and self._active_container_stack != self._global_container_stack: @@ -254,7 +259,6 @@ class MachineManager(QObject): if not has_errors: self._global_stack_valid = True self.globalValidationChanged.emit() - def _onGlobalContainerChanged(self): if self._global_container_stack: self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index cef5f86b33..957a68b94c 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -60,8 +60,6 @@ ScrollView property var settingDefinitionsModel: definitionsModel property var propertyProvider: provider - property var stackId: ExtruderManager.activeExtruderStackId ? ExtruderManager.activeExtruderStackId : Cura.MachineManager.activeMachineId - //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989 //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes, //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely. From df46a61d38cf6f6f3da056006fd079b0b9459ced Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 22 Jul 2016 11:44:37 +0200 Subject: [PATCH 054/102] Active stack should no longer influence changing of targeted settings CURA-1758 --- cura/Settings/MachineManager.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 58906be9d6..4afcd325f1 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -210,17 +210,23 @@ class MachineManager(QObject): if self._active_container_stack and self._active_container_stack != self._global_container_stack: # Make the global current settings mirror the stack values appropriate for this setting if self._active_container_stack.getProperty("extruder_nr", "value") == int(self._active_container_stack.getProperty(key, "global_inherits_stack")): + new_value = self._active_container_stack.getProperty(key, "value") self._global_container_stack.getTop().setProperty(key, "value", new_value) # Global-only setting values should be set on all extruders and the global stack if not self._global_container_stack.getProperty(key, "settable_per_extruder"): - new_value = self._active_container_stack.getProperty(key, "value") - active_stack_has_user_value = self._active_container_stack.getTop().getInstance(key) != None - - for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()): - if extruder_stack != self._active_container_stack: - if active_stack_has_user_value: + extruder_stacks = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())) + target_stack_position = int(self._active_container_stack.getProperty(key, "global_inherits_stack")) + if target_stack_position == -1: # Prevent -1 from selecting wrong stack. + target_stack = self._active_container_stack + else: + target_stack = extruder_stacks[target_stack_position] + new_value = target_stack.getProperty(key, "value") + target_stack_has_user_value = target_stack.getTop().getInstance(key) != None + for extruder_stack in extruder_stacks: + if extruder_stack != target_stack: + if target_stack_has_user_value: extruder_stack.getTop().setProperty(key, "value", new_value) else: # Remove from the value from the other stacks as well, unless the @@ -232,7 +238,6 @@ class MachineManager(QObject): else: extruder_stack.getTop().removeInstance(key) break - if self._global_container_stack.getProperty(key, "value") != new_value: self._global_container_stack.getTop().setProperty(key, "value", new_value) self._global_event_keys.remove(key) From 83ec6e849ab040d23ab6e74e3ecb67065c9bebc5 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 22 Jul 2016 13:16:35 +0200 Subject: [PATCH 055/102] Multiple changes when quality profiles have different values should no longer break the link CURA-1758 --- cura/Settings/MachineManager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 4afcd325f1..79f0020603 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -236,7 +236,9 @@ class MachineManager(QObject): if container.getProperty(key, "value") != new_value: extruder_stack.getTop().setProperty(key, "value", new_value) else: - extruder_stack.getTop().removeInstance(key) + # Check if we really need to remove something. + if extruder_stack.getProperty(key, "value") != new_value: + extruder_stack.getTop().removeInstance(key) break if self._global_container_stack.getProperty(key, "value") != new_value: self._global_container_stack.getTop().setProperty(key, "value", new_value) From 1f53ce0d4d916a7e89fdbd568260d363edf47a01 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 22 Jul 2016 13:43:26 +0200 Subject: [PATCH 056/102] Fix weight calculation CURA-1038 --- cura/PrintInformation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 4dedf9db16..096170ba22 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -95,7 +95,7 @@ class PrintInformation(QObject): else: # Machine with no extruder stacks density = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("properties", {}).get("density", 0) - self._material_weights.append(float(amount) * float(density)) + self._material_weights.append(float(amount) * float(density) / 1000) self._material_lengths.append(round((amount / (math.pi * r ** 2)) / 1000, 2)) self.materialLengthsChanged.emit() self.materialWeightsChanged.emit() From 9171908c8bc578a393183b53b4e842adb013df85 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 22 Jul 2016 13:44:45 +0200 Subject: [PATCH 057/102] Show weights as well as lengths CURA-1038 --- resources/qml/JobSpecs.qml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 8e14241741..9064c4835f 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -195,15 +195,21 @@ Rectangle { color: UM.Theme.getColor("text_subtext") text: { - var amounts = []; + var lengths = []; + var weights = []; if(base.printMaterialLengths) { for(var index = 0; index < base.printMaterialLengths.length; index++) { - amounts.push(base.printMaterialLengths[index].toFixed(2)); + if(base.printMaterialLengths[index] > 0) { + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.floor(base.printMaterialWeights[index]))); + } } - } else { - amounts = ["0.00"]; } - return catalog.i18nc("@label", "%1 m").arg(amounts.join(" + ")); + if(lengths.length == 0) { + lengths = ["0.00"]; + weights = ["0"]; + } + return catalog.i18nc("@label", "%1 m / %2 g").arg(lengths.join(" + ")).arg(weights.join(" + ")); } } } From a2972e26f5e2711df6157af3615b464cc921fccc Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 22 Jul 2016 13:45:53 +0200 Subject: [PATCH 058/102] Remove the "from" convert_function for extruder settings Fixes CURA-1974 --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 82cc769172..a97522389a 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -97,7 +97,7 @@ class CuraApplication(QtApplication): SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True) - SettingDefinition.addSettingType("extruder", int, str, Validator) + SettingDefinition.addSettingType("extruder", None, str, Validator) ## Add the 4 types of profiles to storage. Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality") From fa61a9e3f0a0f6efb220fa7b843717350d482322 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 22 Jul 2016 14:04:35 +0200 Subject: [PATCH 059/102] Removed bunch of unneeded global inheritance stuff CURA-1758 --- resources/definitions/fdmprinter.def.json | 7 ------- 1 file changed, 7 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index e7e5ffb5d3..511be7780c 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -2177,7 +2177,6 @@ "zigzag": "Zig Zag" }, "default_value": "zigzag", - "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, "settable_per_extruder": false @@ -2188,7 +2187,6 @@ "description": "Connect the ZigZags. This will increase the strength of the zig zag support structure.", "type": "bool", "default_value": true, - "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable and (support_pattern == \"zigzag\")", "settable_per_mesh": false, "settable_per_extruder": false @@ -2202,7 +2200,6 @@ "minimum_value": "0", "maximum_value_warning": "100", "default_value": 15, - "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, "settable_per_extruder": false, @@ -2217,7 +2214,6 @@ "default_value": 2.66, "enabled": "support_enable", "value": "(support_line_width * 100) / support_infill_rate * (2 if support_pattern == \"grid\" else (3 if support_pattern == \"triangles\" else 1))", - "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, "settable_per_extruder": false } @@ -2388,7 +2384,6 @@ "unit": "%", "type": "float", "default_value": 100, - "global_inherits_stack": "support_extruder_nr", "minimum_value": "0", "maximum_value_warning": "100", "enabled":"support_roof_enable and support_enable", @@ -2405,7 +2400,6 @@ "default_value": 0.4, "minimum_value": "0", "value": "0 if support_roof_density == 0 else (support_roof_line_width * 100) / support_roof_density * (2 if support_roof_pattern == \"grid\" else (3 if support_roof_pattern == \"triangles\" else 1))", - "global_inherits_stack": "support_extruder_nr", "enabled": "support_roof_enable and support_enable", "settable_per_mesh": false, "settable_per_extruder": false @@ -2426,7 +2420,6 @@ "zigzag": "Zig Zag" }, "default_value": "concentric", - "global_inherits_stack": "support_extruder_nr", "enabled": "support_roof_enable and support_enable", "settable_per_mesh": false, "settable_per_extruder": false From 6cc6e79e65b522000df63ac359c7db730714b455 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 22 Jul 2016 14:24:52 +0200 Subject: [PATCH 060/102] Stripe objects that are outside the build volume This way they can not be confused with objects that are rendered with a (future) grey materialcolor. --- plugins/SolidView/SolidView.py | 7 +-- resources/shaders/striped.shader | 84 ++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 resources/shaders/striped.shader diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 15cf25e65e..96f60fb7cf 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -34,9 +34,10 @@ class SolidView(View): self._enabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader")) if not self._disabled_shader: - self._disabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader")) - self._disabled_shader.setUniformValue("u_diffuseColor", [0.68, 0.68, 0.68, 1.0]) - self._disabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) + self._disabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader")) + self._disabled_shader.setUniformValue("u_diffuseColor1", [1.0, 0.28, 0.28, 1.0]) + self._disabled_shader.setUniformValue("u_diffuseColor2", [0.68, 0.68, 0.68, 1.0]) + self._disabled_shader.setUniformValue("u_width", 50.0) if Application.getInstance().getGlobalContainerStack(): if Preferences.getInstance().getValue("view/show_overhang"): diff --git a/resources/shaders/striped.shader b/resources/shaders/striped.shader new file mode 100644 index 0000000000..0114f0b2cb --- /dev/null +++ b/resources/shaders/striped.shader @@ -0,0 +1,84 @@ +[shaders] +vertex = + uniform highp mat4 u_modelMatrix; + uniform highp mat4 u_viewProjectionMatrix; + uniform highp mat4 u_normalMatrix; + + attribute highp vec4 a_vertex; + attribute highp vec4 a_normal; + attribute highp vec2 a_uvs; + + varying highp vec3 v_position; + varying highp vec3 v_vertex; + varying highp vec3 v_normal; + + void main() + { + vec4 world_space_vert = u_modelMatrix * a_vertex; + gl_Position = u_viewProjectionMatrix * world_space_vert; + + v_position = gl_Position.xyz; + v_vertex = world_space_vert.xyz; + v_normal = (u_normalMatrix * normalize(a_normal)).xyz; + } + +fragment = + uniform mediump vec4 u_ambientColor; + uniform mediump vec4 u_diffuseColor1; + uniform mediump vec4 u_diffuseColor2; + uniform mediump vec4 u_specularColor; + uniform highp vec3 u_lightPosition; + uniform mediump float u_shininess; + uniform highp vec3 u_viewPosition; + + uniform mediump float u_width; + + varying highp vec3 v_position; + varying highp vec3 v_vertex; + varying highp vec3 v_normal; + + void main() + { + mediump vec4 finalColor = vec4(0.0); + mediump vec4 diffuseColor = (mod((-v_position.x + v_position.y), u_width) < (u_width / 2.)) ? u_diffuseColor1 : u_diffuseColor2; + + /* Ambient Component */ + finalColor += u_ambientColor; + + highp vec3 normal = normalize(v_normal); + highp vec3 lightDir = normalize(u_lightPosition - v_vertex); + + /* Diffuse Component */ + highp float NdotL = clamp(abs(dot(normal, lightDir)), 0.0, 1.0); + finalColor += (NdotL * diffuseColor); + + /* Specular Component */ + /* TODO: We should not do specularity for fragments facing away from the light.*/ + highp vec3 reflectedLight = reflect(-lightDir, normal); + highp vec3 viewVector = normalize(u_viewPosition - v_vertex); + highp float NdotR = clamp(dot(viewVector, reflectedLight), 0.0, 1.0); + finalColor += pow(NdotR, u_shininess) * u_specularColor; + + gl_FragColor = finalColor; + gl_FragColor.a = 1.0; + } + +[defaults] +u_ambientColor = [0.3, 0.3, 0.3, 1.0] +u_diffuseColor1 = [1.0, 0.5, 0.5, 1.0] +u_diffuseColor2 = [0.5, 0.5, 0.5, 1.0] +u_specularColor = [0.4, 0.4, 0.4, 1.0] +u_shininess = 20.0 +u_width = 5.0 + +[bindings] +u_modelMatrix = model_matrix +u_viewProjectionMatrix = view_projection_matrix +u_normalMatrix = normal_matrix +u_viewPosition = view_position +u_lightPosition = light_0_position +u_diffuseColor = diffuse_color + +[attributes] +a_vertex = vertex +a_normal = normal From be252fb84f49fa185ae44fb19c92e7f59a4e9e7e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 22 Jul 2016 15:31:37 +0200 Subject: [PATCH 061/102] We now check if a setting can also be removed to get the same state THis should prevent the reset buttons from showing up when there is nothing going on. CURA-1758 --- cura/Settings/MachineManager.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 79f0020603..d5ccfea88e 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -234,7 +234,27 @@ class MachineManager(QObject): for container in extruder_stack.getContainers(): if container.__class__ == UM.Settings.InstanceContainer and container.getInstance(key) != None: if container.getProperty(key, "value") != new_value: - extruder_stack.getTop().setProperty(key, "value", new_value) + # It could be that the setting needs to be removed instead of updated. + temp = extruder_stack + containers = extruder_stack.getContainers() + # Ensure we have the entire 'chain' + while temp.getNextStack(): + temp = temp.getNextStack() + containers.extend(temp.getContainers()) + instance_needs_removal = False + + if len(containers) > 1: + for index in range(1, len(containers)): + deeper_container = containers[index] + if deeper_container.getProperty(key, "value") == new_value: + # Removal will result in correct value, so do that. + # We do this to prevent the reset from showing up unneeded. + instance_needs_removal = True + break + if instance_needs_removal: + extruder_stack.getTop().removeInstance(key) + else: + extruder_stack.getTop().setProperty(key, "value", new_value) else: # Check if we really need to remove something. if extruder_stack.getProperty(key, "value") != new_value: From cb4c1fd08c01d40cdc3e5608018a5db9b986a0c9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 22 Jul 2016 16:45:11 +0200 Subject: [PATCH 062/102] Rename skirt_speed to skirt_brim_speed Also updated the description. Because this concerns not only the skirt, but also the brim. Contributes to issue CURA-1678. --- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 4 ++++ resources/definitions/bq_hephestos_2.def.json | 2 +- resources/definitions/bq_witbox_2.def.json | 2 +- resources/definitions/fdmprinter.def.json | 6 +++--- resources/definitions/maker_starter.def.json | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 2493e23405..7cf3d0915e 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -31,6 +31,7 @@ _setting_name_translations = { "remove_overlapping_walls_enabled": "travel_compensate_overlapping_walls_enabled", "remove_overlapping_walls_x_enabled": "travel_compensate_overlapping_walls_x_enabled", "retraction_hop": "retraction_hop_enabled", + "skirt_speed": "skirt_brim_speed", "speed_support_lines": "speed_support_infill" } @@ -157,6 +158,9 @@ class VersionUpgrade21to22(VersionUpgrade): elif key == "retraction_hop": #Setting key was changed. del settings[key] settings["retraction_hop_enabled"] = value + elif key == "skirt_speed": #Setting key was changed. + del settings[key] + settings["skirt_brim_speed"] = value elif key == "speed_support_lines": #Setting key was changed. del settings[key] settings["speed_support_infill"] = value diff --git a/resources/definitions/bq_hephestos_2.def.json b/resources/definitions/bq_hephestos_2.def.json index 51777ff83e..920c5f5e9b 100644 --- a/resources/definitions/bq_hephestos_2.def.json +++ b/resources/definitions/bq_hephestos_2.def.json @@ -38,7 +38,7 @@ "speed_wall_0": { "default_value": 30 }, "speed_infill": { "default_value": 80 }, "speed_topbottom": { "default_value": 35 }, - "skirt_speed": { "default_value": 35 }, + "skirt_brim_speed": { "default_value": 35 }, "skirt_line_count": { "default_value": 4 }, "skirt_minimal_length": { "default_value": 30 }, "skirt_gap": { "default_value": 6 }, diff --git a/resources/definitions/bq_witbox_2.def.json b/resources/definitions/bq_witbox_2.def.json index b9d9b497cd..73dff97eeb 100644 --- a/resources/definitions/bq_witbox_2.def.json +++ b/resources/definitions/bq_witbox_2.def.json @@ -89,7 +89,7 @@ "speed_topbottom": { "default_value": 35 }, - "skirt_speed": { + "skirt_brim_speed": { "default_value": 35 }, "skirt_line_count": { diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 8f435c1e5c..e8831e27cd 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1520,9 +1520,9 @@ "maximum_value_warning": "300", "settable_per_mesh": true }, - "skirt_speed": { - "label": "Skirt Speed", - "description": "The speed at which the skirt and brim are printed. Normally this is done at the initial layer speed, but sometimes you might want to print the skirt at a different speed.", + "skirt_brim_speed": { + "label": "Skirt/Brim Speed", + "description": "The speed at which the skirt and brim are printed. Normally this is done at the initial layer speed, but sometimes you might want to print the skirt or brim at a different speed.", "unit": "mm/s", "type": "float", "default_value": 30, diff --git a/resources/definitions/maker_starter.def.json b/resources/definitions/maker_starter.def.json index a26ca058f0..4d3f6e06b7 100644 --- a/resources/definitions/maker_starter.def.json +++ b/resources/definitions/maker_starter.def.json @@ -94,7 +94,7 @@ "speed_layer_0": { "default_value": 20 }, - "skirt_speed": { + "skirt_brim_speed": { "default_value": 15 }, "speed_slowdown_layers": { From 23f643aabc5a14a9202ef9e2fa709bca7ee62b39 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 22 Jul 2016 16:49:31 +0200 Subject: [PATCH 063/102] Rename setting acceleration_skirt to acceleration_skirt_brim Also updated the description. Because this concerns not only the skirt, but also the brim. Contributes to issue CURA-1678. --- resources/definitions/fdmprinter.def.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index e8831e27cd..caaefa8d5a 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1719,9 +1719,9 @@ "enabled": "acceleration_enabled", "settable_per_mesh": true }, - "acceleration_skirt": { - "label": "Skirt Acceleration", - "description": "The acceleration with which the skirt and brim are printed. Normally this is done with the initial layer acceleration, but sometimes you might want to print the skirt at a different acceleration.", + "acceleration_skirt_brim": { + "label": "Skirt/Brim Acceleration", + "description": "The acceleration with which the skirt and brim are printed. Normally this is done with the initial layer acceleration, but sometimes you might want to print the skirt or brim at a different acceleration.", "unit": "mm/s²", "type": "float", "default_value": 3000, From 5e2f1688b47c978a27fbb40a6b1fa4732a1de17f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 22 Jul 2016 16:53:41 +0200 Subject: [PATCH 064/102] Rename setting jerk_skirt to jerk_skirt_brim Because it concerns not only the skirt, but also the brim. Contributes to issue CURA-1678. --- resources/definitions/fdmprinter.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index caaefa8d5a..b433fa0725 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1907,8 +1907,8 @@ "enabled": "jerk_enabled", "settable_per_mesh": true }, - "jerk_skirt": { - "label": "Skirt Jerk", + "jerk_skirt_brim": { + "label": "Skirt/Brim Jerk", "description": "The maximum instantaneous velocity change with which the skirt and brim are printed.", "unit": "mm/s", "type": "float", From d4f96ab62daa4e573c3ec53e224cfed8d3f912dd Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 22 Jul 2016 17:00:30 +0200 Subject: [PATCH 065/102] Rename setting skirt_line_width to skirt_brim_line_width Also updated the description. Because this concerns not only the skirt, but also the brim. Contributes to issue CURA-1678. --- cura/BuildVolume.py | 6 +++--- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 4 ++++ resources/definitions/fdmprinter.def.json | 8 ++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index e5bd037f1b..66428b7cba 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -364,9 +364,9 @@ class BuildVolume(SceneNode): if adhesion_type == "skirt": skirt_distance = container_stack.getProperty("skirt_gap", "value") skirt_line_count = container_stack.getProperty("skirt_line_count", "value") - skirt_size = skirt_distance + (skirt_line_count * container_stack.getProperty("skirt_line_width", "value")) + skirt_size = skirt_distance + (skirt_line_count * container_stack.getProperty("skirt_brim_line_width", "value")) elif adhesion_type == "brim": - skirt_size = container_stack.getProperty("brim_line_count", "value") * container_stack.getProperty("skirt_line_width", "value") + skirt_size = container_stack.getProperty("brim_line_count", "value") * container_stack.getProperty("skirt_brim_line_width", "value") elif adhesion_type == "raft": skirt_size = container_stack.getProperty("raft_margin", "value") @@ -381,5 +381,5 @@ class BuildVolume(SceneNode): def _clamp(self, value, min_value, max_value): return max(min(value, max_value), min_value) - _skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "xy_offset"] + _skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "xy_offset"] _raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap"] diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 7cf3d0915e..a429a7e64c 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -31,6 +31,7 @@ _setting_name_translations = { "remove_overlapping_walls_enabled": "travel_compensate_overlapping_walls_enabled", "remove_overlapping_walls_x_enabled": "travel_compensate_overlapping_walls_x_enabled", "retraction_hop": "retraction_hop_enabled", + "skirt_line_width": "skirt_brim_line_width", "skirt_speed": "skirt_brim_speed", "speed_support_lines": "speed_support_infill" } @@ -158,6 +159,9 @@ class VersionUpgrade21to22(VersionUpgrade): elif key == "retraction_hop": #Setting key was changed. del settings[key] settings["retraction_hop_enabled"] = value + elif key == "skirt_line_width": #Setting key was changed. + del settings[key] + settings["skirt_brim_line_width"] = value elif key == "skirt_speed": #Setting key was changed. del settings[key] settings["skirt_brim_speed"] = value diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index b433fa0725..5f3bb57789 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -603,10 +603,10 @@ "value": "line_width", "settable_per_mesh": true }, - "skirt_line_width": + "skirt_brim_line_width": { - "label": "Skirt Line Width", - "description": "Width of a single skirt line.", + "label": "Skirt/Brim Line Width", + "description": "Width of a single skirt or brim line.", "unit": "mm", "minimum_value": "0.0001", "minimum_value_warning": "0.2", @@ -2557,7 +2557,7 @@ "default_value": 20, "minimum_value": "0", "maximum_value_warning": "300", - "value": "math.ceil(brim_width / skirt_line_width)", + "value": "math.ceil(brim_width / skirt_brim_line_width)", "enabled": "adhesion_type == \"brim\"", "settable_per_mesh": false, "settable_per_extruder": true From 3042b6af5fc464c3f339f954925eceb9d534e7b5 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 22 Jul 2016 17:06:35 +0200 Subject: [PATCH 066/102] Rename skirt size to bed adhesion size Because it concerns not only the skirt, but also the brim and raft (and X/Y offset but I didn't include that in the name). Contributes to issue CURA-1678. --- cura/BuildVolume.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 66428b7cba..03ae28e275 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -195,18 +195,18 @@ class BuildVolume(SceneNode): minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h - self._raft_thickness, max_d)) - skirt_size = 0.0 + bed_adhesion_size = 0.0 container_stack = Application.getInstance().getGlobalContainerStack() if container_stack: - skirt_size = self._getSkirtSize(container_stack) + bed_adhesion_size = self._getBedAdhesionSize(container_stack) # As this works better for UM machines, we only add the disallowed_area_size for the z direction. # This is probably wrong in all other cases. TODO! # The +1 and -1 is added as there is always a bit of extra room required to work properly. scale_to_max_bounds = AxisAlignedBox( - minimum = Vector(min_w + skirt_size + 1, min_h, min_d + disallowed_area_size - skirt_size + 1), - maximum = Vector(max_w - skirt_size - 1, max_h - self._raft_thickness, max_d - disallowed_area_size + skirt_size - 1) + minimum = Vector(min_w + bed_adhesion_size + 1, min_h, min_d + disallowed_area_size - bed_adhesion_size + 1), + maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness, max_d - disallowed_area_size + bed_adhesion_size - 1) ) Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds @@ -311,53 +311,53 @@ class BuildVolume(SceneNode): [prime_x - PRIME_CLEARANCE, prime_y + PRIME_CLEARANCE], ]) - skirt_size = self._getSkirtSize(self._active_container_stack) + bed_adhesion_size = self._getBedAdhesionSize(self._active_container_stack) if disallowed_areas: # Extend every area already in the disallowed_areas with the skirt size. for area in disallowed_areas: poly = Polygon(numpy.array(area, numpy.float32)) - poly = poly.getMinkowskiHull(Polygon(approximatedCircleVertices(skirt_size))) + poly = poly.getMinkowskiHull(Polygon(approximatedCircleVertices(bed_adhesion_size))) areas.append(poly) # Add the skirt areas around the borders of the build plate. - if skirt_size > 0: + if bed_adhesion_size > 0: half_machine_width = self._active_container_stack.getProperty("machine_width", "value") / 2 half_machine_depth = self._active_container_stack.getProperty("machine_depth", "value") / 2 areas.append(Polygon(numpy.array([ [-half_machine_width, -half_machine_depth], [-half_machine_width, half_machine_depth], - [-half_machine_width + skirt_size, half_machine_depth - skirt_size], - [-half_machine_width + skirt_size, -half_machine_depth + skirt_size] + [-half_machine_width + bed_adhesion_size, half_machine_depth - bed_adhesion_size], + [-half_machine_width + bed_adhesion_size, -half_machine_depth + bed_adhesion_size] ], numpy.float32))) areas.append(Polygon(numpy.array([ [half_machine_width, half_machine_depth], [half_machine_width, -half_machine_depth], - [half_machine_width - skirt_size, -half_machine_depth + skirt_size], - [half_machine_width - skirt_size, half_machine_depth - skirt_size] + [half_machine_width - bed_adhesion_size, -half_machine_depth + bed_adhesion_size], + [half_machine_width - bed_adhesion_size, half_machine_depth - bed_adhesion_size] ], numpy.float32))) areas.append(Polygon(numpy.array([ [-half_machine_width, half_machine_depth], [half_machine_width, half_machine_depth], - [half_machine_width - skirt_size, half_machine_depth - skirt_size], - [-half_machine_width + skirt_size, half_machine_depth - skirt_size] + [half_machine_width - bed_adhesion_size, half_machine_depth - bed_adhesion_size], + [-half_machine_width + bed_adhesion_size, half_machine_depth - bed_adhesion_size] ], numpy.float32))) areas.append(Polygon(numpy.array([ [half_machine_width, -half_machine_depth], [-half_machine_width, -half_machine_depth], - [-half_machine_width + skirt_size, -half_machine_depth + skirt_size], - [half_machine_width - skirt_size, -half_machine_depth + skirt_size] + [-half_machine_width + bed_adhesion_size, -half_machine_depth + bed_adhesion_size], + [half_machine_width - bed_adhesion_size, -half_machine_depth + bed_adhesion_size] ], numpy.float32))) self._disallowed_areas = areas ## Convenience function to calculate the size of the bed adhesion in directions x, y. - def _getSkirtSize(self, container_stack): + def _getBedAdhesionSize(self, container_stack): skirt_size = 0.0 adhesion_type = container_stack.getProperty("adhesion_type", "value") From 54685e8898dd82f9cf8248f7b59fa8582f2436e9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 22 Jul 2016 17:12:34 +0200 Subject: [PATCH 067/102] Rename setting skirt_minimal_length to skirt_brim_minimal_length Also updated the description and made it a bit more accurate. Because this concerns not only the skirt, but also the brim. Contributes to issue CURA-1678. --- plugins/LegacyProfileReader/DictionaryOfDoom.json | 2 +- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 4 ++++ resources/definitions/bq_hephestos_2.def.json | 2 +- resources/definitions/bq_witbox_2.def.json | 2 +- resources/definitions/fdmprinter.def.json | 6 +++--- resources/definitions/mankati_fullscale_xt_plus.def.json | 2 +- resources/definitions/rigidbot.def.json | 2 +- resources/definitions/rigidbot_big.def.json | 2 +- 8 files changed, 13 insertions(+), 9 deletions(-) diff --git a/plugins/LegacyProfileReader/DictionaryOfDoom.json b/plugins/LegacyProfileReader/DictionaryOfDoom.json index 9dd0c04a05..471604ddbc 100644 --- a/plugins/LegacyProfileReader/DictionaryOfDoom.json +++ b/plugins/LegacyProfileReader/DictionaryOfDoom.json @@ -47,7 +47,7 @@ "adhesion_type": "\"skirt\" if (platform_adhesion == \"None\") else platform_adhesion.lower()", "skirt_line_count": "skirt_line_count", "skirt_gap": "skirt_gap", - "skirt_minimal_length": "skirt_minimal_length", + "skirt_brim_minimal_length": "skirt_minimal_length", "brim_line_count": "brim_line_count", "raft_margin": "raft_margin", "raft_airgap": "float(raft_airgap_all) + float(raft_airgap)", diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index a429a7e64c..64ea1ac665 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -32,6 +32,7 @@ _setting_name_translations = { "remove_overlapping_walls_x_enabled": "travel_compensate_overlapping_walls_x_enabled", "retraction_hop": "retraction_hop_enabled", "skirt_line_width": "skirt_brim_line_width", + "skirt_minimal_length": "skirt_brim_minimal_length", "skirt_speed": "skirt_brim_speed", "speed_support_lines": "speed_support_infill" } @@ -159,6 +160,9 @@ class VersionUpgrade21to22(VersionUpgrade): elif key == "retraction_hop": #Setting key was changed. del settings[key] settings["retraction_hop_enabled"] = value + elif key == "skirt_minimal_length": #Setting key was changed. + del settings[key] + settings["skirt_brim_minimal_length"] = value elif key == "skirt_line_width": #Setting key was changed. del settings[key] settings["skirt_brim_line_width"] = value diff --git a/resources/definitions/bq_hephestos_2.def.json b/resources/definitions/bq_hephestos_2.def.json index 920c5f5e9b..e49e7163e8 100644 --- a/resources/definitions/bq_hephestos_2.def.json +++ b/resources/definitions/bq_hephestos_2.def.json @@ -40,7 +40,7 @@ "speed_topbottom": { "default_value": 35 }, "skirt_brim_speed": { "default_value": 35 }, "skirt_line_count": { "default_value": 4 }, - "skirt_minimal_length": { "default_value": 30 }, + "skirt_brim_minimal_length": { "default_value": 30 }, "skirt_gap": { "default_value": 6 }, "cool_fan_full_at_height": { "default_value": 0.4 } } diff --git a/resources/definitions/bq_witbox_2.def.json b/resources/definitions/bq_witbox_2.def.json index 73dff97eeb..e47d082d0f 100644 --- a/resources/definitions/bq_witbox_2.def.json +++ b/resources/definitions/bq_witbox_2.def.json @@ -95,7 +95,7 @@ "skirt_line_count": { "default_value": 4 }, - "skirt_minimal_length": { + "skirt_brim_minimal_length": { "default_value": 30 }, "skirt_gap": { diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 5f3bb57789..6f8168d4ae 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -2521,10 +2521,10 @@ "settable_per_mesh": false, "settable_per_extruder": true }, - "skirt_minimal_length": + "skirt_brim_minimal_length": { - "label": "Skirt Minimum Length", - "description": "The minimum length of the skirt. If this length is not reached by the skirt line count, more skirt lines will be added until the minimum length is reached. Note: If the line count is set to 0 this is ignored.", + "label": "Skirt/Brim Minimum Length", + "description": "The minimum length of the skirt or brim. If this length is not reached by all skirt or brim lines together, more skirt or brim lines will be added until the minimum length is reached. Note: If the line count is set to 0 this is ignored.", "unit": "mm", "type": "float", "default_value": 250, diff --git a/resources/definitions/mankati_fullscale_xt_plus.def.json b/resources/definitions/mankati_fullscale_xt_plus.def.json index 57742696fb..eb8b99aaf9 100644 --- a/resources/definitions/mankati_fullscale_xt_plus.def.json +++ b/resources/definitions/mankati_fullscale_xt_plus.def.json @@ -61,6 +61,6 @@ "cool_fan_speed": { "default": 0 }, "skirt_line_count": { "default": 3 }, "skirt_gap": { "default": 4 }, - "skirt_minimal_length": { "default": 200 } + "skirt_brim_minimal_length": { "default": 200 } } } diff --git a/resources/definitions/rigidbot.def.json b/resources/definitions/rigidbot.def.json index ace8300d5d..fb8112a582 100644 --- a/resources/definitions/rigidbot.def.json +++ b/resources/definitions/rigidbot.def.json @@ -94,7 +94,7 @@ "default_value": 4, "enabled": "adhesion_type == \"Skirt\"" }, - "skirt_minimal_length": { + "skirt_brim_minimal_length": { "default_value": 200, "enabled": "adhesion_type == \"Skirt\"" } diff --git a/resources/definitions/rigidbot_big.def.json b/resources/definitions/rigidbot_big.def.json index ce27d36745..7026df646b 100644 --- a/resources/definitions/rigidbot_big.def.json +++ b/resources/definitions/rigidbot_big.def.json @@ -97,7 +97,7 @@ "default_value": 4, "enabled": "adhesion_type == \"Skirt\"" }, - "skirt_minimal_length": { + "skirt_brim_minimal_length": { "default_value": 200, "enabled": "adhesion_type == \"Skirt\"" } From 4e9bfc657398c3f2ac14271fe4cb0c41eb19a422 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 22 Jul 2016 17:23:03 +0200 Subject: [PATCH 068/102] Display some skirt/brim settings if skirt/brim enabled Some needed to be updated because they concern both skirt and brim. Others missed the check entirely. Contributes to issue CURA-1678. --- resources/definitions/fdmprinter.def.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 6f8168d4ae..024d679315 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -614,6 +614,7 @@ "default_value": 0.4, "type": "float", "value": "line_width", + "enabled": "adhesion_type == \"skirt\" or adhesion_type == \"brim\"", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -1530,6 +1531,7 @@ "maximum_value": "299792458000", "maximum_value_warning": "300", "value": "speed_layer_0", + "enabled": "adhesion_type == \"skirt\" or adhesion_type == \"brim\"", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -2531,7 +2533,7 @@ "minimum_value": "0", "minimum_value_warning": "25", "maximum_value_warning": "2500", - "enabled": "adhesion_type == \"skirt\"", + "enabled": "adhesion_type == \"skirt\" or adhesion_type == \"brim\"", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -2597,7 +2599,7 @@ "value": "raft_airgap / 2", "minimum_value": "0", "maximum_value_warning": "layer_height", - "enabled": "adhesion_type == 'raft'", + "enabled": "adhesion_type == \"raft\"", "settable_per_mesh": false, "settable_per_extruder": true }, From 000c4d5f41ea377265a13b29f76019d0d0c8a7d0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 25 Jul 2016 09:58:33 +0200 Subject: [PATCH 069/102] Fixed issue where changing a setting would cause mismatch between stacks CURA-1758 --- cura/Settings/MachineManager.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index d5ccfea88e..9f51591a58 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -246,11 +246,16 @@ class MachineManager(QObject): if len(containers) > 1: for index in range(1, len(containers)): deeper_container = containers[index] + if deeper_container.getProperty(key, "value") is None: + continue # Deeper container does not have the value, so continue. if deeper_container.getProperty(key, "value") == new_value: # Removal will result in correct value, so do that. # We do this to prevent the reset from showing up unneeded. instance_needs_removal = True break + else: + # Container has the value, but it's not the same. Stop looking. + break if instance_needs_removal: extruder_stack.getTop().removeInstance(key) else: From 374ec8e577071c7622142d73f430d682cfedd868 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 25 Jul 2016 11:30:30 +0200 Subject: [PATCH 070/102] Don't show user/quality emphasis on Per Object Settings --- plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml | 1 + resources/qml/Settings/SettingItem.qml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 546b7086e6..7695bea821 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -182,6 +182,7 @@ Item { settingLoader.item.showRevertButton = false settingLoader.item.showInheritButton = false settingLoader.item.doDepthIndentation = false + settingLoader.item.doQualityUserSettingEmphasis = false } sourceComponent: diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index a7bdabb3c5..4911bf37a4 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -22,6 +22,7 @@ Item { property var showRevertButton: true property var showInheritButton: true property var doDepthIndentation: true + property var doQualityUserSettingEmphasis: true // Create properties to put property provider stuff in (bindings break in qt 5.5.1 otherwise) property var state: propertyProvider.properties.state @@ -115,7 +116,7 @@ Item { color: UM.Theme.getColor("setting_control_text"); // emphasize the setting if it has a value in the user or quality profile - font: base.stackLevel != undefined && base.stackLevel <= 1 ? UM.Theme.getFont("default_italic") : UM.Theme.getFont("default") + font: base.doQualityUserSettingEmphasis && base.stackLevel != undefined && base.stackLevel <= 1 ? UM.Theme.getFont("default_italic") : UM.Theme.getFont("default") } Row From 87abed39f02674769753bfeb2dae16383db1f9a9 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 25 Jul 2016 11:41:54 +0200 Subject: [PATCH 071/102] Fix two warnings on startup CURA-1758 --- resources/qml/Settings/SettingView.qml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 957a68b94c..009dbf09a9 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -33,8 +33,6 @@ ScrollView exclude: ["machine_settings"] expanded: Printer.expandedCategories onExpandedChanged: Printer.setExpandedCategories(expanded) - - filter: {} } delegate: Loader @@ -183,7 +181,7 @@ ScrollView //: Settings context menu action text: catalog.i18nc("@action:menu", "Copy value to all extruders") visible: machineExtruderCount.properties.value > 1 - enabled: contextMenu.provider.properties.settable_per_extruder != "False" + enabled: contextMenu.provider != undefined && contextMenu.provider.properties.settable_per_extruder != "False" onTriggered: Cura.MachineManager.copyValueToExtruders(contextMenu.key) } From d9839ee41de81005636973903c873fa524f6e315 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Mon, 25 Jul 2016 13:40:19 +0200 Subject: [PATCH 072/102] SliceInfo: Using getDisplayString(ISO8601) instead --- plugins/SliceInfoPlugin/SliceInfo.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 85d26a0b93..0337aee02c 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -10,6 +10,7 @@ from UM.Message import Message from UM.i18n import i18nCatalog from UM.Logger import Logger from UM.Platform import Platform +from UM.Qt.Duration import DurationFormat import collections import json @@ -90,7 +91,7 @@ class SliceInfo(Extension): "settings": global_container_stack.serialize(), # global_container with references on used containers "version": Application.getInstance().getVersion(), "modelhash": "None", - "printtime": print_information.currentPrintTime.getDurationInISOFormat(), + "printtime": print_information.currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601), "filament": material_used, "language": Preferences.getInstance().getValue("general/language"), "materials_profiles ": {} @@ -127,4 +128,4 @@ class SliceInfo(Extension): except: # We really can't afford to have a mistake here, as this would break the sending of g-code to a device # (Either saving or directly to a printer). The functionality of the slice data is not *that* important. - pass \ No newline at end of file + pass From f51cffdf2e6e57fbd223ed869fb7ec4ad073b4dc Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 25 Jul 2016 13:50:47 +0200 Subject: [PATCH 073/102] We now ask the stack for the position (instead of definition) --- cura/Settings/ExtrudersModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 15e80d3f6b..cbe127d511 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -120,7 +120,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): material = extruder.findContainer({ "type": "material" }) if material: extruder_name = "%s (%s)" % (material.getName(), extruder_name) - position = extruder.getBottom().getMetaDataEntry("position", default = "0") #Position in the definition. + position = extruder.getMetaDataEntry("position", default = "0") # Get the position try: position = int(position) except ValueError: #Not a proper int. From 59e85258554fa0ac42e3fad41476e58b9569952f Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Mon, 25 Jul 2016 14:50:05 +0200 Subject: [PATCH 074/102] JSON fix: flow temp graph reenabled (CURA-1174) --- resources/definitions/fdmprinter.def.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index d4933be5bb..973645259d 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1060,10 +1060,9 @@ { "label": "Flow Temperature Graph", "description": "Data linking material flow (in mm3 per second) to temperature (degrees Celsius).", - "unit": "", + "unit": "[[mm³,°C]]", "type": "str", "default_value": "[[3.5,200],[7.0,240]]", - "enabled": "False", "comments": "old enabled function: material_flow_dependent_temperature", "settable_per_mesh": false, "settable_per_extruder": true From 7ce5e71b4c4154f0aa8e3fb7d7c5530fd70ed59d Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 25 Jul 2016 14:58:12 +0200 Subject: [PATCH 075/102] Code cleanup --- resources/qml/SidebarAdvanced.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/qml/SidebarAdvanced.qml b/resources/qml/SidebarAdvanced.qml index 30f4e74db6..0a3f25528f 100644 --- a/resources/qml/SidebarAdvanced.qml +++ b/resources/qml/SidebarAdvanced.qml @@ -8,6 +8,4 @@ import QtQuick.Controls 1.2 import "Settings" SettingView { -// expandedCategories: Printer.expandedCategories; -// onExpandedCategoriesChanged: Printer.setExpandedCategories(expandedCategories); } From 70242ba502c6237ffca8679a85d6a02afaf5bb39 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 25 Jul 2016 15:44:16 +0200 Subject: [PATCH 076/102] Saving a file no longer causes a re-slice CURA-1933 --- cura/Settings/MachineManager.py | 9 ++------- plugins/GCodeWriter/GCodeWriter.py | 10 +++++++++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 9f51591a58..7f6da8d821 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -543,15 +543,10 @@ class MachineManager(QObject): if containers: new_name = self._createUniqueName("quality", "", containers[0].getName(), catalog.i18nc("@label", "Custom profile")) - new_container = UM.Settings.InstanceContainer("") + new_container = containers[0].duplicate(new_name, new_name) - ## Copy all values - new_container.deserialize(containers[0].serialize()) - - new_container.setReadOnly(False) - new_container.setName(new_name) - new_container._id = new_name UM.Settings.ContainerRegistry.getInstance().addContainer(new_container) + return new_name return "" diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index 08105b06e8..15de585ae0 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -78,15 +78,19 @@ class GCodeWriter(MeshWriter): # Duplicate the current quality profile and update it with any user settings. flat_quality_id = machine_manager.duplicateContainer(container_with_profile.getId()) + flat_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = flat_quality_id)[0] user_settings = stack.getTop() + + # We don't want to send out any signals, so disconnect them. + flat_quality.propertyChanged.disconnectAll() + for key in user_settings.getAllKeys(): flat_quality.setProperty(key, "value", user_settings.getProperty(key, "value")) serialized = flat_quality.serialize() data = {"global_quality": serialized} - manager = ExtruderManager.getInstance() for extruder in manager.getMachineExtruders(stack.getId()): extruder_quality = extruder.findContainer({"type": "quality"}) @@ -94,6 +98,10 @@ class GCodeWriter(MeshWriter): flat_extruder_quality_id = machine_manager.duplicateContainer(extruder_quality.getId()) flat_extruder_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id=flat_extruder_quality_id)[0] extruder_user_settings = extruder.getTop() + + # We don't want to send out any signals, so disconnect them. + flat_extruder_quality.propertyChanged.disconnectAll() + for key in extruder_user_settings.getAllKeys(): flat_extruder_quality.setProperty(key, "value", extruder_user_settings.getProperty(key, "value")) From d77f6e86e4f31b18d0c5e0415ded41435c2a566a Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 25 Jul 2016 16:45:38 +0200 Subject: [PATCH 077/102] Added an extruded convex hull as a raft instead of a grey plane. - New shader transparent_object.shader - Raft thickness is calculated in BuildVolume and used in ConvexHullDecorator, notified by a Signal. - Removed old grey plane from BuildVolume. - Vertex data below build plane is no longer discarded (caused convex hulls that are too small). - Uses new functions in MeshBuilder (update Uranium as well). CURA-1707 --- cura/BuildVolume.py | 22 +++--- cura/ConvexHullDecorator.py | 22 ++++-- cura/ConvexHullNode.py | 49 ++++++-------- resources/shaders/transparent_object.shader | 74 +++++++++++++++++++++ 4 files changed, 121 insertions(+), 46 deletions(-) create mode 100644 resources/shaders/transparent_object.shader diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 03ae28e275..02d5ff1c67 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -13,6 +13,7 @@ from UM.Math.Color import Color from UM.Math.AxisAlignedBox import AxisAlignedBox from UM.Math.Polygon import Polygon from UM.Message import Message +from UM.Signal import Signal from UM.View.RenderBatch import RenderBatch from UM.View.GL.OpenGL import OpenGL @@ -49,6 +50,8 @@ def approximatedCircleVertices(r): class BuildVolume(SceneNode): VolumeOutlineColor = Color(12, 169, 227, 255) + raftThicknessChanged = Signal() + def __init__(self, parent = None): super().__init__(parent) @@ -69,7 +72,6 @@ class BuildVolume(SceneNode): self._raft_thickness = 0.0 self._adhesion_type = None - self._raft_mesh = None self._platform = Platform(self) self._active_container_stack = None @@ -103,8 +105,6 @@ class BuildVolume(SceneNode): renderer.queueNode(self, mesh = self._grid_mesh, shader = self._grid_shader, backface_cull = True) if self._disallowed_area_mesh: renderer.queueNode(self, mesh = self._disallowed_area_mesh, shader = self._shader, transparent = True, backface_cull = True, sort = -9) - if self._raft_mesh and self._adhesion_type == "raft": - renderer.queueNode(self, mesh=self._raft_mesh, transparent=True, backface_cull=True, sort=-9) return True @@ -153,17 +153,6 @@ class BuildVolume(SceneNode): mb.setVertexUVCoordinates(n, v[0], v[2]) self._grid_mesh = mb.build() - # Build raft mesh: a plane on the height of the raft. - mb = MeshBuilder() - mb.addQuad( - Vector(min_w, self._raft_thickness, min_d), - Vector(max_w, self._raft_thickness, min_d), - Vector(max_w, self._raft_thickness, max_d), - Vector(min_w, self._raft_thickness, max_d), - color=Color(128, 128, 128, 64) - ) - self._raft_mesh = mb.build() - disallowed_area_height = 0.1 disallowed_area_size = 0 if self._disallowed_areas: @@ -221,6 +210,9 @@ class BuildVolume(SceneNode): " \"Print Sequence\" setting to prevent the gantry from colliding" " with printed objects."), lifetime=10).show() + def getRaftThickness(self): + return self._raft_thickness + def _updateRaftThickness(self): old_raft_thickness = self._raft_thickness self._adhesion_type = self._active_container_stack.getProperty("adhesion_type", "value") @@ -232,9 +224,11 @@ class BuildVolume(SceneNode): self._active_container_stack.getProperty("raft_surface_layers", "value") * self._active_container_stack.getProperty("raft_surface_thickness", "value") + self._active_container_stack.getProperty("raft_airgap", "value")) + # Rounding errors do not matter, we check if raft_thickness has changed at all if old_raft_thickness != self._raft_thickness: self.setPosition(Vector(0, -self._raft_thickness, 0), SceneNode.TransformSpace.World) + self.raftThicknessChanged.emit() def _onGlobalContainerStackChanged(self): if self._active_container_stack: diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py index 3ccb9481f9..348a747f9c 100644 --- a/cura/ConvexHullDecorator.py +++ b/cura/ConvexHullDecorator.py @@ -15,6 +15,11 @@ class ConvexHullDecorator(SceneNodeDecorator): self._convex_hull_node = None self._init2DConvexHullCache() + self._raft_thickness = 0.0 + # For raft thickness, DRY + self._build_volume = Application.getInstance().getBuildVolume() + self._build_volume.raftThicknessChanged.connect(self._onChanged) + self._global_stack = None Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged) Application.getInstance().getController().toolOperationStarted.connect(self._onChanged) @@ -93,14 +98,17 @@ class ConvexHullDecorator(SceneNodeDecorator): convex_hull = self.getConvexHull() if self._convex_hull_node: - if self._convex_hull_node.getHull() == convex_hull: + # Check if convex hull has changed + if (self._convex_hull_node.getHull() == convex_hull and + self._convex_hull_node.getThickness() == self._raft_thickness): + return self._convex_hull_node.setParent(None) - hull_node = ConvexHullNode.ConvexHullNode(self._node, convex_hull, root) + hull_node = ConvexHullNode.ConvexHullNode(self._node, convex_hull, self._raft_thickness, root) self._convex_hull_node = hull_node def _onSettingValueChanged(self, key, property_name): - if key == "print_sequence" and property_name == "value": + if key in self._affected_settings and property_name == "value": self._onChanged() def _init2DConvexHullCache(self): @@ -157,7 +165,8 @@ class ConvexHullDecorator(SceneNodeDecorator): vertex_data = mesh.getConvexHullTransformedVertices(world_transform) # Don't use data below 0. # TODO; We need a better check for this as this gives poor results for meshes with long edges. - vertex_data = vertex_data[vertex_data[:,1] >= -0.01] + # Do not throw away vertices: the convex hull may be too small and objects can collide. + # vertex_data = vertex_data[vertex_data[:,1] >= -0.01] if len(vertex_data) >= 4: # Round the vertex data to 1/10th of a mm, then remove all duplicate vertices @@ -213,6 +222,7 @@ class ConvexHullDecorator(SceneNodeDecorator): return convex_hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32))) def _onChanged(self, *args): + self._raft_thickness = self._build_volume.getRaftThickness() self.recomputeConvexHull() def _onGlobalStackChanged(self): @@ -235,3 +245,7 @@ class ConvexHullDecorator(SceneNodeDecorator): if root is node: return True return self.__isDescendant(root, node.getParent()) + + _affected_settings = [ + "adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", + "raft_surface_thickness", "raft_airgap", "print_sequence"] diff --git a/cura/ConvexHullNode.py b/cura/ConvexHullNode.py index be571d111e..53bee40a8e 100644 --- a/cura/ConvexHullNode.py +++ b/cura/ConvexHullNode.py @@ -4,16 +4,16 @@ from UM.Scene.SceneNode import SceneNode from UM.Resources import Resources from UM.Math.Color import Color -from UM.Math.Vector import Vector from UM.Mesh.MeshBuilder import MeshBuilder # To create a mesh to display the convex hull with. from UM.View.GL.OpenGL import OpenGL class ConvexHullNode(SceneNode): - ## Convex hull node is a special type of scene node that is used to display a 2D area, to indicate the + ## Convex hull node is a special type of scene node that is used to display an area, to indicate the # location an object uses on the buildplate. This area (or area's in case of one at a time printing) is - # then displayed as a transparent shadow. - def __init__(self, node, hull, parent = None): + # then displayed as a transparent shadow. If the adhesion type is set to raft, the area is extruded + # to represent the raft as well. + def __init__(self, node, hull, thickness, parent = None): super().__init__(parent) self.setCalculateBoundingBox(False) @@ -23,7 +23,7 @@ class ConvexHullNode(SceneNode): self._original_parent = parent # Color of the drawn convex hull - self._color = Color(35, 35, 35, 128) + self._color = Color(35, 35, 35, 192) # The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting. self._mesh_height = 0.1 @@ -34,11 +34,17 @@ class ConvexHullNode(SceneNode): self._onNodeDecoratorsChanged(self._node) self._convex_hull_head_mesh = None - self._hull = hull + self._hull = hull + self._thickness = thickness if self._hull: - hull_mesh = self.createHullMesh(self._hull.getPoints()) - if hull_mesh: + hull_mesh_builder = MeshBuilder() + + if hull_mesh_builder.addConvexPolygonExtrusion( + self._hull.getPoints()[::-1], # bottom layer is reversed + self._mesh_height-thickness, self._mesh_height, color=self._color): + + hull_mesh = hull_mesh_builder.build() self.setMeshData(hull_mesh) convex_hull_head = self._node.callDecoration("getConvexHullHead") if convex_hull_head: @@ -47,31 +53,17 @@ class ConvexHullNode(SceneNode): def getHull(self): return self._hull - ## Actually create the mesh from the hullpoints - # /param hull_points list of xy values - # /return meshData - def createHullMesh(self, hull_points): - # Input checking. - if len(hull_points) < 3: - return None - - mesh_builder = MeshBuilder() - point_first = Vector(hull_points[0][0], self._mesh_height, hull_points[0][1]) - point_previous = Vector(hull_points[1][0], self._mesh_height, hull_points[1][1]) - for point in hull_points[2:]: # Add the faces in the order of a triangle fan. - point_new = Vector(point[0], self._mesh_height, point[1]) - mesh_builder.addFace(point_first, point_previous, point_new, color = self._color) - point_previous = point_new # Prepare point_previous for the next triangle. - - return mesh_builder.build() + def getThickness(self): + return self._thickness def getWatchedNode(self): return self._node def render(self, renderer): if not self._shader: - self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader")) - self._shader.setUniformValue("u_color", self._color) + self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader")) + self._shader.setUniformValue("u_diffuseColor", self._color) + self._shader.setUniformValue("u_opacity", 0.6) if self.getParent(): if self.getMeshData(): @@ -85,4 +77,5 @@ class ConvexHullNode(SceneNode): self._color = Color(35, 35, 35, 0.5) if not node: - return \ No newline at end of file + return + diff --git a/resources/shaders/transparent_object.shader b/resources/shaders/transparent_object.shader new file mode 100644 index 0000000000..a3790901bc --- /dev/null +++ b/resources/shaders/transparent_object.shader @@ -0,0 +1,74 @@ +[shaders] +vertex = + uniform highp mat4 u_viewProjectionMatrix; + uniform highp mat4 u_modelMatrix; + uniform highp mat4 u_normalMatrix; + + attribute highp vec4 a_vertex; + attribute highp vec4 a_normal; + attribute highp vec2 a_uvs; + + varying highp vec3 v_vertex; + varying highp vec3 v_normal; + varying highp vec2 v_uvs; + + void main() + { + vec4 world_space_vert = u_modelMatrix * a_vertex; + gl_Position = u_viewProjectionMatrix * world_space_vert; + + v_vertex = world_space_vert.xyz; + v_normal = (u_normalMatrix * normalize(a_normal)).xyz; + + v_uvs = a_uvs; + } + +fragment = + uniform mediump vec4 u_ambientColor; + uniform mediump vec4 u_diffuseColor; + uniform highp vec3 u_lightPosition; + uniform highp vec3 u_viewPosition; + uniform mediump float u_opacity; + uniform sampler2D u_texture; + + varying highp vec3 v_vertex; + varying highp vec3 v_normal; + varying highp vec2 v_uvs; + + void main() + { + // Copied from platform.shader, removed texture + mediump vec4 final_color = vec4(0.0); + + /* Ambient Component */ + final_color += u_ambientColor; + + highp vec3 normal = normalize(v_normal); + highp vec3 light_dir = normalize(u_lightPosition - v_vertex); + + /* Diffuse Component */ + highp float n_dot_l = clamp(dot(normal, light_dir), 0.0, 1.0); + final_color += (n_dot_l * u_diffuseColor); + + final_color.a = u_opacity; + + gl_FragColor = final_color; + } + +[defaults] +u_ambientColor = [0.3, 0.3, 0.3, 1.0] +u_diffuseColor = [1.0, 1.0, 1.0, 1.0] +u_opacity = 0.5 +u_texture = 0 + +[bindings] +u_viewProjectionMatrix = view_projection_matrix +u_modelMatrix = model_matrix +u_normalMatrix = normal_matrix +u_lightPosition = light_0_position +u_viewPosition = camera_position + +[attributes] +a_vertex = vertex +a_normal = normal +a_uvs = uv0 From d82249e26022059b455704ddf0a44353f89a1620 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 25 Jul 2016 17:09:06 +0200 Subject: [PATCH 078/102] Fix for one-at-a-time. CURA-1707 --- cura/ConvexHullDecorator.py | 3 ++- cura/ConvexHullNode.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py index 348a747f9c..2ba0047cd4 100644 --- a/cura/ConvexHullDecorator.py +++ b/cura/ConvexHullDecorator.py @@ -15,12 +15,13 @@ class ConvexHullDecorator(SceneNodeDecorator): self._convex_hull_node = None self._init2DConvexHullCache() + self._global_stack = None + self._raft_thickness = 0.0 # For raft thickness, DRY self._build_volume = Application.getInstance().getBuildVolume() self._build_volume.raftThicknessChanged.connect(self._onChanged) - self._global_stack = None Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged) Application.getInstance().getController().toolOperationStarted.connect(self._onChanged) Application.getInstance().getController().toolOperationStopped.connect(self._onChanged) diff --git a/cura/ConvexHullNode.py b/cura/ConvexHullNode.py index 53bee40a8e..f73db2a597 100644 --- a/cura/ConvexHullNode.py +++ b/cura/ConvexHullNode.py @@ -48,7 +48,9 @@ class ConvexHullNode(SceneNode): self.setMeshData(hull_mesh) convex_hull_head = self._node.callDecoration("getConvexHullHead") if convex_hull_head: - self._convex_hull_head_mesh = self.createHullMesh(convex_hull_head.getPoints()) + convex_hull_head_builder = MeshBuilder() + convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height-thickness) + self._convex_hull_head_mesh = convex_hull_head_builder.build() def getHull(self): return self._hull From 1057634e47f6d710ad12d8c346be01b6f51dd1a0 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 25 Jul 2016 17:39:43 +0200 Subject: [PATCH 079/102] Remove (non-functional) layer number from print monitor CURA-1940 --- resources/qml/PrintMonitor.qml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/resources/qml/PrintMonitor.qml b/resources/qml/PrintMonitor.qml index c8f56a5b98..ba2f3f822c 100644 --- a/resources/qml/PrintMonitor.qml +++ b/resources/qml/PrintMonitor.qml @@ -62,12 +62,6 @@ Column property string label: catalog.i18nc("@label", "Estimated time left") property string value: printerConnected ? getPrettyTime(Cura.MachineManager.printerOutputDevices[0].timeTotal - Cura.MachineManager.printerOutputDevices[0].timeElapsed) : "" } - Loader - { - sourceComponent: monitorItem - property string label: catalog.i18nc("@label", "Current Layer") - property string value: printerConnected ? "0" : "" - } Component { From 4014cb11f909a997115ded6f332f837d5f7fe0ea Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 25 Jul 2016 18:00:17 +0200 Subject: [PATCH 080/102] Reinstate UMO wizard CURA-1732 --- .../UMOUpgradeSelectionMachineAction.qml | 2 +- resources/definitions/ultimaker_original.def.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml index 07e317d9a4..d960bcf976 100644 --- a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml @@ -22,7 +22,7 @@ Cura.MachineAction { id: pageTitle width: parent.width - text: catalog.i18nc("@title", "Check Printer") + text: catalog.i18nc("@title", "Select Printer Upgrades") wrapMode: Text.WordWrap font.pointSize: 18; } diff --git a/resources/definitions/ultimaker_original.def.json b/resources/definitions/ultimaker_original.def.json index 60eb8dcd00..d2174ece80 100644 --- a/resources/definitions/ultimaker_original.def.json +++ b/resources/definitions/ultimaker_original.def.json @@ -14,7 +14,8 @@ "has_materials": true, "preferred_material": "*pla*", "preferred_quality": "*normal*", - "supported_actions":["UMOCheckup", "UpgradeFirmware", "BedLevel", "UMOUpgradeSelection"] + "first_start_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel"], + "supported_actions": ["UMOCheckup", "UpgradeFirmware", "BedLevel", "UMOUpgradeSelection"] }, "overrides": { From 85f53be9e255620bf2bfb6251394170bed0bee28 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 25 Jul 2016 21:59:37 +0200 Subject: [PATCH 081/102] Tone down striped shader colors --- plugins/SolidView/SolidView.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 96f60fb7cf..2182a0748a 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -35,7 +35,7 @@ class SolidView(View): if not self._disabled_shader: self._disabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader")) - self._disabled_shader.setUniformValue("u_diffuseColor1", [1.0, 0.28, 0.28, 1.0]) + self._disabled_shader.setUniformValue("u_diffuseColor1", [0.48, 0.48, 0.48, 1.0]) self._disabled_shader.setUniformValue("u_diffuseColor2", [0.68, 0.68, 0.68, 1.0]) self._disabled_shader.setUniformValue("u_width", 50.0) From 76723c2412d33cc54000b970dcea12dffd7fe20c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 26 Jul 2016 09:55:04 +0200 Subject: [PATCH 082/102] Removed 50 ms delay in staring top layer calculation --- plugins/LayerView/LayerView.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index bc5ef655b2..87e1a30834 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -49,11 +49,6 @@ class LayerView(View): self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) - self._top_layer_timer = QTimer() - self._top_layer_timer.setInterval(50) - self._top_layer_timer.setSingleShot(True) - self._top_layer_timer.timeout.connect(self._startUpdateTopLayers) - self._busy = False def getActivity(self): @@ -132,8 +127,7 @@ class LayerView(View): self._current_layer_num = self._max_layers self.resetLayerData() - - self._top_layer_timer.start() + self._startUpdateTopLayers() self.currentLayerNumChanged.emit() @@ -165,7 +159,7 @@ class LayerView(View): else: self.setLayer(int(self._max_layers)) self.maxLayersChanged.emit() - self._top_layer_timer.start() + self._startUpdateTopLayers() maxLayersChanged = Signal() currentLayerNumChanged = Signal() @@ -219,7 +213,7 @@ class LayerView(View): self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) self.resetLayerData() - self._top_layer_timer.start() + self._startUpdateTopLayers() class _CreateTopLayersJob(Job): def __init__(self, scene, layer_number, solid_layers): From 3703ed9eb061caa4e2e0cf50bcafb6205b473b03 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 26 Jul 2016 11:45:06 +0200 Subject: [PATCH 083/102] Fix overlapping object with raft/brim/skirt in one-at-a-time mode. head_and_fans is extended by an extra margin to prevent overlapping. CURA-1776 Also fixed: showing one-at-a-time head convex at start. --- cura/ConvexHullDecorator.py | 52 ++++++++++++++++++++++++++++++++----- cura/ConvexHullNode.py | 18 +++++++------ 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py index 2ba0047cd4..d864f4288b 100644 --- a/cura/ConvexHullDecorator.py +++ b/cura/ConvexHullDecorator.py @@ -51,6 +51,7 @@ class ConvexHullDecorator(SceneNodeDecorator): return None hull = self._compute2DConvexHull() + if self._global_stack and self._node: if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"): hull = hull.getMinkowskiHull(Polygon(numpy.array(self._global_stack.getProperty("machine_head_polygon", "value"), numpy.float32))) @@ -72,7 +73,9 @@ class ConvexHullDecorator(SceneNodeDecorator): if self._global_stack: if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"): - return self._compute2DConvexHeadMin() + head_with_fans = self._compute2DConvexHeadMin() + head_with_fans_with_adhesion_margin = self._add2DAdhesionMargin(head_with_fans) + return head_with_fans_with_adhesion_margin return None ## Get convex hull of the node @@ -84,6 +87,7 @@ class ConvexHullDecorator(SceneNodeDecorator): if self._global_stack: if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"): + # Printing one at a time and it's not an object in a group return self._compute2DConvexHull() return None @@ -99,11 +103,6 @@ class ConvexHullDecorator(SceneNodeDecorator): convex_hull = self.getConvexHull() if self._convex_hull_node: - # Check if convex hull has changed - if (self._convex_hull_node.getHull() == convex_hull and - self._convex_hull_node.getThickness() == self._raft_thickness): - - return self._convex_hull_node.setParent(None) hull_node = ConvexHullNode.ConvexHullNode(self._node, convex_hull, self._raft_thickness, root) self._convex_hull_node = hull_node @@ -219,6 +218,44 @@ class ConvexHullDecorator(SceneNodeDecorator): min_head_hull = self._compute2DConvexHull().getMinkowskiHull(head_and_fans) return min_head_hull + ## Compensate given 2D polygon with adhesion margin + # \return 2D polygon with added margin + def _add2DAdhesionMargin(self, poly): + # Compensate for raft/skirt/brim + # Add extra margin depending on adhesion type + adhesion_type = self._global_stack.getProperty("adhesion_type", "value") + extra_margin = 0 + machine_head_coords = numpy.array( + self._global_stack.getProperty("machine_head_with_fans_polygon", "value"), + numpy.float32) + head_y_size = abs(machine_head_coords).min() # safe margin to take off in all directions + + if adhesion_type == "raft": + extra_margin = max(0, self._global_stack.getProperty("raft_margin", "value") - head_y_size) + elif adhesion_type == "brim": + extra_margin = max(0, self._global_stack.getProperty("brim_width", "value") - head_y_size) + elif adhesion_type == "skirt": + extra_margin = max( + 0, self._global_stack.getProperty("skirt_gap", "value") + + self._global_stack.getProperty("skirt_line_count", "value") * self._global_stack.getProperty("skirt_brim_line_width", "value") - + head_y_size) + # adjust head_and_fans with extra margin + if extra_margin > 0: + # In Cura 2.2+, there is a function to create this circle-like polygon. + extra_margin_polygon = Polygon(numpy.array([ + [-extra_margin, 0], + [-extra_margin * 0.707, extra_margin * 0.707], + [0, extra_margin], + [extra_margin * 0.707, extra_margin * 0.707], + [extra_margin, 0], + [extra_margin * 0.707, -extra_margin * 0.707], + [0, -extra_margin], + [-extra_margin * 0.707, -extra_margin * 0.707] + ], numpy.float32)) + + poly = poly.getMinkowskiHull(extra_margin_polygon) + return poly + def _roundHull(self, convex_hull): return convex_hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32))) @@ -249,4 +286,5 @@ class ConvexHullDecorator(SceneNodeDecorator): _affected_settings = [ "adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", - "raft_surface_thickness", "raft_airgap", "print_sequence"] + "raft_surface_thickness", "raft_airgap", "print_sequence", + "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "skirt_distance"] diff --git a/cura/ConvexHullNode.py b/cura/ConvexHullNode.py index f73db2a597..703dfb0bed 100644 --- a/cura/ConvexHullNode.py +++ b/cura/ConvexHullNode.py @@ -28,15 +28,16 @@ class ConvexHullNode(SceneNode): # The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting. self._mesh_height = 0.1 + self._thickness = thickness + # The node this mesh is "watching" self._node = node + self._convex_hull_head_mesh = None + self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged) self._onNodeDecoratorsChanged(self._node) - self._convex_hull_head_mesh = None - self._hull = hull - self._thickness = thickness if self._hull: hull_mesh_builder = MeshBuilder() @@ -46,11 +47,6 @@ class ConvexHullNode(SceneNode): hull_mesh = hull_mesh_builder.build() self.setMeshData(hull_mesh) - convex_hull_head = self._node.callDecoration("getConvexHullHead") - if convex_hull_head: - convex_hull_head_builder = MeshBuilder() - convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height-thickness) - self._convex_hull_head_mesh = convex_hull_head_builder.build() def getHull(self): return self._hull @@ -78,6 +74,12 @@ class ConvexHullNode(SceneNode): def _onNodeDecoratorsChanged(self, node): self._color = Color(35, 35, 35, 0.5) + convex_hull_head = self._node.callDecoration("getConvexHullHead") + if convex_hull_head: + convex_hull_head_builder = MeshBuilder() + convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height-self._thickness) + self._convex_hull_head_mesh = convex_hull_head_builder.build() + if not node: return From 336dd406b15bff2e7a530e41592ef03d60f4c2bb Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 26 Jul 2016 11:58:25 +0200 Subject: [PATCH 084/102] Add a reusable mechanism for plugins to add controls to designated areas in the GUI contributes to CURA-271 --- cura/CuraApplication.py | 22 +++++++++++++++++++++- resources/qml/SaveButton.qml | 26 +++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a6d0a3b827..9dc5987cb7 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -128,6 +128,8 @@ class CuraApplication(QtApplication): self._machine_action_manager = MachineActionManager.MachineActionManager() self._machine_manager = None # This is initialized on demand. + self._additional_components = {} # Components to add to certain areas in the interface + super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType) self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) @@ -876,4 +878,22 @@ class CuraApplication(QtApplication): self.getMainWindow().setMinimumSize(size) def getBuildVolume(self): - return self._volume \ No newline at end of file + return self._volume + + additionalComponentsChanged = pyqtSignal(str, arguments = ["areaId"]) + + @pyqtProperty("QVariantMap", notify = additionalComponentsChanged) + def additionalComponents(self): + return self._additional_components + + ## Add a component to a list of components to be reparented to another area in the GUI. + # The actual reparenting is done by the area itself. + # \param area_id \type{str} Identifying name of the area to which the component should be reparented + # \param component \type{QQuickComponent} The component that should be reparented + @pyqtSlot(str, "QVariant") + def addAdditionalComponent(self, area_id, component): + if area_id not in self._additional_components: + self._additional_components[area_id] = [] + self._additional_components[area_id].append(component) + + self.additionalComponentsChanged.emit(area_id) \ No newline at end of file diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 8b95de15ee..9ea6e181bb 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -84,6 +84,27 @@ Rectangle { anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.left: parent.left + Row { + id: additionalComponentsRow + anchors.top: parent.top + anchors.right: saveToButton.visible ? saveToButton.left : parent.right + anchors.rightMargin: UM.Theme.getSize("default_margin").width + + spacing: UM.Theme.getSize("default_margin").width + } + + Connections { + target: Printer + onAdditionalComponentsChanged: + { + if(areaId == "saveButton") { + for (var component in Printer.additionalComponents["saveButton"]) { + Printer.additionalComponents["saveButton"][component].parent = additionalComponentsRow + } + } + } + } + Button { id: saveToButton @@ -102,8 +123,7 @@ Rectangle { } style: ButtonStyle { - background: - Rectangle + background: Rectangle { border.width: UM.Theme.getSize("default_lining").width border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") : @@ -126,7 +146,7 @@ Rectangle { text: control.text; } } - label: Item { } + label: Item { } } } From 8540e104697a48529e94d71f096db5c375dd5e6c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 26 Jul 2016 12:10:25 +0200 Subject: [PATCH 085/102] Updating multiple materials with one message box now works CURA-1909 --- cura/Settings/MachineManager.py | 126 ++++++++++++++++---------------- 1 file changed, 61 insertions(+), 65 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 7f6da8d821..0f03c98775 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -64,9 +64,8 @@ class MachineManager(QObject): # Make sure _active_container_stack is properly initiated ExtruderManager.getInstance().setActiveExtruderIndex(0) - self._auto_change_material_hotend_flood_window = 10 # The minimum number of seconds between asking if the material or hotend on the machine should be used - self._auto_change_material_hotend_flood_time = 0 # The last timestamp (in seconds) when the user was asked about changing the material or hotend to whatis loaded on the machine - self._auto_change_material_hotend_flood_last_choice = None # The last choice that was made, so we can apply that choice again + self._auto_materials_changed = {} + self._auto_hotends_changed = {} globalContainerChanged = pyqtSignal() activeMaterialChanged = pyqtSignal() @@ -104,48 +103,50 @@ class MachineManager(QObject): if not self._global_container_stack: return - containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = self._global_container_stack.getBottom().getId(), name = hotend_id) - if containers: + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type="variant", definition=self._global_container_stack.getBottom().getId(), name=hotend_id) + if containers: # New material ID is known extruder_manager = ExtruderManager.getInstance() - old_index = extruder_manager.activeExtruderIndex - if old_index != index: - extruder_manager.setActiveExtruderIndex(index) - else: - old_index = None + extruders = list(extruder_manager.getMachineExtruders(self.activeMachineId)) + matching_extruder = None + for extruder in extruders: + if str(index) == extruder.getMetaDataEntry("position"): + matching_extruder = extruder + break + if matching_extruder and matching_extruder.findContainer({"type": "variant"}).getName() != hotend_id: + # Save the material that needs to be changed. Multiple changes will be handled by the callback. + self._auto_hotends_changed[str(index)] = containers[0].getId() + Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), + catalog.i18nc("@label", + "Do you want to change the hotend to match the hotend in your printer?"), + catalog.i18nc("@label", + "The hotend on your printer was changed. For best results always slice for the hotend that is inserted in your printer."), + buttons=QMessageBox.Yes + QMessageBox.No, + icon=QMessageBox.Question, + callback=self._hotendChangedDialogCallback) - if self.activeVariantId != containers[0].getId(): - if time.time() - self._auto_change_material_hotend_flood_time > self._auto_change_material_hotend_flood_window: - Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the hotend to match the hotend in your printer?"), - catalog.i18nc("@label", "The hotend on your printer was changed. For best results always slice for the hotend that is inserted in your printer."), - buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._hotendChangedDialogCallback, callback_arguments = [index, containers[0].getId()]) - else: - self._hotendChangedDialogCallback(self._auto_change_material_hotend_flood_last_choice, index, containers[0].getId()) - if old_index is not None: - extruder_manager.setActiveExtruderIndex(old_index) else: Logger.log("w", "No variant found for printer definition %s with id %s" % (self._global_container_stack.getBottom().getId(), hotend_id)) - def _hotendChangedDialogCallback(self, button, index, hotend_id): - self._auto_change_material_hotend_flood_time = time.time() - self._auto_change_material_hotend_flood_last_choice = button - + def _hotendChangedDialogCallback(self, button): if button == QMessageBox.No: + self._auto_hotends_changed = {} return - Logger.log("d", "Setting hotend variant of hotend %d to %s" % (index, hotend_id)) - extruder_manager = ExtruderManager.getInstance() - old_index = extruder_manager.activeExtruderIndex - if old_index != index: - extruder_manager.setActiveExtruderIndex(index) - else: - old_index = None + for position in self._auto_hotends_changed: + hotend_id = self._auto_hotends_changed[position] + old_index = extruder_manager.activeExtruderIndex - self.setActiveVariant(hotend_id) + if old_index != int(position): + extruder_manager.setActiveExtruderIndex(int(position)) + else: + old_index = None + Logger.log("d", "Setting hotend variant of hotend %s to %s" % (position, hotend_id)) + self.setActiveVariant(hotend_id) - if old_index is not None: - extruder_manager.setActiveExtruderIndex(old_index) + if old_index is not None: + extruder_manager.setActiveExtruderIndex(old_index) def _onMaterialIdChanged(self, index, material_id): if not self._global_container_stack: @@ -154,50 +155,45 @@ class MachineManager(QObject): definition_id = "fdmprinter" if self._global_container_stack.getMetaDataEntry("has_machine_materials", False): definition_id = self._global_container_stack.getBottom().getId() - + extruder_manager = ExtruderManager.getInstance() containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "material", definition = definition_id, GUID = material_id) - if containers: - extruder_manager = ExtruderManager.getInstance() - old_index = extruder_manager.activeExtruderIndex - if old_index != index: - extruder_manager.setActiveExtruderIndex(index) - else: - old_index = None + if containers: # New material ID is known + extruders = list(extruder_manager.getMachineExtruders(self.activeMachineId)) + matching_extruder = None + for extruder in extruders: + if str(index) == extruder.getMetaDataEntry("position"): + matching_extruder = extruder + break - if self.activeMaterialId != containers[0].getId(): - if time.time() - self._auto_change_material_hotend_flood_time > self._auto_change_material_hotend_flood_window: - Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the material to match the material in your printer?"), - catalog.i18nc("@label", "The material on your printer was changed. For best results always slice for the material that is inserted in your printer."), - buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._materialIdChangedDialogCallback, callback_arguments = [index, containers[0].getId()]) - else: - self._materialIdChangedDialogCallback(self._auto_change_material_hotend_flood_last_choice, index, containers[0].getId()) - if old_index is not None: - extruder_manager.setActiveExtruderIndex(old_index) + if matching_extruder and matching_extruder.findContainer({"type":"material"}).getMetaDataEntry("GUID") != material_id: + # Save the material that needs to be changed. Multiple changes will be handled by the callback. + self._auto_materials_changed[str(index)] = containers[0].getId() + Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the material to match the material in your printer?"), + catalog.i18nc("@label", "The material on your printer was changed. For best results always slice for the material that is inserted in your printer."), + buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._materialIdChangedDialogCallback) else: Logger.log("w", "No material definition found for printer definition %s and GUID %s" % (definition_id, material_id)) - def _materialIdChangedDialogCallback(self, button, index, material_id): - self._auto_change_material_hotend_flood_time = time.time() - self._auto_change_material_hotend_flood_last_choice = button - + def _materialIdChangedDialogCallback(self, button): if button == QMessageBox.No: + self._auto_materials_changed = {} return - - Logger.log("d", "Setting material of hotend %d to %s" % (index, material_id)) - extruder_manager = ExtruderManager.getInstance() - old_index = extruder_manager.activeExtruderIndex - if old_index != index: - extruder_manager.setActiveExtruderIndex(index) - else: - old_index = None + for position in self._auto_materials_changed: + material_id = self._auto_materials_changed[position] + old_index = extruder_manager.activeExtruderIndex - self.setActiveMaterial(material_id) + if old_index != int(position): + extruder_manager.setActiveExtruderIndex(int(position)) + else: + old_index = None - if old_index is not None: - extruder_manager.setActiveExtruderIndex(old_index) + Logger.log("d", "Setting material of hotend %s to %s" % (position, material_id)) + self.setActiveMaterial(material_id) + if old_index is not None: + extruder_manager.setActiveExtruderIndex(old_index) def _onGlobalPropertyChanged(self, key, property_name): if property_name == "value": From 05b2c147e0a1bec79507d68775343752b3e15e7b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 26 Jul 2016 13:28:53 +0200 Subject: [PATCH 086/102] Combined material & hotend message Even if all data is different, the update is correctly handled CURA-1909 --- cura/Settings/MachineManager.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 0f03c98775..ca09cbc5dc 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -117,22 +117,18 @@ class MachineManager(QObject): self._auto_hotends_changed[str(index)] = containers[0].getId() Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", - "Do you want to change the hotend to match the hotend in your printer?"), + "Do you want to change the materials and hotends to match the material in your printer?"), catalog.i18nc("@label", - "The hotend on your printer was changed. For best results always slice for the hotend that is inserted in your printer."), + "The materials and / or hotends on your printer were changed. For best results always slice for the materials . hotends that are inserted in your printer."), buttons=QMessageBox.Yes + QMessageBox.No, icon=QMessageBox.Question, - callback=self._hotendChangedDialogCallback) + callback=self._materialHotendChangedCallback) else: Logger.log("w", "No variant found for printer definition %s with id %s" % (self._global_container_stack.getBottom().getId(), hotend_id)) - def _hotendChangedDialogCallback(self, button): - if button == QMessageBox.No: - self._auto_hotends_changed = {} - return - + def _autoUpdateHotends(self): extruder_manager = ExtruderManager.getInstance() for position in self._auto_hotends_changed: hotend_id = self._auto_hotends_changed[position] @@ -168,17 +164,22 @@ class MachineManager(QObject): if matching_extruder and matching_extruder.findContainer({"type":"material"}).getMetaDataEntry("GUID") != material_id: # Save the material that needs to be changed. Multiple changes will be handled by the callback. self._auto_materials_changed[str(index)] = containers[0].getId() - Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the material to match the material in your printer?"), - catalog.i18nc("@label", "The material on your printer was changed. For best results always slice for the material that is inserted in your printer."), - buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._materialIdChangedDialogCallback) + Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the materials and hotends to match the material in your printer?"), + catalog.i18nc("@label", "The materials and / or hotends on your printer were changed. For best results always slice for the materials . hotends that are inserted in your printer."), + buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._materialHotendChangedCallback) else: Logger.log("w", "No material definition found for printer definition %s and GUID %s" % (definition_id, material_id)) - def _materialIdChangedDialogCallback(self, button): + def _materialHotendChangedCallback(self, button): if button == QMessageBox.No: self._auto_materials_changed = {} + self._auto_hotends_changed = {} return + self._autoUpdateMaterials() + self._autoUpdateHotends() + + def _autoUpdateMaterials(self): extruder_manager = ExtruderManager.getInstance() for position in self._auto_materials_changed: material_id = self._auto_materials_changed[position] From b3f0c257e4c251847c1f5d1882b2fa35cf804bcd Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 26 Jul 2016 13:37:28 +0200 Subject: [PATCH 087/102] Flattend profiles are now set to dirty This fixes issue where profiles used in g-code writer were saved --- plugins/GCodeWriter/GCodeWriter.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index 15de585ae0..11c5dc5f84 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -80,6 +80,7 @@ class GCodeWriter(MeshWriter): flat_quality_id = machine_manager.duplicateContainer(container_with_profile.getId()) flat_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = flat_quality_id)[0] + flat_quality._dirty = False user_settings = stack.getTop() # We don't want to send out any signals, so disconnect them. @@ -97,11 +98,12 @@ class GCodeWriter(MeshWriter): flat_extruder_quality_id = machine_manager.duplicateContainer(extruder_quality.getId()) flat_extruder_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id=flat_extruder_quality_id)[0] + flat_extruder_quality._dirty = False extruder_user_settings = extruder.getTop() # We don't want to send out any signals, so disconnect them. flat_extruder_quality.propertyChanged.disconnectAll() - + for key in extruder_user_settings.getAllKeys(): flat_extruder_quality.setProperty(key, "value", extruder_user_settings.getProperty(key, "value")) From d64eff9947a4a9a116cb302af59e61fe632d28a9 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 26 Jul 2016 18:54:55 +0200 Subject: [PATCH 088/102] Fix updating the Per Object Settings color swatch CURA-1971 --- plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 79f10f508f..dc0574d14d 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -47,7 +47,9 @@ Item { id: extruders_model onRowsInserted: extruderSelector.visible = extruders_model.rowCount() > 1 onModelReset: extruderSelector.visible = extruders_model.rowCount() > 1 + onModelChanged: extruderSelector.color = extruders_model.getItem(extruderSelector.currentIndex).colour } + property string color: extruders_model.getItem(extruderSelector.currentIndex).colour visible: extruders_model.rowCount() > 1 textRole: "name" width: UM.Theme.getSize("setting_control").width @@ -88,7 +90,7 @@ Item { anchors.leftMargin: UM.Theme.getSize("default_lining").width anchors.verticalCenter: parent.verticalCenter - color: extruders_model.getItem(extruderSelector.currentIndex).colour + color: extruderSelector.color border.width: UM.Theme.getSize("default_lining").width border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : UM.Theme.getColor("setting_control_border") } @@ -136,6 +138,7 @@ Item { if(extruders_model.getItem(i).id == UM.ActiveTool.properties.getValue("SelectedActiveExtruder")) { extruderSelector.currentIndex = i; + extruderSelector.color = extruders_model.getItem(i).colour; return; } } From 762ea15e785cef44ab14460c41044a8bc589236c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 27 Jul 2016 09:54:02 +0200 Subject: [PATCH 089/102] Global inherited settings now use target (instead of active) stack to copy values Fixes multiple issues with support settings not being updated properly --- cura/Settings/MachineManager.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index ca09cbc5dc..5bc0ff65fa 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -269,13 +269,16 @@ class MachineManager(QObject): if property_name == "global_inherits_stack": if self._active_container_stack and self._active_container_stack != self._global_container_stack: # Update the global user value when the "global_inherits_stack" function points to a different stack - stack_index = int(self._active_container_stack.getProperty(key, property_name)) - extruder_stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())] + extruder_stacks = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())) + target_stack_position = int(self._active_container_stack.getProperty(key, "global_inherits_stack")) + if target_stack_position == -1: # Prevent -1 from selecting wrong stack. + target_stack = self._active_container_stack + else: + target_stack = extruder_stacks[target_stack_position] - if len(extruder_stacks) > stack_index: - new_value = extruder_stacks[stack_index].getProperty(key, "value") - if self._global_container_stack.getProperty(key, "value") != new_value: - self._global_container_stack.getTop().setProperty(key, "value", new_value) + new_value = target_stack.getProperty(key, "value") + if self._global_container_stack.getProperty(key, "value") != new_value: + self._global_container_stack.getTop().setProperty(key, "value", new_value) if property_name == "validationState": if self._global_stack_valid: @@ -549,7 +552,7 @@ class MachineManager(QObject): return "" @pyqtSlot(str, str) - def renameQualityContainer(self, container_id, new_name): + def renameQualityContainer(self, container_id, nbalew_name): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id, type = "quality") if containers: new_name = self._createUniqueName("quality", containers[0].getName(), new_name, From 1e2147522deab8e4d64e4422dc1095731c8afe61 Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Wed, 27 Jul 2016 11:00:47 +0200 Subject: [PATCH 090/102] Increase the size of the backend log. Contributes to CURA-1509 Cura in slicing loop, Arcus Error (8) --- plugins/CuraEngineBackend/CuraEngineBackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index aedc91f130..bf68a6cb78 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -92,7 +92,7 @@ class CuraEngineBackend(Backend): self._always_restart = True #Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness. self._process_layers_job = None #The currently active job to process layers, or None if it is not processing layers. - self._backend_log_max_lines = 200 # Maximal count of lines to buffer + self._backend_log_max_lines = 20000 # Maximum number of lines to buffer self._error_message = None #Pop-up message that shows errors. self.backendQuit.connect(self._onBackendQuit) From 4175e51dbb1f8707c05910872c0dd628baacce62 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 27 Jul 2016 14:00:42 +0200 Subject: [PATCH 091/102] Corrected gantry height for ultimaker2.json (extended and go inherit). CURA-1979 Made same change in resources/definitions/ultimaker2.def.json as in the original resources/machines/ultimaker2.json --- resources/definitions/ultimaker2.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/ultimaker2.def.json b/resources/definitions/ultimaker2.def.json index 3712963f23..d8e73a7e14 100644 --- a/resources/definitions/ultimaker2.def.json +++ b/resources/definitions/ultimaker2.def.json @@ -57,7 +57,7 @@ "default_value": 2 }, "gantry_height": { - "default_value": 55 + "default_value": 48 }, "machine_use_extruder_offset_to_offset_coords": { "default_value": true From afed6eed93e2ebb11bb0183ca08aa6e8d4fef8f1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 27 Jul 2016 14:14:13 +0200 Subject: [PATCH 092/102] Print montior also handles wait_for_cleanup correctly now --- resources/qml/MonitorButton.qml | 6 +++++- resources/qml/Sidebar.qml | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index af163a39e2..7a87eb4f60 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -22,7 +22,7 @@ Rectangle { if(!printerConnected) return UM.Theme.getColor("status_offline") - else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print") + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print" || Cura.MachineManager.printerOutputDevices[0].jobState == "wait_cleanup" ) return UM.Theme.getColor("status_busy") else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready" || Cura.MachineManager.printerOutputDevices[0].jobState == "") return UM.Theme.getColor("status_ready") @@ -53,6 +53,10 @@ Rectangle { return catalog.i18nc("@label:", "Preparing...") } + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "wait_cleanup") + { + return catalog.i18nc("@label:", "Waiting for cleanup...") + } else { return " " diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 53a2375394..1542e24f5d 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -108,7 +108,7 @@ Rectangle iconSource: { if(!printerConnected) return UM.Theme.getIcon("tab_monitor") - else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print") + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print" || Cura.MachineManager.printerOutputDevices[0].jobState == "wait_cleanup" ) return UM.Theme.getIcon("tab_monitor_busy") else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready" || Cura.MachineManager.printerOutputDevices[0].jobState == "") return UM.Theme.getIcon("tab_monitor_connected") From cd26794155ee73ea6b10bfb7e179c23892e52afb Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Wed, 27 Jul 2016 17:27:57 +0200 Subject: [PATCH 093/102] JSON fix: retrieve globalish support settings from support extruder (CURA-2003) --- resources/definitions/fdmprinter.def.json | 47 +++++++++-------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 950c856a6e..f8bd162c27 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -630,9 +630,8 @@ "type": "float", "enabled": "support_enable", "value": "line_width", - "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "support_roof_line_width": { @@ -645,9 +644,8 @@ "type": "float", "enabled": "support_roof_enable", "value": "line_width", - "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "prime_tower_line_width": { @@ -1444,10 +1442,9 @@ "maximum_value_warning": "150", "default_value": 60, "value": "speed_print", - "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, - "settable_per_extruder": false, + "settable_per_extruder": true, "children": { "speed_support_infill": @@ -1461,10 +1458,9 @@ "maximum_value": "299792458000", "maximum_value_warning": "150", "value": "speed_support", - "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "speed_support_roof": { @@ -1478,9 +1474,8 @@ "maximum_value_warning": "150", "enabled": "support_roof_enable and support_enable", "value": "speed_support / 1.5", - "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true } } }, @@ -1650,10 +1645,9 @@ "maximum_value_warning": "10000", "default_value": 3000, "value": "acceleration_print", - "global_inherits_stack": "support_extruder_nr", "enabled": "acceleration_enabled and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false, + "settable_per_extruder": true, "children": { "acceleration_support_infill": { "label": "Support Infill Acceleration", @@ -1662,13 +1656,12 @@ "type": "float", "default_value": 3000, "value": "acceleration_support", - "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "100", "maximum_value_warning": "10000", "enabled": "acceleration_enabled and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "acceleration_support_roof": { "label": "Support Roof Acceleration", @@ -1677,13 +1670,12 @@ "type": "float", "default_value": 3000, "value": "acceleration_support", - "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "100", "maximum_value_warning": "10000", "enabled": "acceleration_enabled and support_roof_enable and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true } } }, @@ -1841,10 +1833,9 @@ "maximum_value_warning": "50", "default_value": 20, "value": "jerk_print", - "global_inherits_stack": "support_extruder_nr", "enabled": "jerk_enabled and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false, + "settable_per_extruder": true, "children": { "jerk_support_infill": { "label": "Support Infill Jerk", @@ -1853,13 +1844,12 @@ "type": "float", "default_value": 20, "value": "jerk_support", - "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "5", "maximum_value_warning": "50", "enabled": "jerk_enabled and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "jerk_support_roof": { "label": "Support Roof Jerk", @@ -1868,13 +1858,12 @@ "type": "float", "default_value": 20, "value": "jerk_support", - "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "5", "maximum_value_warning": "50", "enabled": "jerk_enabled and support_roof_enable and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true } } }, @@ -2180,7 +2169,7 @@ "default_value": "zigzag", "enabled": "support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "support_connect_zigzags": { @@ -2190,7 +2179,7 @@ "default_value": true, "enabled": "support_enable and (support_pattern == \"zigzag\")", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "support_infill_rate": { @@ -2203,7 +2192,7 @@ "default_value": 15, "enabled": "support_enable", "settable_per_mesh": false, - "settable_per_extruder": false, + "settable_per_extruder": true, "children": { "support_line_distance": { @@ -2216,7 +2205,7 @@ "enabled": "support_enable", "value": "(support_line_width * 100) / support_infill_rate * (2 if support_pattern == \"grid\" else (3 if support_pattern == \"triangles\" else 1))", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true } } }, @@ -2389,7 +2378,7 @@ "maximum_value_warning": "100", "enabled":"support_roof_enable and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false, + "settable_per_extruder": true, "children": { "support_roof_line_distance": @@ -2403,7 +2392,7 @@ "value": "0 if support_roof_density == 0 else (support_roof_line_width * 100) / support_roof_density * (2 if support_roof_pattern == \"grid\" else (3 if support_roof_pattern == \"triangles\" else 1))", "enabled": "support_roof_enable and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true } } }, @@ -2423,7 +2412,7 @@ "default_value": "concentric", "enabled": "support_roof_enable and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "support_use_towers": { From bac3f73e6db141554af50bf6b281c429da47c5a1 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 27 Jul 2016 17:42:15 +0200 Subject: [PATCH 094/102] Check ConvexHullHead vs ConvexHullHead collision. CURA-1776 --- cura/PlatformPhysics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index d2a848dd72..91d2b1a1ef 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -95,11 +95,11 @@ class PlatformPhysics: # Get the overlap distance for both convex hulls. If this returns None, there is no intersection. head_hull = node.callDecoration("getConvexHullHead") if head_hull: - overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHull")) + overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHullHead")) if not overlap: other_head_hull = other_node.callDecoration("getConvexHullHead") if other_head_hull: - overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_head_hull) + overlap = node.callDecoration("getConvexHullHead").intersectsPolygon(other_head_hull) else: own_convex_hull = node.callDecoration("getConvexHull") other_convex_hull = other_node.callDecoration("getConvexHull") From 6af0f28f32e776e971d04cd3e15b01f75319425d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 27 Jul 2016 17:50:25 +0200 Subject: [PATCH 095/102] Only show draft shield height if draft shield enabled We must've missed that one previously. Contributes to issue CURA-1295. --- resources/definitions/fdmprinter.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index f8bd162c27..2df6c295a4 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -3348,7 +3348,7 @@ "maximum_value_warning": "9999", "default_value": 0, "value": "9999 if draft_shield_height_limitation == 'full' and draft_shield_enabled else 0.0", - "enabled": "draft_shield_height_limitation == \"limited\"", + "enabled": "draft_shield_enabled and draft_shield_height_limitation == \"limited\"", "settable_per_mesh": false, "settable_per_extruder": false }, From 823ab61cc577eb072a61cb3bd7cb524e1b4561d3 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 27 Jul 2016 18:02:31 +0200 Subject: [PATCH 096/102] Tweak convex hull node and transparent object shader Now we render at least a vague hint at lighting instead of nothing at all. --- cura/ConvexHullNode.py | 2 +- resources/shaders/transparent_object.shader | 34 ++++++++------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/cura/ConvexHullNode.py b/cura/ConvexHullNode.py index 703dfb0bed..232b30e317 100644 --- a/cura/ConvexHullNode.py +++ b/cura/ConvexHullNode.py @@ -23,7 +23,7 @@ class ConvexHullNode(SceneNode): self._original_parent = parent # Color of the drawn convex hull - self._color = Color(35, 35, 35, 192) + self._color = Color(0.4, 0.4, 0.4, 1.0) # The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting. self._mesh_height = 0.1 diff --git a/resources/shaders/transparent_object.shader b/resources/shaders/transparent_object.shader index a3790901bc..cd27a40769 100644 --- a/resources/shaders/transparent_object.shader +++ b/resources/shaders/transparent_object.shader @@ -1,7 +1,7 @@ [shaders] vertex = - uniform highp mat4 u_viewProjectionMatrix; uniform highp mat4 u_modelMatrix; + uniform highp mat4 u_viewProjectionMatrix; uniform highp mat4 u_normalMatrix; attribute highp vec4 a_vertex; @@ -10,7 +10,6 @@ vertex = varying highp vec3 v_vertex; varying highp vec3 v_normal; - varying highp vec2 v_uvs; void main() { @@ -19,56 +18,47 @@ vertex = v_vertex = world_space_vert.xyz; v_normal = (u_normalMatrix * normalize(a_normal)).xyz; - - v_uvs = a_uvs; } fragment = uniform mediump vec4 u_ambientColor; uniform mediump vec4 u_diffuseColor; uniform highp vec3 u_lightPosition; - uniform highp vec3 u_viewPosition; + uniform mediump float u_opacity; - uniform sampler2D u_texture; varying highp vec3 v_vertex; varying highp vec3 v_normal; - varying highp vec2 v_uvs; void main() { - // Copied from platform.shader, removed texture - mediump vec4 final_color = vec4(0.0); + mediump vec4 finalColor = vec4(0.0); /* Ambient Component */ - final_color += u_ambientColor; + finalColor += u_ambientColor; highp vec3 normal = normalize(v_normal); - highp vec3 light_dir = normalize(u_lightPosition - v_vertex); + highp vec3 lightDir = normalize(u_lightPosition - v_vertex); /* Diffuse Component */ - highp float n_dot_l = clamp(dot(normal, light_dir), 0.0, 1.0); - final_color += (n_dot_l * u_diffuseColor); + highp float NdotL = clamp(abs(dot(normal, lightDir)), 0.0, 1.0); + finalColor += (NdotL * u_diffuseColor); - final_color.a = u_opacity; - - gl_FragColor = final_color; + gl_FragColor = finalColor; + gl_FragColor.a = u_opacity; } [defaults] -u_ambientColor = [0.3, 0.3, 0.3, 1.0] -u_diffuseColor = [1.0, 1.0, 1.0, 1.0] +u_ambientColor = [0.1, 0.1, 0.1, 1.0] +u_diffuseColor = [0.4, 0.4, 0.4, 1.0] u_opacity = 0.5 -u_texture = 0 [bindings] -u_viewProjectionMatrix = view_projection_matrix u_modelMatrix = model_matrix +u_viewProjectionMatrix = view_projection_matrix u_normalMatrix = normal_matrix u_lightPosition = light_0_position -u_viewPosition = camera_position [attributes] a_vertex = vertex a_normal = normal -a_uvs = uv0 From 7971ffdc0828377810e3c36fc93408a344cf8fc3 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 27 Jul 2016 18:02:50 +0200 Subject: [PATCH 097/102] Properly ignore Arcus debug messages --- plugins/CuraEngineBackend/CuraEngineBackend.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index bf68a6cb78..aa6f2b0807 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -252,6 +252,9 @@ class CuraEngineBackend(Backend): return super()._onSocketError(error) + if error.getErrorCode() == Arcus.ErrorCode.Debug: + return + self._terminate() if error.getErrorCode() not in [Arcus.ErrorCode.BindFailedError, Arcus.ErrorCode.ConnectionResetError, Arcus.ErrorCode.Debug]: From b9857a697576db4a311c792bb57130f03f7c9904 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 26 Jul 2016 17:10:17 +0200 Subject: [PATCH 098/102] Partially solve setting the UMO heated bed kit option CURA-580 --- plugins/UltimakerMachineActions/UMOUpgradeSelection.py | 3 +++ .../UMOUpgradeSelectionMachineAction.qml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py index 2e13ec50d5..5d00fd5e3a 100644 --- a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py @@ -12,6 +12,9 @@ class UMOUpgradeSelection(MachineAction): super().__init__("UMOUpgradeSelection", catalog.i18nc("@action", "Select upgrades")) self._qml_url = "UMOUpgradeSelectionMachineAction.qml" + def _reset(self): + self.heatedBedChanged.emit() + heatedBedChanged = pyqtSignal() @pyqtProperty(bool, notify = heatedBedChanged) diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml index d960bcf976..234e53c0a6 100644 --- a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml @@ -44,7 +44,7 @@ Cura.MachineAction text: catalog.i18nc("@label", "Heated bed (official kit or self-built)") checked: manager.hasHeatedBed - onClicked: manager.hasHeatedBed ? manager.removeHeatedBed() : manager.addHeatedBed() + onClicked: checked ? manager.addHeatedBed() : manager.removeHeatedBed() } UM.I18nCatalog { id: catalog; name: "cura"; } From dc6d415cf5b2e5a0ac49e207f4159e16fbf275fe Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 28 Jul 2016 09:13:35 +0200 Subject: [PATCH 099/102] Disabled duplicate material, because it has a lot of issues. CURA-1969. --- resources/qml/Preferences/MaterialsPage.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index aaaa4f5e9d..9da5522762 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -67,6 +67,8 @@ UM.ManagementPage enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId onClicked: Cura.MachineManager.setActiveMaterial(base.currentItem.id) }, + /* + // disabled because it has a lot of issues Button { text: catalog.i18nc("@action:button", "Duplicate"); @@ -89,7 +91,7 @@ UM.ManagementPage Cura.MachineManager.setActiveMaterial(material_id) } - }, + }, */ Button { text: catalog.i18nc("@action:button", "Remove"); From 1147e915c19f033e632329df63a4174a83b45d28 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 28 Jul 2016 09:27:20 +0200 Subject: [PATCH 100/102] Setting a instance to the same state no longer causes display to break CURA-1985 --- plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index dc0574d14d..75cf522c14 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -246,6 +246,7 @@ Item { key: model.key watchedProperties: [ "value", "enabled", "validationState" ] storeIndex: 0 + removeUnusedValue: false } } } From c6d1caaf0f0c64da1d9d2b0abcd92964847f5ba3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 28 Jul 2016 11:47:56 +0200 Subject: [PATCH 101/102] Convex hull is no longer needlessly re-calculated CURA-2002 --- cura/ConvexHullDecorator.py | 5 +++-- cura/PlatformPhysics.py | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py index d864f4288b..783d74e41b 100644 --- a/cura/ConvexHullDecorator.py +++ b/cura/ConvexHullDecorator.py @@ -30,9 +30,10 @@ class ConvexHullDecorator(SceneNodeDecorator): def setNode(self, node): previous_node = self._node + # Disconnect from previous node signals if previous_node is not None and node is not previous_node: - previous_node.transformationChanged.connect(self._onChanged) - previous_node.parentChanged.connect(self._onChanged) + previous_node.transformationChanged.disconnect(self._onChanged) + previous_node.parentChanged.disconnect(self._onChanged) super().setNode(node) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 91d2b1a1ef..56daaddc18 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -71,7 +71,6 @@ class PlatformPhysics: # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) - node.callDecoration("recomputeConvexHull") if Preferences.getInstance().getValue("physics/automatic_push_free"): # Check for collisions between convex hulls From cab1dc7a13416d1a72592962e1708ad365702051 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 28 Jul 2016 12:35:36 +0200 Subject: [PATCH 102/102] Fix merge mistake --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a6d0a3b827..1142783c9c 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -98,7 +98,7 @@ class CuraApplication(QtApplication): SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("global_inherits_stack", DefinitionPropertyType.Function, default = "-1") - SettingDefinition.addSettingType("extruder", int, str, Validator) + SettingDefinition.addSettingType("extruder", None, str, Validator) ## Add the 4 types of profiles to storage. Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality")