mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-07 06:57:28 -06:00
Merge branch 'print_simulation_view'
This commit is contained in:
commit
ed3d8266b3
33 changed files with 2642 additions and 1190 deletions
|
@ -218,7 +218,7 @@ class CuraApplication(QtApplication):
|
||||||
"CuraEngineBackend",
|
"CuraEngineBackend",
|
||||||
"UserAgreement",
|
"UserAgreement",
|
||||||
"SolidView",
|
"SolidView",
|
||||||
"LayerView",
|
"SimulationView",
|
||||||
"STLReader",
|
"STLReader",
|
||||||
"SelectionTool",
|
"SelectionTool",
|
||||||
"CameraTool",
|
"CameraTool",
|
||||||
|
@ -1386,7 +1386,7 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
extension = os.path.splitext(filename)[1]
|
extension = os.path.splitext(filename)[1]
|
||||||
if extension.lower() in self._non_sliceable_extensions:
|
if extension.lower() in self._non_sliceable_extensions:
|
||||||
self.getController().setActiveView("LayerView")
|
self.getController().setActiveView("SimulationView")
|
||||||
view = self.getController().getActiveView()
|
view = self.getController().getActiveView()
|
||||||
view.resetLayerData()
|
view.resetLayerData()
|
||||||
view.setLayer(9999999)
|
view.setLayer(9999999)
|
||||||
|
|
|
@ -47,12 +47,12 @@ class Layer:
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def build(self, vertex_offset, index_offset, vertices, colors, line_dimensions, extruders, line_types, indices):
|
def build(self, vertex_offset, index_offset, vertices, colors, line_dimensions, feedrates, extruders, line_types, indices):
|
||||||
result_vertex_offset = vertex_offset
|
result_vertex_offset = vertex_offset
|
||||||
result_index_offset = index_offset
|
result_index_offset = index_offset
|
||||||
self._element_count = 0
|
self._element_count = 0
|
||||||
for polygon in self._polygons:
|
for polygon in self._polygons:
|
||||||
polygon.build(result_vertex_offset, result_index_offset, vertices, colors, line_dimensions, extruders, line_types, indices)
|
polygon.build(result_vertex_offset, result_index_offset, vertices, colors, line_dimensions, feedrates, extruders, line_types, indices)
|
||||||
result_vertex_offset += polygon.lineMeshVertexCount()
|
result_vertex_offset += polygon.lineMeshVertexCount()
|
||||||
result_index_offset += polygon.lineMeshElementCount()
|
result_index_offset += polygon.lineMeshElementCount()
|
||||||
self._element_count += polygon.elementCount
|
self._element_count += polygon.elementCount
|
||||||
|
|
|
@ -20,11 +20,11 @@ class LayerDataBuilder(MeshBuilder):
|
||||||
if layer not in self._layers:
|
if layer not in self._layers:
|
||||||
self._layers[layer] = Layer(layer)
|
self._layers[layer] = Layer(layer)
|
||||||
|
|
||||||
def addPolygon(self, layer, polygon_type, data, line_width):
|
def addPolygon(self, layer, polygon_type, data, line_width, line_thickness, line_feedrate):
|
||||||
if layer not in self._layers:
|
if layer not in self._layers:
|
||||||
self.addLayer(layer)
|
self.addLayer(layer)
|
||||||
|
|
||||||
p = LayerPolygon(self, polygon_type, data, line_width)
|
p = LayerPolygon(self, polygon_type, data, line_width, line_thickness, line_feedrate)
|
||||||
self._layers[layer].polygons.append(p)
|
self._layers[layer].polygons.append(p)
|
||||||
|
|
||||||
def getLayer(self, layer):
|
def getLayer(self, layer):
|
||||||
|
@ -64,13 +64,14 @@ class LayerDataBuilder(MeshBuilder):
|
||||||
line_dimensions = numpy.empty((vertex_count, 2), numpy.float32)
|
line_dimensions = numpy.empty((vertex_count, 2), numpy.float32)
|
||||||
colors = numpy.empty((vertex_count, 4), numpy.float32)
|
colors = numpy.empty((vertex_count, 4), numpy.float32)
|
||||||
indices = numpy.empty((index_count, 2), numpy.int32)
|
indices = numpy.empty((index_count, 2), numpy.int32)
|
||||||
|
feedrates = numpy.empty((vertex_count), numpy.float32)
|
||||||
extruders = numpy.empty((vertex_count), numpy.float32)
|
extruders = numpy.empty((vertex_count), numpy.float32)
|
||||||
line_types = numpy.empty((vertex_count), numpy.float32)
|
line_types = numpy.empty((vertex_count), numpy.float32)
|
||||||
|
|
||||||
vertex_offset = 0
|
vertex_offset = 0
|
||||||
index_offset = 0
|
index_offset = 0
|
||||||
for layer, data in sorted(self._layers.items()):
|
for layer, data in sorted(self._layers.items()):
|
||||||
( vertex_offset, index_offset ) = data.build( vertex_offset, index_offset, vertices, colors, line_dimensions, extruders, line_types, indices)
|
( vertex_offset, index_offset ) = data.build( vertex_offset, index_offset, vertices, colors, line_dimensions, feedrates, extruders, line_types, indices)
|
||||||
self._element_counts[layer] = data.elementCount
|
self._element_counts[layer] = data.elementCount
|
||||||
|
|
||||||
self.addVertices(vertices)
|
self.addVertices(vertices)
|
||||||
|
@ -107,6 +108,11 @@ class LayerDataBuilder(MeshBuilder):
|
||||||
"value": line_types,
|
"value": line_types,
|
||||||
"opengl_name": "a_line_type",
|
"opengl_name": "a_line_type",
|
||||||
"opengl_type": "float"
|
"opengl_type": "float"
|
||||||
|
},
|
||||||
|
"feedrates": {
|
||||||
|
"value": feedrates,
|
||||||
|
"opengl_name": "a_feedrate",
|
||||||
|
"opengl_type": "float"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,8 @@ class LayerPolygon:
|
||||||
# \param data new_points
|
# \param data new_points
|
||||||
# \param line_widths array with line widths
|
# \param line_widths array with line widths
|
||||||
# \param line_thicknesses: array with type as index and thickness as value
|
# \param line_thicknesses: array with type as index and thickness as value
|
||||||
def __init__(self, extruder, line_types, data, line_widths, line_thicknesses):
|
# \param line_feedrates array with line feedrates
|
||||||
|
def __init__(self, extruder, line_types, data, line_widths, line_thicknesses, line_feedrates):
|
||||||
self._extruder = extruder
|
self._extruder = extruder
|
||||||
self._types = line_types
|
self._types = line_types
|
||||||
for i in range(len(self._types)):
|
for i in range(len(self._types)):
|
||||||
|
@ -37,6 +38,7 @@ class LayerPolygon:
|
||||||
self._data = data
|
self._data = data
|
||||||
self._line_widths = line_widths
|
self._line_widths = line_widths
|
||||||
self._line_thicknesses = line_thicknesses
|
self._line_thicknesses = line_thicknesses
|
||||||
|
self._line_feedrates = line_feedrates
|
||||||
|
|
||||||
self._vertex_begin = 0
|
self._vertex_begin = 0
|
||||||
self._vertex_end = 0
|
self._vertex_end = 0
|
||||||
|
@ -84,10 +86,11 @@ class LayerPolygon:
|
||||||
# \param vertices : vertex numpy array to be filled
|
# \param vertices : vertex numpy array to be filled
|
||||||
# \param colors : vertex numpy array to be filled
|
# \param colors : vertex numpy array to be filled
|
||||||
# \param line_dimensions : vertex numpy array to be filled
|
# \param line_dimensions : vertex numpy array to be filled
|
||||||
|
# \param feedrates : vertex numpy array to be filled
|
||||||
# \param extruders : vertex numpy array to be filled
|
# \param extruders : vertex numpy array to be filled
|
||||||
# \param line_types : vertex numpy array to be filled
|
# \param line_types : vertex numpy array to be filled
|
||||||
# \param indices : index numpy array to be filled
|
# \param indices : index numpy array to be filled
|
||||||
def build(self, vertex_offset, index_offset, vertices, colors, line_dimensions, extruders, line_types, indices):
|
def build(self, vertex_offset, index_offset, vertices, colors, line_dimensions, feedrates, extruders, line_types, indices):
|
||||||
if self._build_cache_line_mesh_mask is None or self._build_cache_needed_points is None:
|
if self._build_cache_line_mesh_mask is None or self._build_cache_needed_points is None:
|
||||||
self.buildCache()
|
self.buildCache()
|
||||||
|
|
||||||
|
@ -109,10 +112,13 @@ class LayerPolygon:
|
||||||
# Create an array with colors for each vertex and remove the color data for the points that has been thrown away.
|
# 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.tile(self._colors, (1, 2)).reshape((-1, 4))[needed_points_list.ravel()]
|
||||||
|
|
||||||
# Create an array with line widths for each vertex.
|
# Create an array with line widths and thicknesses for each vertex.
|
||||||
line_dimensions[self._vertex_begin:self._vertex_end, 0] = numpy.tile(self._line_widths, (1, 2)).reshape((-1, 1))[needed_points_list.ravel()][:, 0]
|
line_dimensions[self._vertex_begin:self._vertex_end, 0] = numpy.tile(self._line_widths, (1, 2)).reshape((-1, 1))[needed_points_list.ravel()][:, 0]
|
||||||
line_dimensions[self._vertex_begin:self._vertex_end, 1] = numpy.tile(self._line_thicknesses, (1, 2)).reshape((-1, 1))[needed_points_list.ravel()][:, 0]
|
line_dimensions[self._vertex_begin:self._vertex_end, 1] = numpy.tile(self._line_thicknesses, (1, 2)).reshape((-1, 1))[needed_points_list.ravel()][:, 0]
|
||||||
|
|
||||||
|
# Create an array with feedrates for each line
|
||||||
|
feedrates[self._vertex_begin:self._vertex_end] = numpy.tile(self._line_feedrates, (1, 2)).reshape((-1, 1))[needed_points_list.ravel()][:, 0]
|
||||||
|
|
||||||
extruders[self._vertex_begin:self._vertex_end] = self._extruder
|
extruders[self._vertex_begin:self._vertex_end] = self._extruder
|
||||||
|
|
||||||
# Convert type per vertex to type per line
|
# Convert type per vertex to type per line
|
||||||
|
@ -166,6 +172,14 @@ class LayerPolygon:
|
||||||
@property
|
@property
|
||||||
def lineWidths(self):
|
def lineWidths(self):
|
||||||
return self._line_widths
|
return self._line_widths
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lineThicknesses(self):
|
||||||
|
return self._line_thicknesses
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lineFeedrates(self):
|
||||||
|
return self._line_feedrates
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def jumpMask(self):
|
def jumpMask(self):
|
||||||
|
|
|
@ -61,6 +61,8 @@ message Polygon {
|
||||||
Type type = 1; // Type of move
|
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)
|
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
|
float line_width = 3; // The width of the line being laid down
|
||||||
|
float line_thickness = 4; // The thickness of the line being laid down
|
||||||
|
float line_feedrate = 5; // The feedrate of the line being laid down
|
||||||
}
|
}
|
||||||
|
|
||||||
message LayerOptimized {
|
message LayerOptimized {
|
||||||
|
@ -82,6 +84,8 @@ message PathSegment {
|
||||||
bytes points = 3; // The points defining the line segments, bytes of float[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_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
|
||||||
|
bytes line_thickness = 6; // The thickness of the line segments as bytes of a float array of length 1 or N
|
||||||
|
bytes line_feedrate = 7; // The feedrate of the line segments as bytes of a float array of length 1 or N
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -608,7 +608,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
def _onActiveViewChanged(self):
|
def _onActiveViewChanged(self):
|
||||||
if Application.getInstance().getController().getActiveView():
|
if Application.getInstance().getController().getActiveView():
|
||||||
view = Application.getInstance().getController().getActiveView()
|
view = Application.getInstance().getController().getActiveView()
|
||||||
if view.getPluginId() == "LayerView": # If switching to layer view, we should process the layers if that hasn't been done yet.
|
if view.getPluginId() == "SimulationView": # If switching to layer view, we should process the layers if that hasn't been done yet.
|
||||||
self._layer_view_active = True
|
self._layer_view_active = True
|
||||||
# There is data and we're not slicing at the moment
|
# 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 we are slicing, there is no need to re-calculate the data as it will be invalid in a moment.
|
||||||
|
|
|
@ -61,7 +61,7 @@ class ProcessSlicedLayersJob(Job):
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
start_time = time()
|
start_time = time()
|
||||||
if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
|
if Application.getInstance().getController().getActiveView().getPluginId() == "SimulationView":
|
||||||
self._progress_message.show()
|
self._progress_message.show()
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
if self._abort_requested:
|
if self._abort_requested:
|
||||||
|
@ -109,6 +109,7 @@ class ProcessSlicedLayersJob(Job):
|
||||||
layer_data.addLayer(abs_layer_number)
|
layer_data.addLayer(abs_layer_number)
|
||||||
this_layer = layer_data.getLayer(abs_layer_number)
|
this_layer = layer_data.getLayer(abs_layer_number)
|
||||||
layer_data.setLayerHeight(abs_layer_number, layer.height)
|
layer_data.setLayerHeight(abs_layer_number, layer.height)
|
||||||
|
layer_data.setLayerThickness(abs_layer_number, layer.thickness)
|
||||||
|
|
||||||
for p in range(layer.repeatedMessageCount("path_segment")):
|
for p in range(layer.repeatedMessageCount("path_segment")):
|
||||||
polygon = layer.getRepeatedMessage("path_segment", p)
|
polygon = layer.getRepeatedMessage("path_segment", p)
|
||||||
|
@ -127,10 +128,11 @@ class ProcessSlicedLayersJob(Job):
|
||||||
line_widths = numpy.fromstring(polygon.line_width, dtype="f4") # 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.
|
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.
|
||||||
|
|
||||||
# In the future, line_thicknesses should be given by CuraEngine as well.
|
line_thicknesses = numpy.fromstring(polygon.line_thickness, dtype="f4") # Convert bytearray to numpy array
|
||||||
# Currently the infill layer thickness also translates to line width
|
line_thicknesses = line_thicknesses.reshape((-1,1)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
|
||||||
line_thicknesses = numpy.zeros(line_widths.shape, dtype="f4")
|
|
||||||
line_thicknesses[:] = layer.thickness / 1000 # from micrometer to millimeter
|
line_feedrates = numpy.fromstring(polygon.line_feedrate, dtype="f4") # Convert bytearray to numpy array
|
||||||
|
line_feedrates = line_feedrates.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.
|
# 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
|
# This uses manual array creation + copy rather than numpy.insert since this is
|
||||||
|
@ -145,7 +147,7 @@ class ProcessSlicedLayersJob(Job):
|
||||||
new_points[:, 1] = points[:, 2]
|
new_points[:, 1] = points[:, 2]
|
||||||
new_points[:, 2] = -points[:, 1]
|
new_points[:, 2] = -points[:, 1]
|
||||||
|
|
||||||
this_poly = LayerPolygon.LayerPolygon(extruder, line_types, new_points, line_widths, line_thicknesses)
|
this_poly = LayerPolygon.LayerPolygon(extruder, line_types, new_points, line_widths, line_thicknesses, line_feedrates)
|
||||||
this_poly.buildCache()
|
this_poly.buildCache()
|
||||||
|
|
||||||
this_layer.polygons.append(this_poly)
|
this_layer.polygons.append(this_poly)
|
||||||
|
@ -219,7 +221,7 @@ class ProcessSlicedLayersJob(Job):
|
||||||
self._progress_message.setProgress(100)
|
self._progress_message.setProgress(100)
|
||||||
|
|
||||||
view = Application.getInstance().getController().getActiveView()
|
view = Application.getInstance().getController().getActiveView()
|
||||||
if view.getPluginId() == "LayerView":
|
if view.getPluginId() == "SimulationView":
|
||||||
view.resetLayerData()
|
view.resetLayerData()
|
||||||
|
|
||||||
if self._progress_message:
|
if self._progress_message:
|
||||||
|
@ -232,7 +234,7 @@ class ProcessSlicedLayersJob(Job):
|
||||||
|
|
||||||
def _onActiveViewChanged(self):
|
def _onActiveViewChanged(self):
|
||||||
if self.isRunning():
|
if self.isRunning():
|
||||||
if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
|
if Application.getInstance().getController().getActiveView().getPluginId() == "SimulationView":
|
||||||
if not self._progress_message:
|
if not self._progress_message:
|
||||||
self._progress_message = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, 0, catalog.i18nc("@info:title", "Information"))
|
self._progress_message = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, 0, catalog.i18nc("@info:title", "Information"))
|
||||||
if self._progress_message.getProgress() != 100:
|
if self._progress_message.getProgress() != 100:
|
||||||
|
|
|
@ -40,7 +40,8 @@ class GCodeReader(MeshReader):
|
||||||
self._extruder_number = 0
|
self._extruder_number = 0
|
||||||
self._clearValues()
|
self._clearValues()
|
||||||
self._scene_node = None
|
self._scene_node = None
|
||||||
self._position = namedtuple('Position', ['x', 'y', 'z', 'e'])
|
# X, Y, Z position, F feedrate and E extruder values are stored
|
||||||
|
self._position = namedtuple('Position', ['x', 'y', 'z', 'f', 'e'])
|
||||||
self._is_layers_in_file = False # Does the Gcode have the layers comment?
|
self._is_layers_in_file = False # Does the Gcode have the layers comment?
|
||||||
self._extruder_offsets = {} # Offsets for multi extruders. key is index, value is [x-offset, y-offset]
|
self._extruder_offsets = {} # Offsets for multi extruders. key is index, value is [x-offset, y-offset]
|
||||||
self._current_layer_thickness = 0.2 # default
|
self._current_layer_thickness = 0.2 # default
|
||||||
|
@ -48,7 +49,9 @@ class GCodeReader(MeshReader):
|
||||||
Preferences.getInstance().addPreference("gcodereader/show_caution", True)
|
Preferences.getInstance().addPreference("gcodereader/show_caution", True)
|
||||||
|
|
||||||
def _clearValues(self):
|
def _clearValues(self):
|
||||||
|
self._filament_diameter = 2.85
|
||||||
self._extruder_number = 0
|
self._extruder_number = 0
|
||||||
|
self._extrusion_length_offset = [0]
|
||||||
self._layer_type = LayerPolygon.Inset0Type
|
self._layer_type = LayerPolygon.Inset0Type
|
||||||
self._layer_number = 0
|
self._layer_number = 0
|
||||||
self._previous_z = 0
|
self._previous_z = 0
|
||||||
|
@ -97,7 +100,7 @@ class GCodeReader(MeshReader):
|
||||||
def _createPolygon(self, layer_thickness, path, extruder_offsets):
|
def _createPolygon(self, layer_thickness, path, extruder_offsets):
|
||||||
countvalid = 0
|
countvalid = 0
|
||||||
for point in path:
|
for point in path:
|
||||||
if point[3] > 0:
|
if point[5] > 0:
|
||||||
countvalid += 1
|
countvalid += 1
|
||||||
if countvalid >= 2:
|
if countvalid >= 2:
|
||||||
# we know what to do now, no need to count further
|
# we know what to do now, no need to count further
|
||||||
|
@ -115,27 +118,56 @@ class GCodeReader(MeshReader):
|
||||||
line_types = numpy.empty((count - 1, 1), numpy.int32)
|
line_types = numpy.empty((count - 1, 1), numpy.int32)
|
||||||
line_widths = numpy.empty((count - 1, 1), numpy.float32)
|
line_widths = numpy.empty((count - 1, 1), numpy.float32)
|
||||||
line_thicknesses = numpy.empty((count - 1, 1), numpy.float32)
|
line_thicknesses = numpy.empty((count - 1, 1), numpy.float32)
|
||||||
# TODO: need to calculate actual line width based on E values
|
line_feedrates = numpy.empty((count - 1, 1), numpy.float32)
|
||||||
line_widths[:, 0] = 0.35 # Just a guess
|
line_widths[:, 0] = 0.35 # Just a guess
|
||||||
line_thicknesses[:, 0] = layer_thickness
|
line_thicknesses[:, 0] = layer_thickness
|
||||||
points = numpy.empty((count, 3), numpy.float32)
|
points = numpy.empty((count, 3), numpy.float32)
|
||||||
|
extrusion_values = numpy.empty((count, 1), numpy.float32)
|
||||||
i = 0
|
i = 0
|
||||||
for point in path:
|
for point in path:
|
||||||
points[i, :] = [point[0] + extruder_offsets[0], point[2], -point[1] - extruder_offsets[1]]
|
points[i, :] = [point[0] + extruder_offsets[0], point[2], -point[1] - extruder_offsets[1]]
|
||||||
|
extrusion_values[i] = point[4]
|
||||||
if i > 0:
|
if i > 0:
|
||||||
line_types[i - 1] = point[3]
|
line_feedrates[i - 1] = point[3]
|
||||||
if point[3] in [LayerPolygon.MoveCombingType, LayerPolygon.MoveRetractionType]:
|
line_types[i - 1] = point[5]
|
||||||
|
if point[5] in [LayerPolygon.MoveCombingType, LayerPolygon.MoveRetractionType]:
|
||||||
line_widths[i - 1] = 0.1
|
line_widths[i - 1] = 0.1
|
||||||
|
line_thicknesses[i - 1] = 0.0 # Travels are set as zero thickness lines
|
||||||
|
else:
|
||||||
|
line_widths[i - 1] = self._calculateLineWidth(points[i], points[i-1], extrusion_values[i], extrusion_values[i-1], layer_thickness)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
this_poly = LayerPolygon(self._extruder_number, line_types, points, line_widths, line_thicknesses)
|
this_poly = LayerPolygon(self._extruder_number, line_types, points, line_widths, line_thicknesses, line_feedrates)
|
||||||
this_poly.buildCache()
|
this_poly.buildCache()
|
||||||
|
|
||||||
this_layer.polygons.append(this_poly)
|
this_layer.polygons.append(this_poly)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _calculateLineWidth(self, current_point, previous_point, current_extrusion, previous_extrusion, layer_thickness):
|
||||||
|
# Area of the filament
|
||||||
|
Af = (self._filament_diameter / 2) ** 2 * numpy.pi
|
||||||
|
# Length of the extruded filament
|
||||||
|
de = current_extrusion - previous_extrusion
|
||||||
|
# Volumne of the extruded filament
|
||||||
|
dVe = de * Af
|
||||||
|
# Length of the printed line
|
||||||
|
dX = numpy.sqrt((current_point[0] - previous_point[0])**2 + (current_point[2] - previous_point[2])**2)
|
||||||
|
# When the extruder recovers from a retraction, we get zero distance
|
||||||
|
if dX == 0:
|
||||||
|
return 0.1
|
||||||
|
# Area of the printed line. This area is a rectangle
|
||||||
|
Ae = dVe / dX
|
||||||
|
# This area is a rectangle with area equal to layer_thickness * layer_width
|
||||||
|
line_width = Ae / layer_thickness
|
||||||
|
|
||||||
|
# A threshold is set to avoid weird paths in the GCode
|
||||||
|
if line_width > 1.2:
|
||||||
|
return 0.35
|
||||||
|
return line_width
|
||||||
|
|
||||||
def _gCode0(self, position, params, path):
|
def _gCode0(self, position, params, path):
|
||||||
x, y, z, e = position
|
|
||||||
|
x, y, z, f, e = position
|
||||||
if self._is_absolute_positioning:
|
if self._is_absolute_positioning:
|
||||||
x = params.x if params.x is not None else x
|
x = params.x if params.x is not None else x
|
||||||
y = params.y if params.y is not None else y
|
y = params.y if params.y is not None else y
|
||||||
|
@ -144,23 +176,25 @@ class GCodeReader(MeshReader):
|
||||||
x += params.x if params.x is not None else 0
|
x += params.x if params.x is not None else 0
|
||||||
y += params.y if params.y is not None else 0
|
y += params.y if params.y is not None else 0
|
||||||
z += params.z if params.z is not None else 0
|
z += params.z if params.z is not None else 0
|
||||||
|
|
||||||
|
f = params.f if params.f is not None else f
|
||||||
|
|
||||||
if params.e is not None:
|
if params.e is not None:
|
||||||
new_extrusion_value = params.e if self._is_absolute_positioning else e[self._extruder_number] + params.e
|
new_extrusion_value = params.e if self._is_absolute_positioning else e[self._extruder_number] + params.e
|
||||||
if new_extrusion_value > e[self._extruder_number]:
|
if new_extrusion_value > e[self._extruder_number]:
|
||||||
path.append([x, y, z, self._layer_type]) # extrusion
|
path.append([x, y, z, f, params.e + self._extrusion_length_offset[self._extruder_number], self._layer_type]) # extrusion
|
||||||
else:
|
else:
|
||||||
path.append([x, y, z, LayerPolygon.MoveRetractionType]) # retraction
|
path.append([x, y, z, f, params.e + self._extrusion_length_offset[self._extruder_number], LayerPolygon.MoveRetractionType]) # retraction
|
||||||
e[self._extruder_number] = new_extrusion_value
|
e[self._extruder_number] = new_extrusion_value
|
||||||
|
|
||||||
# Only when extruding we can determine the latest known "layer height" which is the difference in height between extrusions
|
# Only when extruding we can determine the latest known "layer height" which is the difference in height between extrusions
|
||||||
# Also, 1.5 is a heuristic for any priming or whatsoever, we skip those.
|
# Also, 1.5 is a heuristic for any priming or whatsoever, we skip those.
|
||||||
if z > self._previous_z and (z - self._previous_z < 1.5):
|
if z > self._previous_z and (z - self._previous_z < 1.5):
|
||||||
self._current_layer_thickness = z - self._previous_z + 0.05 # allow a tiny overlap
|
self._current_layer_thickness = z - self._previous_z # allow a tiny overlap
|
||||||
self._previous_z = z
|
self._previous_z = z
|
||||||
else:
|
else:
|
||||||
path.append([x, y, z, LayerPolygon.MoveCombingType])
|
path.append([x, y, z, f, e[self._extruder_number] + self._extrusion_length_offset[self._extruder_number], LayerPolygon.MoveCombingType])
|
||||||
return self._position(x, y, z, e)
|
return self._position(x, y, z, f, e)
|
||||||
|
|
||||||
# G0 and G1 should be handled exactly the same.
|
# G0 and G1 should be handled exactly the same.
|
||||||
_gCode1 = _gCode0
|
_gCode1 = _gCode0
|
||||||
|
@ -171,6 +205,7 @@ class GCodeReader(MeshReader):
|
||||||
params.x if params.x is not None else position.x,
|
params.x if params.x is not None else position.x,
|
||||||
params.y if params.y is not None else position.y,
|
params.y if params.y is not None else position.y,
|
||||||
0,
|
0,
|
||||||
|
position.f,
|
||||||
position.e)
|
position.e)
|
||||||
|
|
||||||
## Set the absolute positioning
|
## Set the absolute positioning
|
||||||
|
@ -187,11 +222,14 @@ class GCodeReader(MeshReader):
|
||||||
# For example: G92 X10 will set the X to 10 without any physical motion.
|
# For example: G92 X10 will set the X to 10 without any physical motion.
|
||||||
def _gCode92(self, position, params, path):
|
def _gCode92(self, position, params, path):
|
||||||
if params.e is not None:
|
if params.e is not None:
|
||||||
|
# Sometimes a G92 E0 is introduced in the middle of the GCode so we need to keep those offsets for calculate the line_width
|
||||||
|
self._extrusion_length_offset[self._extruder_number] += position.e[self._extruder_number] - params.e
|
||||||
position.e[self._extruder_number] = params.e
|
position.e[self._extruder_number] = params.e
|
||||||
return self._position(
|
return self._position(
|
||||||
params.x if params.x is not None else position.x,
|
params.x if params.x is not None else position.x,
|
||||||
params.y if params.y is not None else position.y,
|
params.y if params.y is not None else position.y,
|
||||||
params.z if params.z is not None else position.z,
|
params.z if params.z is not None else position.z,
|
||||||
|
params.f if params.f is not None else position.f,
|
||||||
position.e)
|
position.e)
|
||||||
|
|
||||||
def _processGCode(self, G, line, position, path):
|
def _processGCode(self, G, line, position, path):
|
||||||
|
@ -199,7 +237,7 @@ class GCodeReader(MeshReader):
|
||||||
line = line.split(";", 1)[0] # Remove comments (if any)
|
line = line.split(";", 1)[0] # Remove comments (if any)
|
||||||
if func is not None:
|
if func is not None:
|
||||||
s = line.upper().split(" ")
|
s = line.upper().split(" ")
|
||||||
x, y, z, e = None, None, None, None
|
x, y, z, f, e = None, None, None, None, None
|
||||||
for item in s[1:]:
|
for item in s[1:]:
|
||||||
if len(item) <= 1:
|
if len(item) <= 1:
|
||||||
continue
|
continue
|
||||||
|
@ -211,17 +249,20 @@ class GCodeReader(MeshReader):
|
||||||
y = float(item[1:])
|
y = float(item[1:])
|
||||||
if item[0] == "Z":
|
if item[0] == "Z":
|
||||||
z = float(item[1:])
|
z = float(item[1:])
|
||||||
|
if item[0] == "F":
|
||||||
|
f = float(item[1:]) / 60
|
||||||
if item[0] == "E":
|
if item[0] == "E":
|
||||||
e = float(item[1:])
|
e = float(item[1:])
|
||||||
if self._is_absolute_positioning and ((x is not None and x < 0) or (y is not None and y < 0)):
|
if self._is_absolute_positioning and ((x is not None and x < 0) or (y is not None and y < 0)):
|
||||||
self._center_is_zero = True
|
self._center_is_zero = True
|
||||||
params = self._position(x, y, z, e)
|
params = self._position(x, y, z, f, e)
|
||||||
return func(position, params, path)
|
return func(position, params, path)
|
||||||
return position
|
return position
|
||||||
|
|
||||||
def _processTCode(self, T, line, position, path):
|
def _processTCode(self, T, line, position, path):
|
||||||
self._extruder_number = T
|
self._extruder_number = T
|
||||||
if self._extruder_number + 1 > len(position.e):
|
if self._extruder_number + 1 > len(position.e):
|
||||||
|
self._extrusion_length_offset.extend([0] * (self._extruder_number - len(position.e) + 1))
|
||||||
position.e.extend([0] * (self._extruder_number - len(position.e) + 1))
|
position.e.extend([0] * (self._extruder_number - len(position.e) + 1))
|
||||||
return position
|
return position
|
||||||
|
|
||||||
|
@ -240,6 +281,8 @@ class GCodeReader(MeshReader):
|
||||||
def read(self, file_name):
|
def read(self, file_name):
|
||||||
Logger.log("d", "Preparing to load %s" % file_name)
|
Logger.log("d", "Preparing to load %s" % file_name)
|
||||||
self._cancelled = False
|
self._cancelled = False
|
||||||
|
# We obtain the filament diameter from the selected printer to calculate line widths
|
||||||
|
self._filament_diameter = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value")
|
||||||
|
|
||||||
scene_node = SceneNode()
|
scene_node = SceneNode()
|
||||||
# Override getBoundingBox function of the sceneNode, as this node should return a bounding box, but there is no
|
# Override getBoundingBox function of the sceneNode, as this node should return a bounding box, but there is no
|
||||||
|
@ -277,7 +320,7 @@ class GCodeReader(MeshReader):
|
||||||
|
|
||||||
Logger.log("d", "Parsing %s..." % file_name)
|
Logger.log("d", "Parsing %s..." % file_name)
|
||||||
|
|
||||||
current_position = self._position(0, 0, 0, [0])
|
current_position = self._position(0, 0, 0, 0, [0])
|
||||||
current_path = []
|
current_path = []
|
||||||
|
|
||||||
for line in file:
|
for line in file:
|
||||||
|
@ -310,6 +353,7 @@ class GCodeReader(MeshReader):
|
||||||
else:
|
else:
|
||||||
Logger.log("w", "Encountered a unknown type (%s) while parsing g-code.", type)
|
Logger.log("w", "Encountered a unknown type (%s) while parsing g-code.", type)
|
||||||
|
|
||||||
|
# When the layer change is reached, the polygon is computed so we have just one layer per layer per extruder
|
||||||
if self._is_layers_in_file and line[:len(self._layer_keyword)] == self._layer_keyword:
|
if self._is_layers_in_file and line[:len(self._layer_keyword)] == self._layer_keyword:
|
||||||
try:
|
try:
|
||||||
layer_number = int(line[len(self._layer_keyword):])
|
layer_number = int(line[len(self._layer_keyword):])
|
||||||
|
@ -325,17 +369,12 @@ class GCodeReader(MeshReader):
|
||||||
|
|
||||||
G = self._getInt(line, "G")
|
G = self._getInt(line, "G")
|
||||||
if G is not None:
|
if G is not None:
|
||||||
|
# When find a movement, the new posistion is calculated and added to the current_path, but
|
||||||
|
# don't need to create a polygon until the end of the layer
|
||||||
current_position = self._processGCode(G, line, current_position, current_path)
|
current_position = self._processGCode(G, line, current_position, current_path)
|
||||||
|
|
||||||
# < 2 is a heuristic for a movement only, that should not be counted as a layer
|
|
||||||
if current_position.z > last_z and abs(current_position.z - last_z) < 2:
|
|
||||||
if self._createPolygon(self._current_layer_thickness, current_path, self._extruder_offsets.get(self._extruder_number, [0, 0])):
|
|
||||||
current_path.clear()
|
|
||||||
if not self._is_layers_in_file:
|
|
||||||
self._layer_number += 1
|
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# When changing the extruder, the polygon with the stored paths is computed
|
||||||
if line.startswith("T"):
|
if line.startswith("T"):
|
||||||
T = self._getInt(line, "T")
|
T = self._getInt(line, "T")
|
||||||
if T is not None:
|
if T is not None:
|
||||||
|
@ -344,8 +383,8 @@ class GCodeReader(MeshReader):
|
||||||
|
|
||||||
current_position = self._processTCode(T, line, current_position, current_path)
|
current_position = self._processTCode(T, line, current_position, current_path)
|
||||||
|
|
||||||
# "Flush" leftovers
|
# "Flush" leftovers. Last layer paths are still stored
|
||||||
if not self._is_layers_in_file and len(current_path) > 1:
|
if len(current_path) > 1:
|
||||||
if self._createPolygon(self._current_layer_thickness, current_path, self._extruder_offsets.get(self._extruder_number, [0, 0])):
|
if self._createPolygon(self._current_layer_thickness, current_path, self._extruder_offsets.get(self._extruder_number, [0, 0])):
|
||||||
self._layer_number += 1
|
self._layer_number += 1
|
||||||
current_path.clear()
|
current_path.clear()
|
||||||
|
|
|
@ -1,113 +0,0 @@
|
||||||
# Copyright (c) 2016 Ultimaker B.V.
|
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
|
||||||
|
|
||||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|
||||||
from UM.Resources import Resources
|
|
||||||
from UM.Scene.SceneNode import SceneNode
|
|
||||||
from UM.Scene.ToolHandle import ToolHandle
|
|
||||||
from UM.Application import Application
|
|
||||||
from UM.PluginRegistry import PluginRegistry
|
|
||||||
|
|
||||||
from UM.View.RenderPass import RenderPass
|
|
||||||
from UM.View.RenderBatch import RenderBatch
|
|
||||||
from UM.View.GL.OpenGL import OpenGL
|
|
||||||
|
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
|
||||||
|
|
||||||
|
|
||||||
import os.path
|
|
||||||
|
|
||||||
## RenderPass used to display g-code paths.
|
|
||||||
class LayerPass(RenderPass):
|
|
||||||
def __init__(self, width, height):
|
|
||||||
super().__init__("layerview", width, height)
|
|
||||||
|
|
||||||
self._layer_shader = None
|
|
||||||
self._tool_handle_shader = None
|
|
||||||
self._gl = OpenGL.getInstance().getBindingsObject()
|
|
||||||
self._scene = Application.getInstance().getController().getScene()
|
|
||||||
self._extruder_manager = ExtruderManager.getInstance()
|
|
||||||
|
|
||||||
self._layer_view = None
|
|
||||||
self._compatibility_mode = None
|
|
||||||
|
|
||||||
def setLayerView(self, layerview):
|
|
||||||
self._layer_view = layerview
|
|
||||||
self._compatibility_mode = layerview.getCompatibilityMode()
|
|
||||||
|
|
||||||
def render(self):
|
|
||||||
if not self._layer_shader:
|
|
||||||
if self._compatibility_mode:
|
|
||||||
shader_filename = "layers.shader"
|
|
||||||
else:
|
|
||||||
shader_filename = "layers3d.shader"
|
|
||||||
self._layer_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("LayerView"), shader_filename))
|
|
||||||
# Use extruder 0 if the extruder manager reports extruder index -1 (for single extrusion printers)
|
|
||||||
self._layer_shader.setUniformValue("u_active_extruder", float(max(0, self._extruder_manager.activeExtruderIndex)))
|
|
||||||
if self._layer_view:
|
|
||||||
self._layer_shader.setUniformValue("u_layer_view_type", self._layer_view.getLayerViewType())
|
|
||||||
self._layer_shader.setUniformValue("u_extruder_opacity", self._layer_view.getExtruderOpacities())
|
|
||||||
self._layer_shader.setUniformValue("u_show_travel_moves", self._layer_view.getShowTravelMoves())
|
|
||||||
self._layer_shader.setUniformValue("u_show_helpers", self._layer_view.getShowHelpers())
|
|
||||||
self._layer_shader.setUniformValue("u_show_skin", self._layer_view.getShowSkin())
|
|
||||||
self._layer_shader.setUniformValue("u_show_infill", self._layer_view.getShowInfill())
|
|
||||||
else:
|
|
||||||
#defaults
|
|
||||||
self._layer_shader.setUniformValue("u_layer_view_type", 1)
|
|
||||||
self._layer_shader.setUniformValue("u_extruder_opacity", [1, 1, 1, 1])
|
|
||||||
self._layer_shader.setUniformValue("u_show_travel_moves", 0)
|
|
||||||
self._layer_shader.setUniformValue("u_show_helpers", 1)
|
|
||||||
self._layer_shader.setUniformValue("u_show_skin", 1)
|
|
||||||
self._layer_shader.setUniformValue("u_show_infill", 1)
|
|
||||||
|
|
||||||
if not self._tool_handle_shader:
|
|
||||||
self._tool_handle_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "toolhandle.shader"))
|
|
||||||
|
|
||||||
self.bind()
|
|
||||||
|
|
||||||
tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay)
|
|
||||||
|
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
|
||||||
|
|
||||||
if isinstance(node, ToolHandle):
|
|
||||||
tool_handle_batch.addItem(node.getWorldTransformation(), mesh = node.getSolidMesh())
|
|
||||||
|
|
||||||
elif isinstance(node, SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
|
|
||||||
layer_data = node.callDecoration("getLayerData")
|
|
||||||
if not layer_data:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Render all layers below a certain number as line mesh instead of vertices.
|
|
||||||
if self._layer_view._current_layer_num > -1 and ((not self._layer_view._only_show_top_layers) or (not self._layer_view.getCompatibilityMode())):
|
|
||||||
start = 0
|
|
||||||
end = 0
|
|
||||||
element_counts = layer_data.getElementCounts()
|
|
||||||
for layer in sorted(element_counts.keys()):
|
|
||||||
if layer > self._layer_view._current_layer_num:
|
|
||||||
break
|
|
||||||
if self._layer_view._minimum_layer_num > layer:
|
|
||||||
start += element_counts[layer]
|
|
||||||
end += element_counts[layer]
|
|
||||||
|
|
||||||
# This uses glDrawRangeElements internally to only draw a certain range of lines.
|
|
||||||
batch = RenderBatch(self._layer_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (start, end))
|
|
||||||
batch.addItem(node.getWorldTransformation(), layer_data)
|
|
||||||
batch.render(self._scene.getActiveCamera())
|
|
||||||
|
|
||||||
# Create a new batch that is not range-limited
|
|
||||||
batch = RenderBatch(self._layer_shader, type = RenderBatch.RenderType.Solid)
|
|
||||||
|
|
||||||
if self._layer_view.getCurrentLayerMesh():
|
|
||||||
batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerMesh())
|
|
||||||
|
|
||||||
if self._layer_view.getCurrentLayerJumps():
|
|
||||||
batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerJumps())
|
|
||||||
|
|
||||||
if len(batch.items) > 0:
|
|
||||||
batch.render(self._scene.getActiveCamera())
|
|
||||||
|
|
||||||
# Render toolhandles on top of the layerview
|
|
||||||
if len(tool_handle_batch.items) > 0:
|
|
||||||
tool_handle_batch.render(self._scene.getActiveCamera())
|
|
||||||
|
|
||||||
self.release()
|
|
|
@ -1,388 +0,0 @@
|
||||||
// Copyright (c) 2017 Ultimaker B.V.
|
|
||||||
// Cura is released under the terms of the LGPLv3 or higher.
|
|
||||||
|
|
||||||
import QtQuick 2.4
|
|
||||||
import QtQuick.Controls 1.2
|
|
||||||
import QtQuick.Layouts 1.1
|
|
||||||
import QtQuick.Controls.Styles 1.1
|
|
||||||
|
|
||||||
import UM 1.0 as UM
|
|
||||||
import Cura 1.0 as Cura
|
|
||||||
|
|
||||||
Item
|
|
||||||
{
|
|
||||||
id: base
|
|
||||||
width: {
|
|
||||||
if (UM.LayerView.compatibilityMode) {
|
|
||||||
return UM.Theme.getSize("layerview_menu_size_compatibility").width;
|
|
||||||
} else {
|
|
||||||
return UM.Theme.getSize("layerview_menu_size").width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
height: {
|
|
||||||
if (UM.LayerView.compatibilityMode) {
|
|
||||||
return UM.Theme.getSize("layerview_menu_size_compatibility").height;
|
|
||||||
} else if (UM.Preferences.getValue("layerview/layer_view_type") == 0) {
|
|
||||||
return UM.Theme.getSize("layerview_menu_size_material_color_mode").height + UM.LayerView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
|
|
||||||
} else {
|
|
||||||
return UM.Theme.getSize("layerview_menu_size").height + UM.LayerView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property var buttonTarget: {
|
|
||||||
if(parent != null)
|
|
||||||
{
|
|
||||||
var force_binding = parent.y; // ensure this gets reevaluated when the panel moves
|
|
||||||
return base.mapFromItem(parent.parent, parent.buttonTarget.x, parent.buttonTarget.y)
|
|
||||||
}
|
|
||||||
return Qt.point(0,0)
|
|
||||||
}
|
|
||||||
|
|
||||||
visible: parent != null ? !parent.parent.monitoringPrint: true
|
|
||||||
|
|
||||||
UM.PointingRectangle {
|
|
||||||
id: layerViewMenu
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
width: parent.width
|
|
||||||
height: parent.height
|
|
||||||
z: slider.z - 1
|
|
||||||
color: UM.Theme.getColor("tool_panel_background")
|
|
||||||
borderWidth: UM.Theme.getSize("default_lining").width
|
|
||||||
borderColor: UM.Theme.getColor("lining")
|
|
||||||
arrowSize: 0 // hide arrow until weird issue with first time rendering is fixed
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: view_settings
|
|
||||||
|
|
||||||
property var extruder_opacities: UM.Preferences.getValue("layerview/extruder_opacities").split("|")
|
|
||||||
property bool show_travel_moves: UM.Preferences.getValue("layerview/show_travel_moves")
|
|
||||||
property bool show_helpers: UM.Preferences.getValue("layerview/show_helpers")
|
|
||||||
property bool show_skin: UM.Preferences.getValue("layerview/show_skin")
|
|
||||||
property bool show_infill: UM.Preferences.getValue("layerview/show_infill")
|
|
||||||
// if we are in compatibility mode, we only show the "line type"
|
|
||||||
property bool show_legend: UM.LayerView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type") == 1
|
|
||||||
property bool only_show_top_layers: UM.Preferences.getValue("view/only_show_top_layers")
|
|
||||||
property int top_layer_count: UM.Preferences.getValue("view/top_layer_count")
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
spacing: UM.Theme.getSize("layerview_row_spacing").height
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
|
|
||||||
Label
|
|
||||||
{
|
|
||||||
id: layerViewTypesLabel
|
|
||||||
anchors.left: parent.left
|
|
||||||
text: catalog.i18nc("@label","Color scheme")
|
|
||||||
font: UM.Theme.getFont("default");
|
|
||||||
visible: !UM.LayerView.compatibilityMode
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: UM.Theme.getColor("setting_control_text")
|
|
||||||
}
|
|
||||||
|
|
||||||
ListModel // matches LayerView.py
|
|
||||||
{
|
|
||||||
id: layerViewTypes
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted:
|
|
||||||
{
|
|
||||||
layerViewTypes.append({
|
|
||||||
text: catalog.i18nc("@label:listbox", "Material Color"),
|
|
||||||
type_id: 0
|
|
||||||
})
|
|
||||||
layerViewTypes.append({
|
|
||||||
text: catalog.i18nc("@label:listbox", "Line Type"),
|
|
||||||
type_id: 1 // these ids match the switching in the shader
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
ComboBox
|
|
||||||
{
|
|
||||||
id: layerTypeCombobox
|
|
||||||
anchors.left: parent.left
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
|
||||||
model: layerViewTypes
|
|
||||||
visible: !UM.LayerView.compatibilityMode
|
|
||||||
style: UM.Theme.styles.combobox
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 10 * screenScaleFactor
|
|
||||||
|
|
||||||
onActivated:
|
|
||||||
{
|
|
||||||
UM.Preferences.setValue("layerview/layer_view_type", index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted:
|
|
||||||
{
|
|
||||||
currentIndex = UM.LayerView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type");
|
|
||||||
updateLegends(currentIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLegends(type_id)
|
|
||||||
{
|
|
||||||
// update visibility of legends
|
|
||||||
view_settings.show_legend = UM.LayerView.compatibilityMode || (type_id == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Label
|
|
||||||
{
|
|
||||||
id: compatibilityModeLabel
|
|
||||||
anchors.left: parent.left
|
|
||||||
text: catalog.i18nc("@label","Compatibility Mode")
|
|
||||||
font: UM.Theme.getFont("default")
|
|
||||||
color: UM.Theme.getColor("text")
|
|
||||||
visible: UM.LayerView.compatibilityMode
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height
|
|
||||||
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
|
||||||
}
|
|
||||||
|
|
||||||
Label
|
|
||||||
{
|
|
||||||
id: space2Label
|
|
||||||
anchors.left: parent.left
|
|
||||||
text: " "
|
|
||||||
font.pointSize: 0.5
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: UM.Preferences
|
|
||||||
onPreferenceChanged:
|
|
||||||
{
|
|
||||||
layerTypeCombobox.currentIndex = UM.LayerView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type");
|
|
||||||
layerTypeCombobox.updateLegends(layerTypeCombobox.currentIndex);
|
|
||||||
view_settings.extruder_opacities = UM.Preferences.getValue("layerview/extruder_opacities").split("|");
|
|
||||||
view_settings.show_travel_moves = UM.Preferences.getValue("layerview/show_travel_moves");
|
|
||||||
view_settings.show_helpers = UM.Preferences.getValue("layerview/show_helpers");
|
|
||||||
view_settings.show_skin = UM.Preferences.getValue("layerview/show_skin");
|
|
||||||
view_settings.show_infill = UM.Preferences.getValue("layerview/show_infill");
|
|
||||||
view_settings.only_show_top_layers = UM.Preferences.getValue("view/only_show_top_layers");
|
|
||||||
view_settings.top_layer_count = UM.Preferences.getValue("view/top_layer_count");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: Cura.ExtrudersModel{}
|
|
||||||
CheckBox {
|
|
||||||
id: extrudersModelCheckBox
|
|
||||||
checked: view_settings.extruder_opacities[index] > 0.5 || view_settings.extruder_opacities[index] == undefined || view_settings.extruder_opacities[index] == ""
|
|
||||||
onClicked: {
|
|
||||||
view_settings.extruder_opacities[index] = checked ? 1.0 : 0.0
|
|
||||||
UM.Preferences.setValue("layerview/extruder_opacities", view_settings.extruder_opacities.join("|"));
|
|
||||||
}
|
|
||||||
visible: !UM.LayerView.compatibilityMode
|
|
||||||
enabled: index + 1 <= 4
|
|
||||||
Rectangle {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.right: extrudersModelCheckBox.right
|
|
||||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
width: UM.Theme.getSize("layerview_legend_size").width
|
|
||||||
height: UM.Theme.getSize("layerview_legend_size").height
|
|
||||||
color: model.color
|
|
||||||
radius: width / 2
|
|
||||||
border.width: UM.Theme.getSize("default_lining").width
|
|
||||||
border.color: UM.Theme.getColor("lining")
|
|
||||||
visible: !view_settings.show_legend
|
|
||||||
}
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
|
|
||||||
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
|
||||||
style: UM.Theme.styles.checkbox
|
|
||||||
Label
|
|
||||||
{
|
|
||||||
text: model.name
|
|
||||||
elide: Text.ElideRight
|
|
||||||
color: UM.Theme.getColor("setting_control_text")
|
|
||||||
font: UM.Theme.getFont("default")
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: extrudersModelCheckBox.left;
|
|
||||||
anchors.right: extrudersModelCheckBox.right;
|
|
||||||
anchors.leftMargin: UM.Theme.getSize("checkbox").width + UM.Theme.getSize("default_margin").width /2
|
|
||||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: ListModel {
|
|
||||||
id: typesLegenModel
|
|
||||||
Component.onCompleted:
|
|
||||||
{
|
|
||||||
typesLegenModel.append({
|
|
||||||
label: catalog.i18nc("@label", "Show Travels"),
|
|
||||||
initialValue: view_settings.show_travel_moves,
|
|
||||||
preference: "layerview/show_travel_moves",
|
|
||||||
colorId: "layerview_move_combing"
|
|
||||||
});
|
|
||||||
typesLegenModel.append({
|
|
||||||
label: catalog.i18nc("@label", "Show Helpers"),
|
|
||||||
initialValue: view_settings.show_helpers,
|
|
||||||
preference: "layerview/show_helpers",
|
|
||||||
colorId: "layerview_support"
|
|
||||||
});
|
|
||||||
typesLegenModel.append({
|
|
||||||
label: catalog.i18nc("@label", "Show Shell"),
|
|
||||||
initialValue: view_settings.show_skin,
|
|
||||||
preference: "layerview/show_skin",
|
|
||||||
colorId: "layerview_inset_0"
|
|
||||||
});
|
|
||||||
typesLegenModel.append({
|
|
||||||
label: catalog.i18nc("@label", "Show Infill"),
|
|
||||||
initialValue: view_settings.show_infill,
|
|
||||||
preference: "layerview/show_infill",
|
|
||||||
colorId: "layerview_infill"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
id: legendModelCheckBox
|
|
||||||
checked: model.initialValue
|
|
||||||
onClicked: {
|
|
||||||
UM.Preferences.setValue(model.preference, checked);
|
|
||||||
}
|
|
||||||
Rectangle {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.right: legendModelCheckBox.right
|
|
||||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
width: UM.Theme.getSize("layerview_legend_size").width
|
|
||||||
height: UM.Theme.getSize("layerview_legend_size").height
|
|
||||||
color: UM.Theme.getColor(model.colorId)
|
|
||||||
border.width: UM.Theme.getSize("default_lining").width
|
|
||||||
border.color: UM.Theme.getColor("lining")
|
|
||||||
visible: view_settings.show_legend
|
|
||||||
}
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
|
|
||||||
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
|
||||||
style: UM.Theme.styles.checkbox
|
|
||||||
Label
|
|
||||||
{
|
|
||||||
text: label
|
|
||||||
font: UM.Theme.getFont("default")
|
|
||||||
elide: Text.ElideRight
|
|
||||||
color: UM.Theme.getColor("setting_control_text")
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: legendModelCheckBox.left;
|
|
||||||
anchors.right: legendModelCheckBox.right;
|
|
||||||
anchors.leftMargin: UM.Theme.getSize("checkbox").width + UM.Theme.getSize("default_margin").width /2
|
|
||||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
checked: view_settings.only_show_top_layers
|
|
||||||
onClicked: {
|
|
||||||
UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0);
|
|
||||||
}
|
|
||||||
text: catalog.i18nc("@label", "Only Show Top Layers")
|
|
||||||
visible: UM.LayerView.compatibilityMode
|
|
||||||
style: UM.Theme.styles.checkbox
|
|
||||||
}
|
|
||||||
CheckBox {
|
|
||||||
checked: view_settings.top_layer_count == 5
|
|
||||||
onClicked: {
|
|
||||||
UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1);
|
|
||||||
}
|
|
||||||
text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top")
|
|
||||||
visible: UM.LayerView.compatibilityMode
|
|
||||||
style: UM.Theme.styles.checkbox
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: ListModel {
|
|
||||||
id: typesLegenModelNoCheck
|
|
||||||
Component.onCompleted:
|
|
||||||
{
|
|
||||||
typesLegenModelNoCheck.append({
|
|
||||||
label: catalog.i18nc("@label", "Top / Bottom"),
|
|
||||||
colorId: "layerview_skin",
|
|
||||||
});
|
|
||||||
typesLegenModelNoCheck.append({
|
|
||||||
label: catalog.i18nc("@label", "Inner Wall"),
|
|
||||||
colorId: "layerview_inset_x",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: label
|
|
||||||
visible: view_settings.show_legend
|
|
||||||
id: typesLegendModelLabel
|
|
||||||
Rectangle {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.right: typesLegendModelLabel.right
|
|
||||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
width: UM.Theme.getSize("layerview_legend_size").width
|
|
||||||
height: UM.Theme.getSize("layerview_legend_size").height
|
|
||||||
color: UM.Theme.getColor(model.colorId)
|
|
||||||
border.width: UM.Theme.getSize("default_lining").width
|
|
||||||
border.color: UM.Theme.getColor("lining")
|
|
||||||
visible: view_settings.show_legend
|
|
||||||
}
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
|
|
||||||
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
|
||||||
color: UM.Theme.getColor("setting_control_text")
|
|
||||||
font: UM.Theme.getFont("default")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LayerSlider {
|
|
||||||
id: slider
|
|
||||||
|
|
||||||
width: UM.Theme.getSize("slider_handle").width
|
|
||||||
height: UM.Theme.getSize("layerview_menu_size").height
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: parent.bottom
|
|
||||||
topMargin: UM.Theme.getSize("slider_layerview_margin").height
|
|
||||||
right: layerViewMenu.right
|
|
||||||
rightMargin: UM.Theme.getSize("slider_layerview_margin").width
|
|
||||||
}
|
|
||||||
|
|
||||||
// custom properties
|
|
||||||
upperValue: UM.LayerView.currentLayer
|
|
||||||
lowerValue: UM.LayerView.minimumLayer
|
|
||||||
maximumValue: UM.LayerView.numLayers
|
|
||||||
handleSize: UM.Theme.getSize("slider_handle").width
|
|
||||||
trackThickness: UM.Theme.getSize("slider_groove").width
|
|
||||||
trackColor: UM.Theme.getColor("slider_groove")
|
|
||||||
trackBorderColor: UM.Theme.getColor("slider_groove_border")
|
|
||||||
upperHandleColor: UM.Theme.getColor("slider_handle")
|
|
||||||
lowerHandleColor: UM.Theme.getColor("slider_handle")
|
|
||||||
rangeHandleColor: UM.Theme.getColor("slider_groove_fill")
|
|
||||||
handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width
|
|
||||||
layersVisible: UM.LayerView.layerActivity && CuraApplication.platformActivity ? true : false
|
|
||||||
|
|
||||||
// update values when layer data changes
|
|
||||||
Connections {
|
|
||||||
target: UM.LayerView
|
|
||||||
onMaxLayersChanged: slider.setUpperValue(UM.LayerView.currentLayer)
|
|
||||||
onMinimumLayerChanged: slider.setLowerValue(UM.LayerView.minimumLayer)
|
|
||||||
onCurrentLayerChanged: slider.setUpperValue(UM.LayerView.currentLayer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure the slider handlers show the correct value after switching views
|
|
||||||
Component.onCompleted: {
|
|
||||||
slider.setLowerValue(UM.LayerView.minimumLayer)
|
|
||||||
slider.setUpperValue(UM.LayerView.currentLayer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FontMetrics {
|
|
||||||
id: fontMetrics
|
|
||||||
font: UM.Theme.getFont("default")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,151 +0,0 @@
|
||||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty
|
|
||||||
from UM.FlameProfiler import pyqtSlot
|
|
||||||
from UM.Application import Application
|
|
||||||
|
|
||||||
import LayerView
|
|
||||||
|
|
||||||
|
|
||||||
class LayerViewProxy(QObject):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
self._current_layer = 0
|
|
||||||
self._controller = Application.getInstance().getController()
|
|
||||||
self._controller.activeViewChanged.connect(self._onActiveViewChanged)
|
|
||||||
self._onActiveViewChanged()
|
|
||||||
|
|
||||||
currentLayerChanged = pyqtSignal()
|
|
||||||
maxLayersChanged = pyqtSignal()
|
|
||||||
activityChanged = pyqtSignal()
|
|
||||||
globalStackChanged = pyqtSignal()
|
|
||||||
preferencesChanged = pyqtSignal()
|
|
||||||
busyChanged = pyqtSignal()
|
|
||||||
|
|
||||||
@pyqtProperty(bool, notify=activityChanged)
|
|
||||||
def layerActivity(self):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
return active_view.getActivity()
|
|
||||||
|
|
||||||
@pyqtProperty(int, notify=maxLayersChanged)
|
|
||||||
def numLayers(self):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
return active_view.getMaxLayers()
|
|
||||||
|
|
||||||
@pyqtProperty(int, notify=currentLayerChanged)
|
|
||||||
def currentLayer(self):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
return active_view.getCurrentLayer()
|
|
||||||
|
|
||||||
@pyqtProperty(int, notify=currentLayerChanged)
|
|
||||||
def minimumLayer(self):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
return active_view.getMinimumLayer()
|
|
||||||
|
|
||||||
@pyqtProperty(bool, notify=busyChanged)
|
|
||||||
def busy(self):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
return active_view.isBusy()
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
@pyqtProperty(bool, notify=preferencesChanged)
|
|
||||||
def compatibilityMode(self):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
return active_view.getCompatibilityMode()
|
|
||||||
return False
|
|
||||||
|
|
||||||
@pyqtSlot(int)
|
|
||||||
def setCurrentLayer(self, layer_num):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
active_view.setLayer(layer_num)
|
|
||||||
|
|
||||||
@pyqtSlot(int)
|
|
||||||
def setMinimumLayer(self, layer_num):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
active_view.setMinimumLayer(layer_num)
|
|
||||||
|
|
||||||
@pyqtSlot(int)
|
|
||||||
def setLayerViewType(self, layer_view_type):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
active_view.setLayerViewType(layer_view_type)
|
|
||||||
|
|
||||||
@pyqtSlot(result=int)
|
|
||||||
def getLayerViewType(self):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
return active_view.getLayerViewType()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
# Opacity 0..1
|
|
||||||
@pyqtSlot(int, float)
|
|
||||||
def setExtruderOpacity(self, extruder_nr, opacity):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
active_view.setExtruderOpacity(extruder_nr, opacity)
|
|
||||||
|
|
||||||
@pyqtSlot(int)
|
|
||||||
def setShowTravelMoves(self, show):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
active_view.setShowTravelMoves(show)
|
|
||||||
|
|
||||||
@pyqtSlot(int)
|
|
||||||
def setShowHelpers(self, show):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
active_view.setShowHelpers(show)
|
|
||||||
|
|
||||||
@pyqtSlot(int)
|
|
||||||
def setShowSkin(self, show):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
active_view.setShowSkin(show)
|
|
||||||
|
|
||||||
@pyqtSlot(int)
|
|
||||||
def setShowInfill(self, show):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
active_view.setShowInfill(show)
|
|
||||||
|
|
||||||
@pyqtProperty(int, notify=globalStackChanged)
|
|
||||||
def extruderCount(self):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
return active_view.getExtruderCount()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def _layerActivityChanged(self):
|
|
||||||
self.activityChanged.emit()
|
|
||||||
|
|
||||||
def _onLayerChanged(self):
|
|
||||||
self.currentLayerChanged.emit()
|
|
||||||
self._layerActivityChanged()
|
|
||||||
|
|
||||||
def _onMaxLayersChanged(self):
|
|
||||||
self.maxLayersChanged.emit()
|
|
||||||
|
|
||||||
def _onBusyChanged(self):
|
|
||||||
self.busyChanged.emit()
|
|
||||||
|
|
||||||
def _onGlobalStackChanged(self):
|
|
||||||
self.globalStackChanged.emit()
|
|
||||||
|
|
||||||
def _onPreferencesChanged(self):
|
|
||||||
self.preferencesChanged.emit()
|
|
||||||
|
|
||||||
def _onActiveViewChanged(self):
|
|
||||||
active_view = self._controller.getActiveView()
|
|
||||||
if type(active_view) == LayerView.LayerView.LayerView:
|
|
||||||
active_view.currentLayerNumChanged.connect(self._onLayerChanged)
|
|
||||||
active_view.maxLayersChanged.connect(self._onMaxLayersChanged)
|
|
||||||
active_view.busyChanged.connect(self._onBusyChanged)
|
|
||||||
active_view.globalStackChanged.connect(self._onGlobalStackChanged)
|
|
||||||
active_view.preferencesChanged.connect(self._onPreferencesChanged)
|
|
|
@ -1,25 +0,0 @@
|
||||||
# Copyright (c) 2015 Ultimaker B.V.
|
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
|
||||||
|
|
||||||
from . import LayerView, LayerViewProxy
|
|
||||||
from PyQt5.QtQml import qmlRegisterType, qmlRegisterSingletonType
|
|
||||||
|
|
||||||
from UM.i18n import i18nCatalog
|
|
||||||
catalog = i18nCatalog("cura")
|
|
||||||
|
|
||||||
def getMetaData():
|
|
||||||
return {
|
|
||||||
"view": {
|
|
||||||
"name": catalog.i18nc("@item:inlistbox", "Layer view"),
|
|
||||||
"view_panel": "LayerView.qml",
|
|
||||||
"weight": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def createLayerViewProxy(engine, script_engine):
|
|
||||||
return LayerViewProxy.LayerViewProxy()
|
|
||||||
|
|
||||||
def register(app):
|
|
||||||
layer_view = LayerView.LayerView()
|
|
||||||
qmlRegisterSingletonType(LayerViewProxy.LayerViewProxy, "UM", 1, 0, "LayerView", layer_view.getProxy)
|
|
||||||
return { "view": LayerView.LayerView() }
|
|
|
@ -1,312 +1,325 @@
|
||||||
// Copyright (c) 2017 Ultimaker B.V.
|
// Copyright (c) 2017 Ultimaker B.V.
|
||||||
// Cura is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.2
|
import QtQuick 2.2
|
||||||
import QtQuick.Controls 1.2
|
import QtQuick.Controls 1.2
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.1
|
||||||
import QtQuick.Controls.Styles 1.1
|
import QtQuick.Controls.Styles 1.1
|
||||||
|
|
||||||
import UM 1.0 as UM
|
import UM 1.0 as UM
|
||||||
import Cura 1.0 as Cura
|
import Cura 1.0 as Cura
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: sliderRoot
|
id: sliderRoot
|
||||||
|
|
||||||
// handle properties
|
// handle properties
|
||||||
property real handleSize: 10
|
property real handleSize: 10
|
||||||
property real handleRadius: handleSize / 2
|
property real handleRadius: handleSize / 2
|
||||||
property real minimumRangeHandleSize: handleSize / 2
|
property real minimumRangeHandleSize: handleSize / 2
|
||||||
property color upperHandleColor: "black"
|
property color upperHandleColor: "black"
|
||||||
property color lowerHandleColor: "black"
|
property color lowerHandleColor: "black"
|
||||||
property color rangeHandleColor: "black"
|
property color rangeHandleColor: "black"
|
||||||
property real handleLabelWidth: width
|
property color handleActiveColor: "white"
|
||||||
property var activeHandle: upperHandle
|
property real handleLabelWidth: width
|
||||||
|
property var activeHandle: upperHandle
|
||||||
// track properties
|
|
||||||
property real trackThickness: 4 // width of the slider track
|
// track properties
|
||||||
property real trackRadius: trackThickness / 2
|
property real trackThickness: 4 // width of the slider track
|
||||||
property color trackColor: "white"
|
property real trackRadius: trackThickness / 2
|
||||||
property real trackBorderWidth: 1 // width of the slider track border
|
property color trackColor: "white"
|
||||||
property color trackBorderColor: "black"
|
property real trackBorderWidth: 1 // width of the slider track border
|
||||||
|
property color trackBorderColor: "black"
|
||||||
// value properties
|
|
||||||
property real maximumValue: 100
|
// value properties
|
||||||
property real minimumValue: 0
|
property real maximumValue: 100
|
||||||
property real minimumRange: 0 // minimum range allowed between min and max values
|
property real minimumValue: 0
|
||||||
property bool roundValues: true
|
property real minimumRange: 0 // minimum range allowed between min and max values
|
||||||
property real upperValue: maximumValue
|
property bool roundValues: true
|
||||||
property real lowerValue: minimumValue
|
property real upperValue: maximumValue
|
||||||
|
property real lowerValue: minimumValue
|
||||||
property bool layersVisible: true
|
|
||||||
|
property bool layersVisible: true
|
||||||
function getUpperValueFromSliderHandle () {
|
|
||||||
return upperHandle.getValue()
|
function getUpperValueFromSliderHandle () {
|
||||||
}
|
return upperHandle.getValue()
|
||||||
|
}
|
||||||
function setUpperValue (value) {
|
|
||||||
upperHandle.setValue(value)
|
function setUpperValue (value) {
|
||||||
updateRangeHandle()
|
upperHandle.setValue(value)
|
||||||
}
|
updateRangeHandle()
|
||||||
|
}
|
||||||
function getLowerValueFromSliderHandle () {
|
|
||||||
return lowerHandle.getValue()
|
function getLowerValueFromSliderHandle () {
|
||||||
}
|
return lowerHandle.getValue()
|
||||||
|
}
|
||||||
function setLowerValue (value) {
|
|
||||||
lowerHandle.setValue(value)
|
function setLowerValue (value) {
|
||||||
updateRangeHandle()
|
lowerHandle.setValue(value)
|
||||||
}
|
updateRangeHandle()
|
||||||
|
}
|
||||||
function updateRangeHandle () {
|
|
||||||
rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height)
|
function updateRangeHandle () {
|
||||||
}
|
rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height)
|
||||||
|
}
|
||||||
// set the active handle to show only one label at a time
|
|
||||||
function setActiveHandle (handle) {
|
// set the active handle to show only one label at a time
|
||||||
activeHandle = handle
|
function setActiveHandle (handle) {
|
||||||
}
|
activeHandle = handle
|
||||||
|
}
|
||||||
// slider track
|
|
||||||
Rectangle {
|
// slider track
|
||||||
id: track
|
Rectangle {
|
||||||
|
id: track
|
||||||
width: sliderRoot.trackThickness
|
|
||||||
height: sliderRoot.height - sliderRoot.handleSize
|
width: sliderRoot.trackThickness
|
||||||
radius: sliderRoot.trackRadius
|
height: sliderRoot.height - sliderRoot.handleSize
|
||||||
anchors.centerIn: sliderRoot
|
radius: sliderRoot.trackRadius
|
||||||
color: sliderRoot.trackColor
|
anchors.centerIn: sliderRoot
|
||||||
border.width: sliderRoot.trackBorderWidth
|
color: sliderRoot.trackColor
|
||||||
border.color: sliderRoot.trackBorderColor
|
border.width: sliderRoot.trackBorderWidth
|
||||||
visible: sliderRoot.layersVisible
|
border.color: sliderRoot.trackBorderColor
|
||||||
}
|
visible: sliderRoot.layersVisible
|
||||||
|
}
|
||||||
// Range handle
|
|
||||||
Item {
|
// Range handle
|
||||||
id: rangeHandle
|
Item {
|
||||||
|
id: rangeHandle
|
||||||
y: upperHandle.y + upperHandle.height
|
|
||||||
width: sliderRoot.handleSize
|
y: upperHandle.y + upperHandle.height
|
||||||
height: sliderRoot.minimumRangeHandleSize
|
width: sliderRoot.handleSize
|
||||||
anchors.horizontalCenter: sliderRoot.horizontalCenter
|
height: sliderRoot.minimumRangeHandleSize
|
||||||
visible: sliderRoot.layersVisible
|
anchors.horizontalCenter: sliderRoot.horizontalCenter
|
||||||
|
visible: sliderRoot.layersVisible
|
||||||
// set the new value when dragging
|
|
||||||
function onHandleDragged () {
|
// set the new value when dragging
|
||||||
|
function onHandleDragged () {
|
||||||
upperHandle.y = y - upperHandle.height
|
|
||||||
lowerHandle.y = y + height
|
upperHandle.y = y - upperHandle.height
|
||||||
|
lowerHandle.y = y + height
|
||||||
var upperValue = sliderRoot.getUpperValueFromSliderHandle()
|
|
||||||
var lowerValue = sliderRoot.getLowerValueFromSliderHandle()
|
var upperValue = sliderRoot.getUpperValueFromSliderHandle()
|
||||||
|
var lowerValue = sliderRoot.getLowerValueFromSliderHandle()
|
||||||
// set both values after moving the handle position
|
|
||||||
UM.LayerView.setCurrentLayer(upperValue)
|
// set both values after moving the handle position
|
||||||
UM.LayerView.setMinimumLayer(lowerValue)
|
UM.SimulationView.setCurrentLayer(upperValue)
|
||||||
}
|
UM.SimulationView.setMinimumLayer(lowerValue)
|
||||||
|
}
|
||||||
function setValue (value) {
|
|
||||||
var range = sliderRoot.upperValue - sliderRoot.lowerValue
|
function setValue (value) {
|
||||||
value = Math.min(value, sliderRoot.maximumValue)
|
var range = sliderRoot.upperValue - sliderRoot.lowerValue
|
||||||
value = Math.max(value, sliderRoot.minimumValue + range)
|
value = Math.min(value, sliderRoot.maximumValue)
|
||||||
|
value = Math.max(value, sliderRoot.minimumValue + range)
|
||||||
UM.LayerView.setCurrentLayer(value)
|
|
||||||
UM.LayerView.setMinimumLayer(value - range)
|
UM.SimulationView.setCurrentLayer(value)
|
||||||
}
|
UM.SimulationView.setMinimumLayer(value - range)
|
||||||
|
}
|
||||||
Rectangle {
|
|
||||||
width: sliderRoot.trackThickness - 2 * sliderRoot.trackBorderWidth
|
Rectangle {
|
||||||
height: parent.height + sliderRoot.handleSize
|
width: sliderRoot.trackThickness - 2 * sliderRoot.trackBorderWidth
|
||||||
anchors.centerIn: parent
|
height: parent.height + sliderRoot.handleSize
|
||||||
color: sliderRoot.rangeHandleColor
|
anchors.centerIn: parent
|
||||||
}
|
color: sliderRoot.rangeHandleColor
|
||||||
|
}
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
drag {
|
|
||||||
target: parent
|
drag {
|
||||||
axis: Drag.YAxis
|
target: parent
|
||||||
minimumY: upperHandle.height
|
axis: Drag.YAxis
|
||||||
maximumY: sliderRoot.height - (rangeHandle.height + lowerHandle.height)
|
minimumY: upperHandle.height
|
||||||
}
|
maximumY: sliderRoot.height - (rangeHandle.height + lowerHandle.height)
|
||||||
|
}
|
||||||
onPositionChanged: parent.onHandleDragged()
|
|
||||||
onPressed: sliderRoot.setActiveHandle(rangeHandle)
|
onPositionChanged: parent.onHandleDragged()
|
||||||
}
|
onPressed: sliderRoot.setActiveHandle(rangeHandle)
|
||||||
|
}
|
||||||
LayerSliderLabel {
|
|
||||||
id: rangleHandleLabel
|
SimulationSliderLabel {
|
||||||
|
id: rangleHandleLabel
|
||||||
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
|
|
||||||
x: parent.x - width - UM.Theme.getSize("default_margin").width
|
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
x: parent.x - width - UM.Theme.getSize("default_margin").width
|
||||||
target: Qt.point(sliderRoot.width, y + height / 2)
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: sliderRoot.activeHandle == parent
|
target: Qt.point(sliderRoot.width, y + height / 2)
|
||||||
|
visible: sliderRoot.activeHandle == parent
|
||||||
// custom properties
|
|
||||||
maximumValue: sliderRoot.maximumValue
|
// custom properties
|
||||||
value: sliderRoot.upperValue
|
maximumValue: sliderRoot.maximumValue
|
||||||
busy: UM.LayerView.busy
|
value: sliderRoot.upperValue
|
||||||
setValue: rangeHandle.setValue // connect callback functions
|
busy: UM.SimulationView.busy
|
||||||
}
|
setValue: rangeHandle.setValue // connect callback functions
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Upper handle
|
|
||||||
Rectangle {
|
// Upper handle
|
||||||
id: upperHandle
|
Rectangle {
|
||||||
|
id: upperHandle
|
||||||
y: sliderRoot.height - (sliderRoot.minimumRangeHandleSize + 2 * sliderRoot.handleSize)
|
|
||||||
width: sliderRoot.handleSize
|
y: sliderRoot.height - (sliderRoot.minimumRangeHandleSize + 2 * sliderRoot.handleSize)
|
||||||
height: sliderRoot.handleSize
|
width: sliderRoot.handleSize
|
||||||
anchors.horizontalCenter: sliderRoot.horizontalCenter
|
height: sliderRoot.handleSize
|
||||||
radius: sliderRoot.handleRadius
|
anchors.horizontalCenter: sliderRoot.horizontalCenter
|
||||||
color: sliderRoot.upperHandleColor
|
radius: sliderRoot.handleRadius
|
||||||
visible: sliderRoot.layersVisible
|
color: upperHandleLabel.activeFocus ? sliderRoot.handleActiveColor : sliderRoot.upperHandleColor
|
||||||
|
visible: sliderRoot.layersVisible
|
||||||
function onHandleDragged () {
|
|
||||||
|
function onHandleDragged () {
|
||||||
// don't allow the lower handle to be heigher than the upper handle
|
|
||||||
if (lowerHandle.y - (y + height) < sliderRoot.minimumRangeHandleSize) {
|
// don't allow the lower handle to be heigher than the upper handle
|
||||||
lowerHandle.y = y + height + sliderRoot.minimumRangeHandleSize
|
if (lowerHandle.y - (y + height) < sliderRoot.minimumRangeHandleSize) {
|
||||||
}
|
lowerHandle.y = y + height + sliderRoot.minimumRangeHandleSize
|
||||||
|
}
|
||||||
// update the range handle
|
|
||||||
sliderRoot.updateRangeHandle()
|
// update the range handle
|
||||||
|
sliderRoot.updateRangeHandle()
|
||||||
// set the new value after moving the handle position
|
|
||||||
UM.LayerView.setCurrentLayer(getValue())
|
// set the new value after moving the handle position
|
||||||
}
|
UM.SimulationView.setCurrentLayer(getValue())
|
||||||
|
}
|
||||||
// get the upper value based on the slider position
|
|
||||||
function getValue () {
|
// get the upper value based on the slider position
|
||||||
var result = y / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize))
|
function getValue () {
|
||||||
result = sliderRoot.maximumValue + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumValue))
|
var result = y / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize))
|
||||||
result = sliderRoot.roundValues ? Math.round(result) : result
|
result = sliderRoot.maximumValue + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumValue))
|
||||||
return result
|
result = sliderRoot.roundValues ? Math.round(result) : result
|
||||||
}
|
return result
|
||||||
|
}
|
||||||
// set the slider position based on the upper value
|
|
||||||
function setValue (value) {
|
// set the slider position based on the upper value
|
||||||
|
function setValue (value) {
|
||||||
UM.LayerView.setCurrentLayer(value)
|
|
||||||
|
UM.SimulationView.setCurrentLayer(value)
|
||||||
var diff = (value - sliderRoot.maximumValue) / (sliderRoot.minimumValue - sliderRoot.maximumValue)
|
|
||||||
var newUpperYPosition = Math.round(diff * (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)))
|
var diff = (value - sliderRoot.maximumValue) / (sliderRoot.minimumValue - sliderRoot.maximumValue)
|
||||||
y = newUpperYPosition
|
var newUpperYPosition = Math.round(diff * (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)))
|
||||||
|
y = newUpperYPosition
|
||||||
// update the range handle
|
|
||||||
sliderRoot.updateRangeHandle()
|
// update the range handle
|
||||||
}
|
sliderRoot.updateRangeHandle()
|
||||||
|
}
|
||||||
// dragging
|
|
||||||
MouseArea {
|
Keys.onUpPressed: upperHandleLabel.setValue(upperHandleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
|
||||||
anchors.fill: parent
|
Keys.onDownPressed: upperHandleLabel.setValue(upperHandleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
|
||||||
|
|
||||||
drag {
|
// dragging
|
||||||
target: parent
|
MouseArea {
|
||||||
axis: Drag.YAxis
|
anchors.fill: parent
|
||||||
minimumY: 0
|
|
||||||
maximumY: sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)
|
drag {
|
||||||
}
|
target: parent
|
||||||
|
axis: Drag.YAxis
|
||||||
onPositionChanged: parent.onHandleDragged()
|
minimumY: 0
|
||||||
onPressed: sliderRoot.setActiveHandle(upperHandle)
|
maximumY: sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
LayerSliderLabel {
|
onPositionChanged: parent.onHandleDragged()
|
||||||
id: upperHandleLabel
|
onPressed: {
|
||||||
|
sliderRoot.setActiveHandle(upperHandle)
|
||||||
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
|
upperHandleLabel.forceActiveFocus()
|
||||||
x: parent.x - width - UM.Theme.getSize("default_margin").width
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
}
|
||||||
target: Qt.point(sliderRoot.width, y + height / 2)
|
|
||||||
visible: sliderRoot.activeHandle == parent
|
SimulationSliderLabel {
|
||||||
|
id: upperHandleLabel
|
||||||
// custom properties
|
|
||||||
maximumValue: sliderRoot.maximumValue
|
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
|
||||||
value: sliderRoot.upperValue
|
x: parent.x - width - UM.Theme.getSize("default_margin").width
|
||||||
busy: UM.LayerView.busy
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
setValue: upperHandle.setValue // connect callback functions
|
target: Qt.point(sliderRoot.width, y + height / 2)
|
||||||
}
|
visible: sliderRoot.activeHandle == parent
|
||||||
}
|
|
||||||
|
// custom properties
|
||||||
// Lower handle
|
maximumValue: sliderRoot.maximumValue
|
||||||
Rectangle {
|
value: sliderRoot.upperValue
|
||||||
id: lowerHandle
|
busy: UM.SimulationView.busy
|
||||||
|
setValue: upperHandle.setValue // connect callback functions
|
||||||
y: sliderRoot.height - sliderRoot.handleSize
|
}
|
||||||
width: parent.handleSize
|
}
|
||||||
height: parent.handleSize
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
// Lower handle
|
||||||
radius: sliderRoot.handleRadius
|
Rectangle {
|
||||||
color: sliderRoot.lowerHandleColor
|
id: lowerHandle
|
||||||
|
|
||||||
visible: slider.layersVisible
|
y: sliderRoot.height - sliderRoot.handleSize
|
||||||
|
width: parent.handleSize
|
||||||
function onHandleDragged () {
|
height: parent.handleSize
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
// don't allow the upper handle to be lower than the lower handle
|
radius: sliderRoot.handleRadius
|
||||||
if (y - (upperHandle.y + upperHandle.height) < sliderRoot.minimumRangeHandleSize) {
|
color: lowerHandleLabel.activeFocus ? sliderRoot.handleActiveColor : sliderRoot.lowerHandleColor
|
||||||
upperHandle.y = y - (upperHandle.heigth + sliderRoot.minimumRangeHandleSize)
|
|
||||||
}
|
visible: sliderRoot.layersVisible
|
||||||
|
|
||||||
// update the range handle
|
function onHandleDragged () {
|
||||||
sliderRoot.updateRangeHandle()
|
|
||||||
|
// don't allow the upper handle to be lower than the lower handle
|
||||||
// set the new value after moving the handle position
|
if (y - (upperHandle.y + upperHandle.height) < sliderRoot.minimumRangeHandleSize) {
|
||||||
UM.LayerView.setMinimumLayer(getValue())
|
upperHandle.y = y - (upperHandle.heigth + sliderRoot.minimumRangeHandleSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the lower value from the current slider position
|
// update the range handle
|
||||||
function getValue () {
|
sliderRoot.updateRangeHandle()
|
||||||
var result = (y - (sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)) / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize));
|
|
||||||
result = sliderRoot.maximumValue - sliderRoot.minimumRange + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumRange))
|
// set the new value after moving the handle position
|
||||||
result = sliderRoot.roundValues ? Math.round(result) : result
|
UM.SimulationView.setMinimumLayer(getValue())
|
||||||
return result
|
}
|
||||||
}
|
|
||||||
|
// get the lower value from the current slider position
|
||||||
// set the slider position based on the lower value
|
function getValue () {
|
||||||
function setValue (value) {
|
var result = (y - (sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)) / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize));
|
||||||
|
result = sliderRoot.maximumValue - sliderRoot.minimumRange + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumRange))
|
||||||
UM.LayerView.setMinimumLayer(value)
|
result = sliderRoot.roundValues ? Math.round(result) : result
|
||||||
|
return result
|
||||||
var diff = (value - sliderRoot.maximumValue) / (sliderRoot.minimumValue - sliderRoot.maximumValue)
|
}
|
||||||
var newLowerYPosition = Math.round((sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize) + diff * (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)))
|
|
||||||
y = newLowerYPosition
|
// set the slider position based on the lower value
|
||||||
|
function setValue (value) {
|
||||||
// update the range handle
|
|
||||||
sliderRoot.updateRangeHandle()
|
UM.SimulationView.setMinimumLayer(value)
|
||||||
}
|
|
||||||
|
var diff = (value - sliderRoot.maximumValue) / (sliderRoot.minimumValue - sliderRoot.maximumValue)
|
||||||
// dragging
|
var newLowerYPosition = Math.round((sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize) + diff * (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)))
|
||||||
MouseArea {
|
y = newLowerYPosition
|
||||||
anchors.fill: parent
|
|
||||||
|
// update the range handle
|
||||||
drag {
|
sliderRoot.updateRangeHandle()
|
||||||
target: parent
|
}
|
||||||
axis: Drag.YAxis
|
|
||||||
minimumY: upperHandle.height + sliderRoot.minimumRangeHandleSize
|
Keys.onUpPressed: lowerHandleLabel.setValue(lowerHandleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
|
||||||
maximumY: sliderRoot.height - parent.height
|
Keys.onDownPressed: lowerHandleLabel.setValue(lowerHandleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
|
||||||
}
|
|
||||||
|
// dragging
|
||||||
onPositionChanged: parent.onHandleDragged()
|
MouseArea {
|
||||||
onPressed: sliderRoot.setActiveHandle(lowerHandle)
|
anchors.fill: parent
|
||||||
}
|
|
||||||
|
drag {
|
||||||
LayerSliderLabel {
|
target: parent
|
||||||
id: lowerHandleLabel
|
axis: Drag.YAxis
|
||||||
|
minimumY: upperHandle.height + sliderRoot.minimumRangeHandleSize
|
||||||
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
|
maximumY: sliderRoot.height - parent.height
|
||||||
x: parent.x - width - UM.Theme.getSize("default_margin").width
|
}
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
target: Qt.point(sliderRoot.width, y + height / 2)
|
onPositionChanged: parent.onHandleDragged()
|
||||||
visible: sliderRoot.activeHandle == parent
|
onPressed: {
|
||||||
|
sliderRoot.setActiveHandle(lowerHandle)
|
||||||
// custom properties
|
lowerHandleLabel.forceActiveFocus()
|
||||||
maximumValue: sliderRoot.maximumValue
|
}
|
||||||
value: sliderRoot.lowerValue
|
}
|
||||||
busy: UM.LayerView.busy
|
|
||||||
setValue: lowerHandle.setValue // connect callback functions
|
SimulationSliderLabel {
|
||||||
}
|
id: lowerHandleLabel
|
||||||
}
|
|
||||||
}
|
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
|
||||||
|
x: parent.x - width - UM.Theme.getSize("default_margin").width
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
target: Qt.point(sliderRoot.width, y + height / 2)
|
||||||
|
visible: sliderRoot.activeHandle == parent
|
||||||
|
|
||||||
|
// custom properties
|
||||||
|
maximumValue: sliderRoot.maximumValue
|
||||||
|
value: sliderRoot.lowerValue
|
||||||
|
busy: UM.SimulationView.busy
|
||||||
|
setValue: lowerHandle.setValue // connect callback functions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
plugins/SimulationView/NozzleNode.py
Normal file
49
plugins/SimulationView/NozzleNode.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# Copyright (c) 2017 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from UM.Application import Application
|
||||||
|
from UM.Math.Color import Color
|
||||||
|
from UM.Math.Vector import Vector
|
||||||
|
from UM.PluginRegistry import PluginRegistry
|
||||||
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
from UM.View.GL.OpenGL import OpenGL
|
||||||
|
from UM.Resources import Resources
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
class NozzleNode(SceneNode):
|
||||||
|
def __init__(self, parent = None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self._shader = None
|
||||||
|
self.setCalculateBoundingBox(False)
|
||||||
|
self._createNozzleMesh()
|
||||||
|
|
||||||
|
def _createNozzleMesh(self):
|
||||||
|
mesh_file = "resources/nozzle.stl"
|
||||||
|
try:
|
||||||
|
path = os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), mesh_file)
|
||||||
|
except FileNotFoundError:
|
||||||
|
path = ""
|
||||||
|
|
||||||
|
reader = Application.getInstance().getMeshFileHandler().getReaderForFile(path)
|
||||||
|
node = reader.read(path)
|
||||||
|
|
||||||
|
if node.getMeshData():
|
||||||
|
self.setMeshData(node.getMeshData())
|
||||||
|
|
||||||
|
def render(self, renderer):
|
||||||
|
# Avoid to render if it is not visible
|
||||||
|
if not self.isVisible():
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self._shader:
|
||||||
|
# We now misuse the platform shader, as it actually supports textures
|
||||||
|
self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader"))
|
||||||
|
self._shader.setUniformValue("u_color", Color(*Application.getInstance().getTheme().getColor("layerview_nozzle").getRgb()))
|
||||||
|
# Set the opacity to 0, so that the template is in full control.
|
||||||
|
self._shader.setUniformValue("u_opacity", 0)
|
||||||
|
|
||||||
|
if self.getMeshData():
|
||||||
|
renderer.queueNode(self, shader = self._shader, transparent = True)
|
||||||
|
return True
|
161
plugins/SimulationView/PathSlider.qml
Normal file
161
plugins/SimulationView/PathSlider.qml
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
// Copyright (c) 2017 Ultimaker B.V.
|
||||||
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import QtQuick 2.2
|
||||||
|
import QtQuick.Controls 1.2
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Controls.Styles 1.1
|
||||||
|
|
||||||
|
import UM 1.0 as UM
|
||||||
|
import Cura 1.0 as Cura
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: sliderRoot
|
||||||
|
|
||||||
|
// handle properties
|
||||||
|
property real handleSize: 10
|
||||||
|
property real handleRadius: handleSize / 2
|
||||||
|
property color handleColor: "black"
|
||||||
|
property color handleActiveColor: "white"
|
||||||
|
property color rangeColor: "black"
|
||||||
|
property real handleLabelWidth: width
|
||||||
|
|
||||||
|
// track properties
|
||||||
|
property real trackThickness: 4 // width of the slider track
|
||||||
|
property real trackRadius: trackThickness / 2
|
||||||
|
property color trackColor: "white"
|
||||||
|
property real trackBorderWidth: 1 // width of the slider track border
|
||||||
|
property color trackBorderColor: "black"
|
||||||
|
|
||||||
|
// value properties
|
||||||
|
property real maximumValue: 100
|
||||||
|
property bool roundValues: true
|
||||||
|
property real handleValue: maximumValue
|
||||||
|
|
||||||
|
property bool pathsVisible: true
|
||||||
|
|
||||||
|
function getHandleValueFromSliderHandle () {
|
||||||
|
return handle.getValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setHandleValue (value) {
|
||||||
|
handle.setValue(value)
|
||||||
|
updateRangeHandle()
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateRangeHandle () {
|
||||||
|
rangeHandle.width = handle.x - sliderRoot.handleSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// slider track
|
||||||
|
Rectangle {
|
||||||
|
id: track
|
||||||
|
|
||||||
|
width: sliderRoot.width - sliderRoot.handleSize
|
||||||
|
height: sliderRoot.trackThickness
|
||||||
|
radius: sliderRoot.trackRadius
|
||||||
|
anchors.centerIn: sliderRoot
|
||||||
|
color: sliderRoot.trackColor
|
||||||
|
border.width: sliderRoot.trackBorderWidth
|
||||||
|
border.color: sliderRoot.trackBorderColor
|
||||||
|
visible: sliderRoot.pathsVisible
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress indicator
|
||||||
|
Item {
|
||||||
|
id: rangeHandle
|
||||||
|
|
||||||
|
x: handle.width
|
||||||
|
height: sliderRoot.handleSize
|
||||||
|
width: handle.x - sliderRoot.handleSize
|
||||||
|
anchors.verticalCenter: sliderRoot.verticalCenter
|
||||||
|
visible: sliderRoot.pathsVisible
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: sliderRoot.trackThickness - 2 * sliderRoot.trackBorderWidth
|
||||||
|
width: parent.width + sliderRoot.handleSize
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: sliderRoot.rangeColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle
|
||||||
|
Rectangle {
|
||||||
|
id: handle
|
||||||
|
|
||||||
|
x: sliderRoot.handleSize
|
||||||
|
width: sliderRoot.handleSize
|
||||||
|
height: sliderRoot.handleSize
|
||||||
|
anchors.verticalCenter: sliderRoot.verticalCenter
|
||||||
|
radius: sliderRoot.handleRadius
|
||||||
|
color: handleLabel.activeFocus ? sliderRoot.handleActiveColor : sliderRoot.handleColor
|
||||||
|
visible: sliderRoot.pathsVisible
|
||||||
|
|
||||||
|
function onHandleDragged () {
|
||||||
|
|
||||||
|
// update the range handle
|
||||||
|
sliderRoot.updateRangeHandle()
|
||||||
|
|
||||||
|
// set the new value after moving the handle position
|
||||||
|
UM.SimulationView.setCurrentPath(getValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the value based on the slider position
|
||||||
|
function getValue () {
|
||||||
|
var result = x / (sliderRoot.width - sliderRoot.handleSize)
|
||||||
|
result = result * sliderRoot.maximumValue
|
||||||
|
result = sliderRoot.roundValues ? Math.round(result) : result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the slider position based on the value
|
||||||
|
function setValue (value) {
|
||||||
|
|
||||||
|
UM.SimulationView.setCurrentPath(value)
|
||||||
|
|
||||||
|
var diff = value / sliderRoot.maximumValue
|
||||||
|
var newXPosition = Math.round(diff * (sliderRoot.width - sliderRoot.handleSize))
|
||||||
|
x = newXPosition
|
||||||
|
|
||||||
|
// update the range handle
|
||||||
|
sliderRoot.updateRangeHandle()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onRightPressed: handleLabel.setValue(handleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
|
||||||
|
Keys.onLeftPressed: handleLabel.setValue(handleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
|
||||||
|
|
||||||
|
// dragging
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
drag {
|
||||||
|
target: parent
|
||||||
|
axis: Drag.XAxis
|
||||||
|
minimumX: 0
|
||||||
|
maximumX: sliderRoot.width - sliderRoot.handleSize
|
||||||
|
}
|
||||||
|
onPressed: {
|
||||||
|
handleLabel.forceActiveFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
onPositionChanged: parent.onHandleDragged()
|
||||||
|
}
|
||||||
|
|
||||||
|
SimulationSliderLabel {
|
||||||
|
id: handleLabel
|
||||||
|
|
||||||
|
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
|
||||||
|
y: parent.y + sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
target: Qt.point(x + width / 2, sliderRoot.height)
|
||||||
|
visible: false
|
||||||
|
startFrom: 0
|
||||||
|
|
||||||
|
// custom properties
|
||||||
|
maximumValue: sliderRoot.maximumValue
|
||||||
|
value: sliderRoot.handleValue
|
||||||
|
busy: UM.SimulationView.busy
|
||||||
|
setValue: handle.setValue // connect callback functions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
187
plugins/SimulationView/SimulationPass.py
Normal file
187
plugins/SimulationView/SimulationPass.py
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
# Copyright (c) 2017 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from UM.Math.Color import Color
|
||||||
|
from UM.Math.Vector import Vector
|
||||||
|
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||||
|
from UM.Resources import Resources
|
||||||
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
from UM.Scene.ToolHandle import ToolHandle
|
||||||
|
from UM.Application import Application
|
||||||
|
from UM.PluginRegistry import PluginRegistry
|
||||||
|
|
||||||
|
from UM.View.RenderPass import RenderPass
|
||||||
|
from UM.View.RenderBatch import RenderBatch
|
||||||
|
from UM.View.GL.OpenGL import OpenGL
|
||||||
|
|
||||||
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
|
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
## RenderPass used to display g-code paths.
|
||||||
|
from plugins.SimulationView.NozzleNode import NozzleNode
|
||||||
|
|
||||||
|
|
||||||
|
class SimulationPass(RenderPass):
|
||||||
|
def __init__(self, width, height):
|
||||||
|
super().__init__("simulationview", width, height)
|
||||||
|
|
||||||
|
self._layer_shader = None
|
||||||
|
self._layer_shadow_shader = None
|
||||||
|
self._current_shader = None # This shader will be the shadow or the normal depending if the user wants to see the paths or the layers
|
||||||
|
self._tool_handle_shader = None
|
||||||
|
self._nozzle_shader = None
|
||||||
|
self._old_current_layer = 0
|
||||||
|
self._old_current_path = 0
|
||||||
|
self._gl = OpenGL.getInstance().getBindingsObject()
|
||||||
|
self._scene = Application.getInstance().getController().getScene()
|
||||||
|
self._extruder_manager = ExtruderManager.getInstance()
|
||||||
|
|
||||||
|
self._layer_view = None
|
||||||
|
self._compatibility_mode = None
|
||||||
|
|
||||||
|
def setSimulationView(self, layerview):
|
||||||
|
self._layer_view = layerview
|
||||||
|
self._compatibility_mode = layerview.getCompatibilityMode()
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
if not self._layer_shader:
|
||||||
|
if self._compatibility_mode:
|
||||||
|
shader_filename = "layers.shader"
|
||||||
|
shadow_shader_filename = "layers_shadow.shader"
|
||||||
|
else:
|
||||||
|
shader_filename = "layers3d.shader"
|
||||||
|
shadow_shader_filename = "layers3d_shadow.shader"
|
||||||
|
self._layer_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), shader_filename))
|
||||||
|
self._layer_shadow_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), shadow_shader_filename))
|
||||||
|
self._current_shader = self._layer_shader
|
||||||
|
# Use extruder 0 if the extruder manager reports extruder index -1 (for single extrusion printers)
|
||||||
|
self._layer_shader.setUniformValue("u_active_extruder", float(max(0, self._extruder_manager.activeExtruderIndex)))
|
||||||
|
if self._layer_view:
|
||||||
|
self._layer_shader.setUniformValue("u_max_feedrate", self._layer_view.getMaxFeedrate())
|
||||||
|
self._layer_shader.setUniformValue("u_min_feedrate", self._layer_view.getMinFeedrate())
|
||||||
|
self._layer_shader.setUniformValue("u_max_thickness", self._layer_view.getMaxThickness())
|
||||||
|
self._layer_shader.setUniformValue("u_min_thickness", self._layer_view.getMinThickness())
|
||||||
|
self._layer_shader.setUniformValue("u_layer_view_type", self._layer_view.getSimulationViewType())
|
||||||
|
self._layer_shader.setUniformValue("u_extruder_opacity", self._layer_view.getExtruderOpacities())
|
||||||
|
self._layer_shader.setUniformValue("u_show_travel_moves", self._layer_view.getShowTravelMoves())
|
||||||
|
self._layer_shader.setUniformValue("u_show_helpers", self._layer_view.getShowHelpers())
|
||||||
|
self._layer_shader.setUniformValue("u_show_skin", self._layer_view.getShowSkin())
|
||||||
|
self._layer_shader.setUniformValue("u_show_infill", self._layer_view.getShowInfill())
|
||||||
|
else:
|
||||||
|
#defaults
|
||||||
|
self._layer_shader.setUniformValue("u_max_feedrate", 1)
|
||||||
|
self._layer_shader.setUniformValue("u_min_feedrate", 0)
|
||||||
|
self._layer_shader.setUniformValue("u_max_thickness", 1)
|
||||||
|
self._layer_shader.setUniformValue("u_min_thickness", 0)
|
||||||
|
self._layer_shader.setUniformValue("u_layer_view_type", 1)
|
||||||
|
self._layer_shader.setUniformValue("u_extruder_opacity", [1, 1, 1, 1])
|
||||||
|
self._layer_shader.setUniformValue("u_show_travel_moves", 0)
|
||||||
|
self._layer_shader.setUniformValue("u_show_helpers", 1)
|
||||||
|
self._layer_shader.setUniformValue("u_show_skin", 1)
|
||||||
|
self._layer_shader.setUniformValue("u_show_infill", 1)
|
||||||
|
|
||||||
|
if not self._tool_handle_shader:
|
||||||
|
self._tool_handle_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "toolhandle.shader"))
|
||||||
|
|
||||||
|
if not self._nozzle_shader:
|
||||||
|
self._nozzle_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader"))
|
||||||
|
self._nozzle_shader.setUniformValue("u_color", Color(*Application.getInstance().getTheme().getColor("layerview_nozzle").getRgb()))
|
||||||
|
|
||||||
|
self.bind()
|
||||||
|
|
||||||
|
tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Solid)
|
||||||
|
head_position = None # Indicates the current position of the print head
|
||||||
|
nozzle_node = None
|
||||||
|
|
||||||
|
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
|
|
||||||
|
if isinstance(node, ToolHandle):
|
||||||
|
tool_handle_batch.addItem(node.getWorldTransformation(), mesh = node.getSolidMesh())
|
||||||
|
|
||||||
|
|
||||||
|
elif isinstance(node, NozzleNode):
|
||||||
|
nozzle_node = node
|
||||||
|
nozzle_node.setVisible(False)
|
||||||
|
|
||||||
|
elif isinstance(node, SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
|
||||||
|
layer_data = node.callDecoration("getLayerData")
|
||||||
|
if not layer_data:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Render all layers below a certain number as line mesh instead of vertices.
|
||||||
|
if self._layer_view._current_layer_num > -1 and ((not self._layer_view._only_show_top_layers) or (not self._layer_view.getCompatibilityMode())):
|
||||||
|
start = 0
|
||||||
|
end = 0
|
||||||
|
element_counts = layer_data.getElementCounts()
|
||||||
|
for layer in sorted(element_counts.keys()):
|
||||||
|
# In the current layer, we show just the indicated paths
|
||||||
|
if layer == self._layer_view._current_layer_num:
|
||||||
|
# We look for the position of the head, searching the point of the current path
|
||||||
|
index = self._layer_view._current_path_num
|
||||||
|
offset = 0
|
||||||
|
for polygon in layer_data.getLayer(layer).polygons:
|
||||||
|
# The size indicates all values in the two-dimension array, and the second dimension is
|
||||||
|
# always size 3 because we have 3D points.
|
||||||
|
if index >= polygon.data.size // 3 - offset:
|
||||||
|
index -= polygon.data.size // 3 - offset
|
||||||
|
offset = 1 # This is to avoid the first point when there is more than one polygon, since has the same value as the last point in the previous polygon
|
||||||
|
continue
|
||||||
|
# The head position is calculated and translated
|
||||||
|
head_position = Vector(polygon.data[index+offset][0], polygon.data[index+offset][1], polygon.data[index+offset][2]) + node.getWorldPosition()
|
||||||
|
break
|
||||||
|
break
|
||||||
|
if self._layer_view._minimum_layer_num > layer:
|
||||||
|
start += element_counts[layer]
|
||||||
|
end += element_counts[layer]
|
||||||
|
|
||||||
|
# Calculate the range of paths in the last layer
|
||||||
|
current_layer_start = end
|
||||||
|
current_layer_end = end + self._layer_view._current_path_num * 2 # Because each point is used twice
|
||||||
|
|
||||||
|
# This uses glDrawRangeElements internally to only draw a certain range of lines.
|
||||||
|
# All the layers but the current selected layer are rendered first
|
||||||
|
if self._old_current_path != self._layer_view._current_path_num:
|
||||||
|
self._current_shader = self._layer_shadow_shader
|
||||||
|
if not self._layer_view.isSimulationRunning() and self._old_current_layer != self._layer_view._current_layer_num:
|
||||||
|
self._current_shader = self._layer_shader
|
||||||
|
|
||||||
|
layers_batch = RenderBatch(self._current_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (start, end))
|
||||||
|
layers_batch.addItem(node.getWorldTransformation(), layer_data)
|
||||||
|
layers_batch.render(self._scene.getActiveCamera())
|
||||||
|
|
||||||
|
# Current selected layer is rendered
|
||||||
|
current_layer_batch = RenderBatch(self._layer_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (current_layer_start, current_layer_end))
|
||||||
|
current_layer_batch.addItem(node.getWorldTransformation(), layer_data)
|
||||||
|
current_layer_batch.render(self._scene.getActiveCamera())
|
||||||
|
|
||||||
|
self._old_current_layer = self._layer_view._current_layer_num
|
||||||
|
self._old_current_path = self._layer_view._current_path_num
|
||||||
|
|
||||||
|
# Create a new batch that is not range-limited
|
||||||
|
batch = RenderBatch(self._layer_shader, type = RenderBatch.RenderType.Solid)
|
||||||
|
|
||||||
|
if self._layer_view.getCurrentLayerMesh():
|
||||||
|
batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerMesh())
|
||||||
|
|
||||||
|
if self._layer_view.getCurrentLayerJumps():
|
||||||
|
batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerJumps())
|
||||||
|
|
||||||
|
if len(batch.items) > 0:
|
||||||
|
batch.render(self._scene.getActiveCamera())
|
||||||
|
|
||||||
|
# The nozzle is drawn once we know the correct position
|
||||||
|
if self._layer_view.getActivity() and nozzle_node is not None:
|
||||||
|
if head_position is not None:
|
||||||
|
nozzle_node.setVisible(True)
|
||||||
|
nozzle_node.setPosition(head_position)
|
||||||
|
nozzle_batch = RenderBatch(self._nozzle_shader, type = RenderBatch.RenderType.Solid)
|
||||||
|
nozzle_batch.addItem(nozzle_node.getWorldTransformation(), mesh = nozzle_node.getMeshData())
|
||||||
|
nozzle_batch.render(self._scene.getActiveCamera())
|
||||||
|
|
||||||
|
# Render toolhandles on top of the layerview
|
||||||
|
if len(tool_handle_batch.items) > 0:
|
||||||
|
tool_handle_batch.render(self._scene.getActiveCamera())
|
||||||
|
|
||||||
|
self.release()
|
|
@ -1,103 +1,104 @@
|
||||||
// Copyright (c) 2017 Ultimaker B.V.
|
// Copyright (c) 2017 Ultimaker B.V.
|
||||||
// Cura is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.2
|
import QtQuick 2.2
|
||||||
import QtQuick.Controls 1.2
|
import QtQuick.Controls 1.2
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.1
|
||||||
import QtQuick.Controls.Styles 1.1
|
import QtQuick.Controls.Styles 1.1
|
||||||
|
|
||||||
import UM 1.0 as UM
|
import UM 1.0 as UM
|
||||||
import Cura 1.0 as Cura
|
import Cura 1.0 as Cura
|
||||||
|
|
||||||
UM.PointingRectangle {
|
UM.PointingRectangle {
|
||||||
id: sliderLabelRoot
|
id: sliderLabelRoot
|
||||||
|
|
||||||
// custom properties
|
// custom properties
|
||||||
property real maximumValue: 100
|
property real maximumValue: 100
|
||||||
property real value: 0
|
property real value: 0
|
||||||
property var setValue // Function
|
property var setValue // Function
|
||||||
property bool busy: false
|
property bool busy: false
|
||||||
|
property int startFrom: 1
|
||||||
target: Qt.point(parent.width, y + height / 2)
|
|
||||||
arrowSize: UM.Theme.getSize("default_arrow").width
|
target: Qt.point(parent.width, y + height / 2)
|
||||||
height: parent.height
|
arrowSize: UM.Theme.getSize("default_arrow").width
|
||||||
width: valueLabel.width + UM.Theme.getSize("default_margin").width
|
height: parent.height
|
||||||
visible: false
|
width: valueLabel.width + UM.Theme.getSize("default_margin").width
|
||||||
|
visible: false
|
||||||
// make sure the text field is focussed when pressing the parent handle
|
|
||||||
// needed to connect the key bindings when switching active handle
|
// make sure the text field is focussed when pressing the parent handle
|
||||||
onVisibleChanged: if (visible) valueLabel.forceActiveFocus()
|
// needed to connect the key bindings when switching active handle
|
||||||
|
onVisibleChanged: if (visible) valueLabel.forceActiveFocus()
|
||||||
color: UM.Theme.getColor("tool_panel_background")
|
|
||||||
borderColor: UM.Theme.getColor("lining")
|
color: UM.Theme.getColor("tool_panel_background")
|
||||||
borderWidth: UM.Theme.getSize("default_lining").width
|
borderColor: UM.Theme.getColor("lining")
|
||||||
|
borderWidth: UM.Theme.getSize("default_lining").width
|
||||||
Behavior on height {
|
|
||||||
NumberAnimation {
|
Behavior on height {
|
||||||
duration: 50
|
NumberAnimation {
|
||||||
}
|
duration: 50
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// catch all mouse events so they're not handled by underlying 3D scene
|
|
||||||
MouseArea {
|
// catch all mouse events so they're not handled by underlying 3D scene
|
||||||
anchors.fill: parent
|
MouseArea {
|
||||||
}
|
anchors.fill: parent
|
||||||
|
}
|
||||||
TextField {
|
|
||||||
id: valueLabel
|
TextField {
|
||||||
|
id: valueLabel
|
||||||
anchors {
|
|
||||||
left: parent.left
|
anchors {
|
||||||
leftMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
|
left: parent.left
|
||||||
verticalCenter: parent.verticalCenter
|
leftMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
|
||||||
}
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
width: 40 * screenScaleFactor
|
|
||||||
text: sliderLabelRoot.value + 1 // the current handle value, add 1 because layers is an array
|
width: 40 * screenScaleFactor
|
||||||
horizontalAlignment: TextInput.AlignRight
|
text: sliderLabelRoot.value + startFrom // the current handle value, add 1 because layers is an array
|
||||||
|
horizontalAlignment: TextInput.AlignRight
|
||||||
// key bindings, work when label is currenctly focused (active handle in LayerSlider)
|
|
||||||
Keys.onUpPressed: sliderLabelRoot.setValue(sliderLabelRoot.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
|
// key bindings, work when label is currenctly focused (active handle in LayerSlider)
|
||||||
Keys.onDownPressed: sliderLabelRoot.setValue(sliderLabelRoot.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
|
Keys.onUpPressed: sliderLabelRoot.setValue(sliderLabelRoot.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
|
||||||
|
Keys.onDownPressed: sliderLabelRoot.setValue(sliderLabelRoot.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
|
||||||
style: TextFieldStyle {
|
|
||||||
textColor: UM.Theme.getColor("setting_control_text")
|
style: TextFieldStyle {
|
||||||
font: UM.Theme.getFont("default")
|
textColor: UM.Theme.getColor("setting_control_text")
|
||||||
background: Item { }
|
font: UM.Theme.getFont("default")
|
||||||
}
|
background: Item { }
|
||||||
|
}
|
||||||
onEditingFinished: {
|
|
||||||
|
onEditingFinished: {
|
||||||
// Ensure that the cursor is at the first position. On some systems the text isn't fully visible
|
|
||||||
// Seems to have to do something with different dpi densities that QML doesn't quite handle.
|
// Ensure that the cursor is at the first position. On some systems the text isn't fully visible
|
||||||
// Another option would be to increase the size even further, but that gives pretty ugly results.
|
// Seems to have to do something with different dpi densities that QML doesn't quite handle.
|
||||||
cursorPosition = 0
|
// Another option would be to increase the size even further, but that gives pretty ugly results.
|
||||||
|
cursorPosition = 0
|
||||||
if (valueLabel.text != "") {
|
|
||||||
// -1 because we need to convert back to an array structure
|
if (valueLabel.text != "") {
|
||||||
sliderLabelRoot.setValue(parseInt(valueLabel.text) - 1)
|
// -startFrom because we need to convert back to an array structure
|
||||||
}
|
sliderLabelRoot.setValue(parseInt(valueLabel.text) - startFrom)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
validator: IntValidator {
|
|
||||||
bottom: 1
|
validator: IntValidator {
|
||||||
top: sliderLabelRoot.maximumValue + 1 // +1 because actual layers is an array
|
bottom:startFrom
|
||||||
}
|
top: sliderLabelRoot.maximumValue + startFrom // +startFrom because maybe we want to start in a different value rather than 0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
BusyIndicator {
|
|
||||||
id: busyIndicator
|
BusyIndicator {
|
||||||
|
id: busyIndicator
|
||||||
anchors {
|
|
||||||
left: parent.right
|
anchors {
|
||||||
leftMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
|
left: parent.right
|
||||||
verticalCenter: parent.verticalCenter
|
leftMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
|
||||||
}
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
width: sliderLabelRoot.height
|
|
||||||
height: width
|
width: sliderLabelRoot.height
|
||||||
|
height: width
|
||||||
visible: sliderLabelRoot.busy
|
|
||||||
running: sliderLabelRoot.busy
|
visible: sliderLabelRoot.busy
|
||||||
}
|
running: sliderLabelRoot.busy
|
||||||
}
|
}
|
||||||
|
}
|
216
plugins/LayerView/LayerView.py → plugins/SimulationView/SimulationView.py
Executable file → Normal file
216
plugins/LayerView/LayerView.py → plugins/SimulationView/SimulationView.py
Executable file → Normal file
|
@ -1,46 +1,46 @@
|
||||||
# Copyright (c) 2015 Ultimaker B.V.
|
# Copyright (c) 2017 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from UM.PluginRegistry import PluginRegistry
|
|
||||||
from UM.View.View import View
|
|
||||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|
||||||
from UM.Resources import Resources
|
|
||||||
from UM.Event import Event, KeyEvent
|
|
||||||
from UM.Signal import Signal
|
|
||||||
from UM.Scene.Selection import Selection
|
|
||||||
from UM.Math.Color import Color
|
|
||||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
|
||||||
from UM.Job import Job
|
|
||||||
from UM.Preferences import Preferences
|
|
||||||
from UM.Logger import Logger
|
|
||||||
from UM.View.GL.OpenGL import OpenGL
|
|
||||||
from UM.Message import Message
|
|
||||||
from UM.Application import Application
|
|
||||||
from UM.View.GL.OpenGLContext import OpenGLContext
|
|
||||||
|
|
||||||
from cura.ConvexHullNode import ConvexHullNode
|
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QApplication
|
from PyQt5.QtWidgets import QApplication
|
||||||
|
|
||||||
from . import LayerViewProxy
|
from UM.Application import Application
|
||||||
|
from UM.Event import Event, KeyEvent
|
||||||
|
from UM.Job import Job
|
||||||
|
from UM.Logger import Logger
|
||||||
|
from UM.Math.Color import Color
|
||||||
|
from UM.Math.Vector import Vector
|
||||||
|
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||||
|
from UM.Message import Message
|
||||||
|
from UM.PluginRegistry import PluginRegistry
|
||||||
|
from UM.Preferences import Preferences
|
||||||
|
from UM.Resources import Resources
|
||||||
|
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||||
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
from UM.Scene.Selection import Selection
|
||||||
|
from UM.Signal import Signal
|
||||||
|
from UM.View.GL.OpenGL import OpenGL
|
||||||
|
from UM.View.GL.OpenGLContext import OpenGLContext
|
||||||
|
from UM.View.View import View
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
catalog = i18nCatalog("cura")
|
from cura.ConvexHullNode import ConvexHullNode
|
||||||
|
from plugins.SimulationView.NozzleNode import NozzleNode
|
||||||
|
from . import SimulationPass, SimulationViewProxy
|
||||||
|
|
||||||
from . import LayerPass
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
## View used to display g-code paths.
|
## View used to display g-code paths.
|
||||||
class LayerView(View):
|
class SimulationView(View):
|
||||||
# Must match LayerView.qml
|
# Must match SimulationView.qml
|
||||||
LAYER_VIEW_TYPE_MATERIAL_TYPE = 0
|
LAYER_VIEW_TYPE_MATERIAL_TYPE = 0
|
||||||
LAYER_VIEW_TYPE_LINE_TYPE = 1
|
LAYER_VIEW_TYPE_LINE_TYPE = 1
|
||||||
|
LAYER_VIEW_TYPE_FEEDRATE = 2
|
||||||
|
LAYER_VIEW_TYPE_THICKNESS = 3
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -54,22 +54,29 @@ class LayerView(View):
|
||||||
self._activity = False
|
self._activity = False
|
||||||
self._old_max_layers = 0
|
self._old_max_layers = 0
|
||||||
|
|
||||||
|
self._max_paths = 0
|
||||||
|
self._current_path_num = 0
|
||||||
|
self._minimum_path_num = 0
|
||||||
|
self.currentLayerNumChanged.connect(self._onCurrentLayerNumChanged)
|
||||||
|
|
||||||
self._busy = False
|
self._busy = False
|
||||||
|
self._simulation_running = False
|
||||||
|
|
||||||
self._ghost_shader = None
|
self._ghost_shader = None
|
||||||
self._layer_pass = None
|
self._layer_pass = None
|
||||||
self._composite_pass = None
|
self._composite_pass = None
|
||||||
self._old_layer_bindings = None
|
self._old_layer_bindings = None
|
||||||
self._layerview_composite_shader = None
|
self._simulationview_composite_shader = None
|
||||||
self._old_composite_shader = None
|
self._old_composite_shader = None
|
||||||
|
|
||||||
self._global_container_stack = None
|
self._global_container_stack = None
|
||||||
self._proxy = LayerViewProxy.LayerViewProxy()
|
self._proxy = SimulationViewProxy.SimulationViewProxy()
|
||||||
self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged)
|
self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged)
|
||||||
|
|
||||||
self._resetSettings()
|
self._resetSettings()
|
||||||
self._legend_items = None
|
self._legend_items = None
|
||||||
self._show_travel_moves = False
|
self._show_travel_moves = False
|
||||||
|
self._nozzle_node = None
|
||||||
|
|
||||||
Preferences.getInstance().addPreference("view/top_layer_count", 5)
|
Preferences.getInstance().addPreference("view/top_layer_count", 5)
|
||||||
Preferences.getInstance().addPreference("view/only_show_top_layers", False)
|
Preferences.getInstance().addPreference("view/only_show_top_layers", False)
|
||||||
|
@ -91,7 +98,7 @@ class LayerView(View):
|
||||||
self._compatibility_mode = True # for safety
|
self._compatibility_mode = True # for safety
|
||||||
|
|
||||||
self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"),
|
self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"),
|
||||||
title = catalog.i18nc("@info:title", "Layer View"))
|
title = catalog.i18nc("@info:title", "Simulation View"))
|
||||||
|
|
||||||
def _resetSettings(self):
|
def _resetSettings(self):
|
||||||
self._layer_view_type = 0 # 0 is material color, 1 is color by linetype, 2 is speed
|
self._layer_view_type = 0 # 0 is material color, 1 is color by linetype, 2 is speed
|
||||||
|
@ -101,17 +108,24 @@ class LayerView(View):
|
||||||
self._show_helpers = 1
|
self._show_helpers = 1
|
||||||
self._show_skin = 1
|
self._show_skin = 1
|
||||||
self._show_infill = 1
|
self._show_infill = 1
|
||||||
|
self.resetLayerData()
|
||||||
|
|
||||||
def getActivity(self):
|
def getActivity(self):
|
||||||
return self._activity
|
return self._activity
|
||||||
|
|
||||||
def getLayerPass(self):
|
def setActivity(self, activity):
|
||||||
|
if self._activity == activity:
|
||||||
|
return
|
||||||
|
self._activity = activity
|
||||||
|
self.activityChanged.emit()
|
||||||
|
|
||||||
|
def getSimulationPass(self):
|
||||||
if not self._layer_pass:
|
if not self._layer_pass:
|
||||||
# Currently the RenderPass constructor requires a size > 0
|
# Currently the RenderPass constructor requires a size > 0
|
||||||
# This should be fixed in RenderPass's constructor.
|
# This should be fixed in RenderPass's constructor.
|
||||||
self._layer_pass = LayerPass.LayerPass(1, 1)
|
self._layer_pass = SimulationPass.SimulationPass(1, 1)
|
||||||
self._compatibility_mode = OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode"))
|
self._compatibility_mode = OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode"))
|
||||||
self._layer_pass.setLayerView(self)
|
self._layer_pass.setSimulationView(self)
|
||||||
return self._layer_pass
|
return self._layer_pass
|
||||||
|
|
||||||
def getCurrentLayer(self):
|
def getCurrentLayer(self):
|
||||||
|
@ -120,13 +134,26 @@ class LayerView(View):
|
||||||
def getMinimumLayer(self):
|
def getMinimumLayer(self):
|
||||||
return self._minimum_layer_num
|
return self._minimum_layer_num
|
||||||
|
|
||||||
def _onSceneChanged(self, node):
|
|
||||||
self.calculateMaxLayers()
|
|
||||||
|
|
||||||
def getMaxLayers(self):
|
def getMaxLayers(self):
|
||||||
return self._max_layers
|
return self._max_layers
|
||||||
|
|
||||||
busyChanged = Signal()
|
def getCurrentPath(self):
|
||||||
|
return self._current_path_num
|
||||||
|
|
||||||
|
def getMinimumPath(self):
|
||||||
|
return self._minimum_path_num
|
||||||
|
|
||||||
|
def getMaxPaths(self):
|
||||||
|
return self._max_paths
|
||||||
|
|
||||||
|
def getNozzleNode(self):
|
||||||
|
if not self._nozzle_node:
|
||||||
|
self._nozzle_node = NozzleNode()
|
||||||
|
return self._nozzle_node
|
||||||
|
|
||||||
|
def _onSceneChanged(self, node):
|
||||||
|
self.setActivity(False)
|
||||||
|
self.calculateMaxLayers()
|
||||||
|
|
||||||
def isBusy(self):
|
def isBusy(self):
|
||||||
return self._busy
|
return self._busy
|
||||||
|
@ -136,9 +163,19 @@ class LayerView(View):
|
||||||
self._busy = busy
|
self._busy = busy
|
||||||
self.busyChanged.emit()
|
self.busyChanged.emit()
|
||||||
|
|
||||||
|
def isSimulationRunning(self):
|
||||||
|
return self._simulation_running
|
||||||
|
|
||||||
|
def setSimulationRunning(self, running):
|
||||||
|
self._simulation_running = running
|
||||||
|
|
||||||
def resetLayerData(self):
|
def resetLayerData(self):
|
||||||
self._current_layer_mesh = None
|
self._current_layer_mesh = None
|
||||||
self._current_layer_jumps = None
|
self._current_layer_jumps = None
|
||||||
|
self._max_feedrate = sys.float_info.min
|
||||||
|
self._min_feedrate = sys.float_info.max
|
||||||
|
self._max_thickness = sys.float_info.min
|
||||||
|
self._min_thickness = sys.float_info.max
|
||||||
|
|
||||||
def beginRendering(self):
|
def beginRendering(self):
|
||||||
scene = self.getController().getScene()
|
scene = self.getController().getScene()
|
||||||
|
@ -186,15 +223,43 @@ class LayerView(View):
|
||||||
|
|
||||||
self.currentLayerNumChanged.emit()
|
self.currentLayerNumChanged.emit()
|
||||||
|
|
||||||
|
def setPath(self, value):
|
||||||
|
if self._current_path_num != value:
|
||||||
|
self._current_path_num = value
|
||||||
|
if self._current_path_num < 0:
|
||||||
|
self._current_path_num = 0
|
||||||
|
if self._current_path_num > self._max_paths:
|
||||||
|
self._current_path_num = self._max_paths
|
||||||
|
if self._current_path_num < self._minimum_path_num:
|
||||||
|
self._minimum_path_num = self._current_path_num
|
||||||
|
|
||||||
|
self._startUpdateTopLayers()
|
||||||
|
|
||||||
|
self.currentPathNumChanged.emit()
|
||||||
|
|
||||||
|
def setMinimumPath(self, value):
|
||||||
|
if self._minimum_path_num != value:
|
||||||
|
self._minimum_path_num = value
|
||||||
|
if self._minimum_path_num < 0:
|
||||||
|
self._minimum_path_num = 0
|
||||||
|
if self._minimum_path_num > self._max_layers:
|
||||||
|
self._minimum_path_num = self._max_layers
|
||||||
|
if self._minimum_path_num > self._current_path_num:
|
||||||
|
self._current_path_num = self._minimum_path_num
|
||||||
|
|
||||||
|
self._startUpdateTopLayers()
|
||||||
|
|
||||||
|
self.currentPathNumChanged.emit()
|
||||||
|
|
||||||
## Set the layer view type
|
## Set the layer view type
|
||||||
#
|
#
|
||||||
# \param layer_view_type integer as in LayerView.qml and this class
|
# \param layer_view_type integer as in SimulationView.qml and this class
|
||||||
def setLayerViewType(self, layer_view_type):
|
def setSimulationViewType(self, layer_view_type):
|
||||||
self._layer_view_type = layer_view_type
|
self._layer_view_type = layer_view_type
|
||||||
self.currentLayerNumChanged.emit()
|
self.currentLayerNumChanged.emit()
|
||||||
|
|
||||||
## Return the layer view type, integer as in LayerView.qml and this class
|
## Return the layer view type, integer as in SimulationView.qml and this class
|
||||||
def getLayerViewType(self):
|
def getSimulationViewType(self):
|
||||||
return self._layer_view_type
|
return self._layer_view_type
|
||||||
|
|
||||||
## Set the extruder opacity
|
## Set the extruder opacity
|
||||||
|
@ -243,9 +308,20 @@ class LayerView(View):
|
||||||
def getExtruderCount(self):
|
def getExtruderCount(self):
|
||||||
return self._extruder_count
|
return self._extruder_count
|
||||||
|
|
||||||
|
def getMinFeedrate(self):
|
||||||
|
return self._min_feedrate
|
||||||
|
|
||||||
|
def getMaxFeedrate(self):
|
||||||
|
return self._max_feedrate
|
||||||
|
|
||||||
|
def getMinThickness(self):
|
||||||
|
return self._min_thickness
|
||||||
|
|
||||||
|
def getMaxThickness(self):
|
||||||
|
return self._max_thickness
|
||||||
|
|
||||||
def calculateMaxLayers(self):
|
def calculateMaxLayers(self):
|
||||||
scene = self.getController().getScene()
|
scene = self.getController().getScene()
|
||||||
self._activity = True
|
|
||||||
|
|
||||||
self._old_max_layers = self._max_layers
|
self._old_max_layers = self._max_layers
|
||||||
## Recalculate num max layers
|
## Recalculate num max layers
|
||||||
|
@ -255,9 +331,16 @@ class LayerView(View):
|
||||||
if not layer_data:
|
if not layer_data:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
self.setActivity(True)
|
||||||
min_layer_number = sys.maxsize
|
min_layer_number = sys.maxsize
|
||||||
max_layer_number = -sys.maxsize
|
max_layer_number = -sys.maxsize
|
||||||
for layer_id in layer_data.getLayers():
|
for layer_id in layer_data.getLayers():
|
||||||
|
# Store the max and min feedrates and thicknesses for display purposes
|
||||||
|
for p in layer_data.getLayer(layer_id).polygons:
|
||||||
|
self._max_feedrate = max(float(p.lineFeedrates.max()), self._max_feedrate)
|
||||||
|
self._min_feedrate = min(float(p.lineFeedrates.min()), self._min_feedrate)
|
||||||
|
self._max_thickness = max(float(p.lineThicknesses.max()), self._max_thickness)
|
||||||
|
self._min_thickness = min(float(p.lineThicknesses.min()), self._min_thickness)
|
||||||
if max_layer_number < layer_id:
|
if max_layer_number < layer_id:
|
||||||
max_layer_number = layer_id
|
max_layer_number = layer_id
|
||||||
if min_layer_number > layer_id:
|
if min_layer_number > layer_id:
|
||||||
|
@ -281,10 +364,32 @@ class LayerView(View):
|
||||||
self.maxLayersChanged.emit()
|
self.maxLayersChanged.emit()
|
||||||
self._startUpdateTopLayers()
|
self._startUpdateTopLayers()
|
||||||
|
|
||||||
|
def calculateMaxPathsOnLayer(self, layer_num):
|
||||||
|
# Update the currentPath
|
||||||
|
scene = self.getController().getScene()
|
||||||
|
for node in DepthFirstIterator(scene.getRoot()):
|
||||||
|
layer_data = node.callDecoration("getLayerData")
|
||||||
|
if not layer_data:
|
||||||
|
continue
|
||||||
|
|
||||||
|
layer = layer_data.getLayer(layer_num)
|
||||||
|
if layer is None:
|
||||||
|
return
|
||||||
|
new_max_paths = layer.lineMeshElementCount()
|
||||||
|
if new_max_paths > 0 and new_max_paths != self._max_paths:
|
||||||
|
self._max_paths = new_max_paths
|
||||||
|
self.maxPathsChanged.emit()
|
||||||
|
|
||||||
|
self.setPath(int(new_max_paths))
|
||||||
|
|
||||||
maxLayersChanged = Signal()
|
maxLayersChanged = Signal()
|
||||||
|
maxPathsChanged = Signal()
|
||||||
currentLayerNumChanged = Signal()
|
currentLayerNumChanged = Signal()
|
||||||
|
currentPathNumChanged = Signal()
|
||||||
globalStackChanged = Signal()
|
globalStackChanged = Signal()
|
||||||
preferencesChanged = Signal()
|
preferencesChanged = Signal()
|
||||||
|
busyChanged = Signal()
|
||||||
|
activityChanged = Signal()
|
||||||
|
|
||||||
## Hackish way to ensure the proxy is already created, which ensures that the layerview.qml is already created
|
## Hackish way to ensure the proxy is already created, which ensures that the layerview.qml is already created
|
||||||
# as this caused some issues.
|
# as this caused some issues.
|
||||||
|
@ -308,26 +413,31 @@ class LayerView(View):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if event.type == Event.ViewActivateEvent:
|
if event.type == Event.ViewActivateEvent:
|
||||||
# Make sure the LayerPass is created
|
# Make sure the SimulationPass is created
|
||||||
layer_pass = self.getLayerPass()
|
layer_pass = self.getSimulationPass()
|
||||||
self.getRenderer().addRenderPass(layer_pass)
|
self.getRenderer().addRenderPass(layer_pass)
|
||||||
|
|
||||||
|
# Make sure the NozzleNode is add to the root
|
||||||
|
nozzle = self.getNozzleNode()
|
||||||
|
nozzle.setParent(self.getController().getScene().getRoot())
|
||||||
|
nozzle.setVisible(False)
|
||||||
|
|
||||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||||
self._onGlobalStackChanged()
|
self._onGlobalStackChanged()
|
||||||
|
|
||||||
if not self._layerview_composite_shader:
|
if not self._simulationview_composite_shader:
|
||||||
self._layerview_composite_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("LayerView"), "layerview_composite.shader"))
|
self._simulationview_composite_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), "simulationview_composite.shader"))
|
||||||
theme = Application.getInstance().getTheme()
|
theme = Application.getInstance().getTheme()
|
||||||
self._layerview_composite_shader.setUniformValue("u_background_color", Color(*theme.getColor("viewport_background").getRgb()))
|
self._simulationview_composite_shader.setUniformValue("u_background_color", Color(*theme.getColor("viewport_background").getRgb()))
|
||||||
self._layerview_composite_shader.setUniformValue("u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb()))
|
self._simulationview_composite_shader.setUniformValue("u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb()))
|
||||||
|
|
||||||
if not self._composite_pass:
|
if not self._composite_pass:
|
||||||
self._composite_pass = self.getRenderer().getRenderPass("composite")
|
self._composite_pass = self.getRenderer().getRenderPass("composite")
|
||||||
|
|
||||||
self._old_layer_bindings = self._composite_pass.getLayerBindings()[:] # make a copy so we can restore to it later
|
self._old_layer_bindings = self._composite_pass.getLayerBindings()[:] # make a copy so we can restore to it later
|
||||||
self._composite_pass.getLayerBindings().append("layerview")
|
self._composite_pass.getLayerBindings().append("simulationview")
|
||||||
self._old_composite_shader = self._composite_pass.getCompositeShader()
|
self._old_composite_shader = self._composite_pass.getCompositeShader()
|
||||||
self._composite_pass.setCompositeShader(self._layerview_composite_shader)
|
self._composite_pass.setCompositeShader(self._simulationview_composite_shader)
|
||||||
|
|
||||||
elif event.type == Event.ViewDeactivateEvent:
|
elif event.type == Event.ViewDeactivateEvent:
|
||||||
self._wireprint_warning_message.hide()
|
self._wireprint_warning_message.hide()
|
||||||
|
@ -335,6 +445,7 @@ class LayerView(View):
|
||||||
if self._global_container_stack:
|
if self._global_container_stack:
|
||||||
self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
|
self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
|
||||||
|
|
||||||
|
self._nozzle_node.setParent(None)
|
||||||
self.getRenderer().removeRenderPass(self._layer_pass)
|
self.getRenderer().removeRenderPass(self._layer_pass)
|
||||||
self._composite_pass.setLayerBindings(self._old_layer_bindings)
|
self._composite_pass.setLayerBindings(self._old_layer_bindings)
|
||||||
self._composite_pass.setCompositeShader(self._old_composite_shader)
|
self._composite_pass.setCompositeShader(self._old_composite_shader)
|
||||||
|
@ -364,6 +475,9 @@ class LayerView(View):
|
||||||
else:
|
else:
|
||||||
self._wireprint_warning_message.hide()
|
self._wireprint_warning_message.hide()
|
||||||
|
|
||||||
|
def _onCurrentLayerNumChanged(self):
|
||||||
|
self.calculateMaxPathsOnLayer(self._current_layer_num)
|
||||||
|
|
||||||
def _startUpdateTopLayers(self):
|
def _startUpdateTopLayers(self):
|
||||||
if not self._compatibility_mode:
|
if not self._compatibility_mode:
|
||||||
return
|
return
|
||||||
|
@ -397,7 +511,7 @@ class LayerView(View):
|
||||||
self._compatibility_mode = OpenGLContext.isLegacyOpenGL() or bool(
|
self._compatibility_mode = OpenGLContext.isLegacyOpenGL() or bool(
|
||||||
Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode"))
|
Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode"))
|
||||||
|
|
||||||
self.setLayerViewType(int(float(Preferences.getInstance().getValue("layerview/layer_view_type"))));
|
self.setSimulationViewType(int(float(Preferences.getInstance().getValue("layerview/layer_view_type"))));
|
||||||
|
|
||||||
for extruder_nr, extruder_opacity in enumerate(Preferences.getInstance().getValue("layerview/extruder_opacities").split("|")):
|
for extruder_nr, extruder_opacity in enumerate(Preferences.getInstance().getValue("layerview/extruder_opacities").split("|")):
|
||||||
try:
|
try:
|
645
plugins/SimulationView/SimulationView.qml
Normal file
645
plugins/SimulationView/SimulationView.qml
Normal file
|
@ -0,0 +1,645 @@
|
||||||
|
// Copyright (c) 2017 Ultimaker B.V.
|
||||||
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import QtQuick 2.4
|
||||||
|
import QtQuick.Controls 1.2
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Controls.Styles 1.1
|
||||||
|
|
||||||
|
import UM 1.0 as UM
|
||||||
|
import Cura 1.0 as Cura
|
||||||
|
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: base
|
||||||
|
width: {
|
||||||
|
if (UM.SimulationView.compatibilityMode) {
|
||||||
|
return UM.Theme.getSize("layerview_menu_size_compatibility").width;
|
||||||
|
} else {
|
||||||
|
return UM.Theme.getSize("layerview_menu_size").width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
height: {
|
||||||
|
if (UM.SimulationView.compatibilityMode) {
|
||||||
|
return UM.Theme.getSize("layerview_menu_size_compatibility").height;
|
||||||
|
} else if (UM.Preferences.getValue("layerview/layer_view_type") == 0) {
|
||||||
|
return UM.Theme.getSize("layerview_menu_size_material_color_mode").height + UM.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
|
||||||
|
} else {
|
||||||
|
return UM.Theme.getSize("layerview_menu_size").height + UM.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property var buttonTarget: {
|
||||||
|
if(parent != null)
|
||||||
|
{
|
||||||
|
var force_binding = parent.y; // ensure this gets reevaluated when the panel moves
|
||||||
|
return base.mapFromItem(parent.parent, parent.buttonTarget.x, parent.buttonTarget.y)
|
||||||
|
}
|
||||||
|
return Qt.point(0,0)
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: parent != null ? !parent.parent.monitoringPrint: true
|
||||||
|
|
||||||
|
UM.PointingRectangle {
|
||||||
|
id: layerViewMenu
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
z: layerSlider.z - 1
|
||||||
|
color: UM.Theme.getColor("tool_panel_background")
|
||||||
|
borderWidth: UM.Theme.getSize("default_lining").width
|
||||||
|
borderColor: UM.Theme.getColor("lining")
|
||||||
|
arrowSize: 0 // hide arrow until weird issue with first time rendering is fixed
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: view_settings
|
||||||
|
|
||||||
|
property var extruder_opacities: UM.Preferences.getValue("layerview/extruder_opacities").split("|")
|
||||||
|
property bool show_travel_moves: UM.Preferences.getValue("layerview/show_travel_moves")
|
||||||
|
property bool show_helpers: UM.Preferences.getValue("layerview/show_helpers")
|
||||||
|
property bool show_skin: UM.Preferences.getValue("layerview/show_skin")
|
||||||
|
property bool show_infill: UM.Preferences.getValue("layerview/show_infill")
|
||||||
|
// if we are in compatibility mode, we only show the "line type"
|
||||||
|
property bool show_legend: UM.SimulationView.compatibilityMode ? true : UM.Preferences.getValue("layerview/layer_view_type") == 1
|
||||||
|
property bool show_gradient: UM.SimulationView.compatibilityMode ? false : UM.Preferences.getValue("layerview/layer_view_type") == 2 || UM.Preferences.getValue("layerview/layer_view_type") == 3
|
||||||
|
property bool only_show_top_layers: UM.Preferences.getValue("view/only_show_top_layers")
|
||||||
|
property int top_layer_count: UM.Preferences.getValue("view/top_layer_count")
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
spacing: UM.Theme.getSize("layerview_row_spacing").height
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: layerViewTypesLabel
|
||||||
|
anchors.left: parent.left
|
||||||
|
text: catalog.i18nc("@label","Color scheme")
|
||||||
|
font: UM.Theme.getFont("default");
|
||||||
|
visible: !UM.SimulationView.compatibilityMode
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: UM.Theme.getColor("setting_control_text")
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel // matches SimulationView.py
|
||||||
|
{
|
||||||
|
id: layerViewTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted:
|
||||||
|
{
|
||||||
|
layerViewTypes.append({
|
||||||
|
text: catalog.i18nc("@label:listbox", "Material Color"),
|
||||||
|
type_id: 0
|
||||||
|
})
|
||||||
|
layerViewTypes.append({
|
||||||
|
text: catalog.i18nc("@label:listbox", "Line Type"),
|
||||||
|
type_id: 1
|
||||||
|
})
|
||||||
|
layerViewTypes.append({
|
||||||
|
text: catalog.i18nc("@label:listbox", "Feedrate"),
|
||||||
|
type_id: 2
|
||||||
|
})
|
||||||
|
layerViewTypes.append({
|
||||||
|
text: catalog.i18nc("@label:listbox", "Layer thickness"),
|
||||||
|
type_id: 3 // these ids match the switching in the shader
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ComboBox
|
||||||
|
{
|
||||||
|
id: layerTypeCombobox
|
||||||
|
anchors.left: parent.left
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
||||||
|
model: layerViewTypes
|
||||||
|
visible: !UM.SimulationView.compatibilityMode
|
||||||
|
style: UM.Theme.styles.combobox
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 10 * screenScaleFactor
|
||||||
|
|
||||||
|
onActivated:
|
||||||
|
{
|
||||||
|
UM.Preferences.setValue("layerview/layer_view_type", index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted:
|
||||||
|
{
|
||||||
|
currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type");
|
||||||
|
updateLegends(currentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLegends(type_id)
|
||||||
|
{
|
||||||
|
// update visibility of legends
|
||||||
|
view_settings.show_legend = UM.SimulationView.compatibilityMode || (type_id == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: compatibilityModeLabel
|
||||||
|
anchors.left: parent.left
|
||||||
|
text: catalog.i18nc("@label","Compatibility Mode")
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
|
visible: UM.SimulationView.compatibilityMode
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height
|
||||||
|
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
||||||
|
}
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: space2Label
|
||||||
|
anchors.left: parent.left
|
||||||
|
text: " "
|
||||||
|
font.pointSize: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: UM.Preferences
|
||||||
|
onPreferenceChanged:
|
||||||
|
{
|
||||||
|
layerTypeCombobox.currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type");
|
||||||
|
layerTypeCombobox.updateLegends(layerTypeCombobox.currentIndex);
|
||||||
|
view_settings.extruder_opacities = UM.Preferences.getValue("layerview/extruder_opacities").split("|");
|
||||||
|
view_settings.show_travel_moves = UM.Preferences.getValue("layerview/show_travel_moves");
|
||||||
|
view_settings.show_helpers = UM.Preferences.getValue("layerview/show_helpers");
|
||||||
|
view_settings.show_skin = UM.Preferences.getValue("layerview/show_skin");
|
||||||
|
view_settings.show_infill = UM.Preferences.getValue("layerview/show_infill");
|
||||||
|
view_settings.only_show_top_layers = UM.Preferences.getValue("view/only_show_top_layers");
|
||||||
|
view_settings.top_layer_count = UM.Preferences.getValue("view/top_layer_count");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Cura.ExtrudersModel{}
|
||||||
|
CheckBox {
|
||||||
|
id: extrudersModelCheckBox
|
||||||
|
checked: view_settings.extruder_opacities[index] > 0.5 || view_settings.extruder_opacities[index] == undefined || view_settings.extruder_opacities[index] == ""
|
||||||
|
onClicked: {
|
||||||
|
view_settings.extruder_opacities[index] = checked ? 1.0 : 0.0
|
||||||
|
UM.Preferences.setValue("layerview/extruder_opacities", view_settings.extruder_opacities.join("|"));
|
||||||
|
}
|
||||||
|
visible: !UM.SimulationView.compatibilityMode
|
||||||
|
enabled: index + 1 <= 4
|
||||||
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: extrudersModelCheckBox.right
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
width: UM.Theme.getSize("layerview_legend_size").width
|
||||||
|
height: UM.Theme.getSize("layerview_legend_size").height
|
||||||
|
color: model.color
|
||||||
|
radius: width / 2
|
||||||
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
|
border.color: UM.Theme.getColor("lining")
|
||||||
|
visible: !view_settings.show_legend & !view_settings.show_gradient
|
||||||
|
}
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
|
||||||
|
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
||||||
|
style: UM.Theme.styles.checkbox
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: model.name
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: UM.Theme.getColor("setting_control_text")
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: extrudersModelCheckBox.left;
|
||||||
|
anchors.right: extrudersModelCheckBox.right;
|
||||||
|
anchors.leftMargin: UM.Theme.getSize("checkbox").width + UM.Theme.getSize("default_margin").width /2
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ListModel {
|
||||||
|
id: typesLegendModel
|
||||||
|
Component.onCompleted:
|
||||||
|
{
|
||||||
|
typesLegendModel.append({
|
||||||
|
label: catalog.i18nc("@label", "Show Travels"),
|
||||||
|
initialValue: view_settings.show_travel_moves,
|
||||||
|
preference: "layerview/show_travel_moves",
|
||||||
|
colorId: "layerview_move_combing"
|
||||||
|
});
|
||||||
|
typesLegendModel.append({
|
||||||
|
label: catalog.i18nc("@label", "Show Helpers"),
|
||||||
|
initialValue: view_settings.show_helpers,
|
||||||
|
preference: "layerview/show_helpers",
|
||||||
|
colorId: "layerview_support"
|
||||||
|
});
|
||||||
|
typesLegendModel.append({
|
||||||
|
label: catalog.i18nc("@label", "Show Shell"),
|
||||||
|
initialValue: view_settings.show_skin,
|
||||||
|
preference: "layerview/show_skin",
|
||||||
|
colorId: "layerview_inset_0"
|
||||||
|
});
|
||||||
|
typesLegendModel.append({
|
||||||
|
label: catalog.i18nc("@label", "Show Infill"),
|
||||||
|
initialValue: view_settings.show_infill,
|
||||||
|
preference: "layerview/show_infill",
|
||||||
|
colorId: "layerview_infill"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: legendModelCheckBox
|
||||||
|
checked: model.initialValue
|
||||||
|
onClicked: {
|
||||||
|
UM.Preferences.setValue(model.preference, checked);
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: legendModelCheckBox.right
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
width: UM.Theme.getSize("layerview_legend_size").width
|
||||||
|
height: UM.Theme.getSize("layerview_legend_size").height
|
||||||
|
color: UM.Theme.getColor(model.colorId)
|
||||||
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
|
border.color: UM.Theme.getColor("lining")
|
||||||
|
visible: view_settings.show_legend
|
||||||
|
}
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
|
||||||
|
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
||||||
|
style: UM.Theme.styles.checkbox
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: label
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: UM.Theme.getColor("setting_control_text")
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: legendModelCheckBox.left;
|
||||||
|
anchors.right: legendModelCheckBox.right;
|
||||||
|
anchors.leftMargin: UM.Theme.getSize("checkbox").width + UM.Theme.getSize("default_margin").width /2
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
checked: view_settings.only_show_top_layers
|
||||||
|
onClicked: {
|
||||||
|
UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0);
|
||||||
|
}
|
||||||
|
text: catalog.i18nc("@label", "Only Show Top Layers")
|
||||||
|
visible: UM.SimulationView.compatibilityMode
|
||||||
|
style: UM.Theme.styles.checkbox
|
||||||
|
}
|
||||||
|
CheckBox {
|
||||||
|
checked: view_settings.top_layer_count == 5
|
||||||
|
onClicked: {
|
||||||
|
UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1);
|
||||||
|
}
|
||||||
|
text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top")
|
||||||
|
visible: UM.SimulationView.compatibilityMode
|
||||||
|
style: UM.Theme.styles.checkbox
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ListModel {
|
||||||
|
id: typesLegendModelNoCheck
|
||||||
|
Component.onCompleted:
|
||||||
|
{
|
||||||
|
typesLegendModelNoCheck.append({
|
||||||
|
label: catalog.i18nc("@label", "Top / Bottom"),
|
||||||
|
colorId: "layerview_skin",
|
||||||
|
});
|
||||||
|
typesLegendModelNoCheck.append({
|
||||||
|
label: catalog.i18nc("@label", "Inner Wall"),
|
||||||
|
colorId: "layerview_inset_x",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: label
|
||||||
|
visible: view_settings.show_legend
|
||||||
|
id: typesLegendModelLabel
|
||||||
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: typesLegendModelLabel.right
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
width: UM.Theme.getSize("layerview_legend_size").width
|
||||||
|
height: UM.Theme.getSize("layerview_legend_size").height
|
||||||
|
color: UM.Theme.getColor(model.colorId)
|
||||||
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
|
border.color: UM.Theme.getColor("lining")
|
||||||
|
visible: view_settings.show_legend
|
||||||
|
}
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
|
||||||
|
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
||||||
|
color: UM.Theme.getColor("setting_control_text")
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text for the minimum, maximum and units for the feedrates and layer thickness
|
||||||
|
Rectangle {
|
||||||
|
id: gradientLegend
|
||||||
|
visible: view_settings.show_gradient
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("layerview_row").height
|
||||||
|
anchors {
|
||||||
|
topMargin: UM.Theme.getSize("slider_layerview_margin").height
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: minText()
|
||||||
|
anchors.left: parent.left
|
||||||
|
color: UM.Theme.getColor("setting_control_text")
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
|
||||||
|
function minText() {
|
||||||
|
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) {
|
||||||
|
// Feedrate selected
|
||||||
|
if (UM.Preferences.getValue("layerview/layer_view_type") == 2) {
|
||||||
|
return parseFloat(UM.SimulationView.getMinFeedrate()).toFixed(2)
|
||||||
|
}
|
||||||
|
// Layer thickness selected
|
||||||
|
if (UM.Preferences.getValue("layerview/layer_view_type") == 3) {
|
||||||
|
return parseFloat(UM.SimulationView.getMinThickness()).toFixed(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return catalog.i18nc("@label","min")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: unitsText()
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: UM.Theme.getColor("setting_control_text")
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
|
||||||
|
function unitsText() {
|
||||||
|
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) {
|
||||||
|
// Feedrate selected
|
||||||
|
if (UM.Preferences.getValue("layerview/layer_view_type") == 2) {
|
||||||
|
return "mm/s"
|
||||||
|
}
|
||||||
|
// Layer thickness selected
|
||||||
|
if (UM.Preferences.getValue("layerview/layer_view_type") == 3) {
|
||||||
|
return "mm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: maxText()
|
||||||
|
anchors.right: parent.right
|
||||||
|
color: UM.Theme.getColor("setting_control_text")
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
|
||||||
|
function maxText() {
|
||||||
|
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) {
|
||||||
|
// Feedrate selected
|
||||||
|
if (UM.Preferences.getValue("layerview/layer_view_type") == 2) {
|
||||||
|
return parseFloat(UM.SimulationView.getMaxFeedrate()).toFixed(2)
|
||||||
|
}
|
||||||
|
// Layer thickness selected
|
||||||
|
if (UM.Preferences.getValue("layerview/layer_view_type") == 3) {
|
||||||
|
return parseFloat(UM.SimulationView.getMaxThickness()).toFixed(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return catalog.i18nc("@label","max")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gradient colors for feedrate and thickness
|
||||||
|
Rectangle { // In QML 5.9 can be changed by LinearGradient
|
||||||
|
// Invert values because then the bar is rotated 90 degrees
|
||||||
|
id: gradient
|
||||||
|
visible: view_settings.show_gradient
|
||||||
|
anchors.left: parent.right
|
||||||
|
height: parent.width
|
||||||
|
width: UM.Theme.getSize("layerview_row").height * 1.5
|
||||||
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
|
border.color: UM.Theme.getColor("lining")
|
||||||
|
transform: Rotation {origin.x: 0; origin.y: 0; angle: 90}
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop {
|
||||||
|
position: 0.000
|
||||||
|
color: Qt.rgba(1, 0, 0, 1)
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.25
|
||||||
|
color: Qt.rgba(0.75, 0.5, 0.25, 1)
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.5
|
||||||
|
color: Qt.rgba(0.5, 1, 0.5, 1)
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.75
|
||||||
|
color: Qt.rgba(0.25, 0.5, 0.75, 1)
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 1.0
|
||||||
|
color: Qt.rgba(0, 0, 1, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: slidersBox
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.bottom
|
||||||
|
topMargin: UM.Theme.getSize("slider_layerview_margin").height
|
||||||
|
left: parent.left
|
||||||
|
}
|
||||||
|
|
||||||
|
PathSlider {
|
||||||
|
id: pathSlider
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("slider_handle").width
|
||||||
|
anchors.left: parent.left
|
||||||
|
visible: !UM.SimulationView.compatibilityMode
|
||||||
|
|
||||||
|
// custom properties
|
||||||
|
handleValue: UM.SimulationView.currentPath
|
||||||
|
maximumValue: UM.SimulationView.numPaths
|
||||||
|
handleSize: UM.Theme.getSize("slider_handle").width
|
||||||
|
trackThickness: UM.Theme.getSize("slider_groove").width
|
||||||
|
trackColor: UM.Theme.getColor("slider_groove")
|
||||||
|
trackBorderColor: UM.Theme.getColor("slider_groove_border")
|
||||||
|
handleColor: UM.Theme.getColor("slider_handle")
|
||||||
|
handleActiveColor: UM.Theme.getColor("slider_handle_active")
|
||||||
|
rangeColor: UM.Theme.getColor("slider_groove_fill")
|
||||||
|
|
||||||
|
// update values when layer data changes
|
||||||
|
Connections {
|
||||||
|
target: UM.SimulationView
|
||||||
|
onMaxPathsChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath)
|
||||||
|
onCurrentPathChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the slider handlers show the correct value after switching views
|
||||||
|
Component.onCompleted: {
|
||||||
|
pathSlider.setHandleValue(UM.SimulationView.currentPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LayerSlider {
|
||||||
|
id: layerSlider
|
||||||
|
|
||||||
|
width: UM.Theme.getSize("slider_handle").width
|
||||||
|
height: UM.Theme.getSize("layerview_menu_size").height
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: pathSlider.bottom
|
||||||
|
topMargin: UM.Theme.getSize("slider_layerview_margin").height
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: UM.Theme.getSize("slider_layerview_margin").width
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom properties
|
||||||
|
upperValue: UM.SimulationView.currentLayer
|
||||||
|
lowerValue: UM.SimulationView.minimumLayer
|
||||||
|
maximumValue: UM.SimulationView.numLayers
|
||||||
|
handleSize: UM.Theme.getSize("slider_handle").width
|
||||||
|
trackThickness: UM.Theme.getSize("slider_groove").width
|
||||||
|
trackColor: UM.Theme.getColor("slider_groove")
|
||||||
|
trackBorderColor: UM.Theme.getColor("slider_groove_border")
|
||||||
|
upperHandleColor: UM.Theme.getColor("slider_handle")
|
||||||
|
lowerHandleColor: UM.Theme.getColor("slider_handle")
|
||||||
|
rangeHandleColor: UM.Theme.getColor("slider_groove_fill")
|
||||||
|
handleActiveColor: UM.Theme.getColor("slider_handle_active")
|
||||||
|
handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width
|
||||||
|
|
||||||
|
// update values when layer data changes
|
||||||
|
Connections {
|
||||||
|
target: UM.SimulationView
|
||||||
|
onMaxLayersChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer)
|
||||||
|
onMinimumLayerChanged: layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
|
||||||
|
onCurrentLayerChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the slider handlers show the correct value after switching views
|
||||||
|
Component.onCompleted: {
|
||||||
|
layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
|
||||||
|
layerSlider.setUpperValue(UM.SimulationView.currentLayer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play simulation button
|
||||||
|
Button {
|
||||||
|
id: playButton
|
||||||
|
implicitWidth: UM.Theme.getSize("button").width * 0.75;
|
||||||
|
implicitHeight: UM.Theme.getSize("button").height * 0.75;
|
||||||
|
iconSource: "./resources/simulation_resume.svg"
|
||||||
|
style: UM.Theme.styles.tool_button
|
||||||
|
visible: !UM.SimulationView.compatibilityMode
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: layerSlider.horizontalCenter
|
||||||
|
top: layerSlider.bottom
|
||||||
|
topMargin: UM.Theme.getSize("slider_layerview_margin").width
|
||||||
|
}
|
||||||
|
|
||||||
|
property var status: 0 // indicates if it's stopped (0) or playing (1)
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
switch(status) {
|
||||||
|
case 0: {
|
||||||
|
resumeSimulation()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
pauseSimulation()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pauseSimulation() {
|
||||||
|
UM.SimulationView.setSimulationRunning(false)
|
||||||
|
iconSource = "./resources/simulation_resume.svg"
|
||||||
|
simulationTimer.stop()
|
||||||
|
status = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function resumeSimulation() {
|
||||||
|
UM.SimulationView.setSimulationRunning(true)
|
||||||
|
iconSource = "./resources/simulation_pause.svg"
|
||||||
|
simulationTimer.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer
|
||||||
|
{
|
||||||
|
id: simulationTimer
|
||||||
|
interval: 250
|
||||||
|
running: false
|
||||||
|
repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
var currentPath = UM.SimulationView.currentPath
|
||||||
|
var numPaths = UM.SimulationView.numPaths
|
||||||
|
var currentLayer = UM.SimulationView.currentLayer
|
||||||
|
var numLayers = UM.SimulationView.numLayers
|
||||||
|
// When the user plays the simulation, if the path slider is at the end of this layer, we start
|
||||||
|
// the simulation at the beginning of the current layer.
|
||||||
|
if (playButton.status == 0)
|
||||||
|
{
|
||||||
|
if (currentPath >= numPaths)
|
||||||
|
{
|
||||||
|
UM.SimulationView.setCurrentPath(0)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UM.SimulationView.setCurrentPath(currentPath+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the simulation is already playing and we reach the end of a layer, then it automatically
|
||||||
|
// starts at the beginning of the next layer.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (currentPath >= numPaths)
|
||||||
|
{
|
||||||
|
// At the end of the model, the simulation stops
|
||||||
|
if (currentLayer >= numLayers)
|
||||||
|
{
|
||||||
|
playButton.pauseSimulation()
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UM.SimulationView.setCurrentLayer(currentLayer+1)
|
||||||
|
UM.SimulationView.setCurrentPath(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UM.SimulationView.setCurrentPath(currentPath+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
playButton.status = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FontMetrics {
|
||||||
|
id: fontMetrics
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
}
|
||||||
|
}
|
261
plugins/SimulationView/SimulationViewProxy.py
Normal file
261
plugins/SimulationView/SimulationViewProxy.py
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
# Copyright (c) 2017 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty
|
||||||
|
from UM.FlameProfiler import pyqtSlot
|
||||||
|
from UM.Application import Application
|
||||||
|
|
||||||
|
import SimulationView
|
||||||
|
|
||||||
|
|
||||||
|
class SimulationViewProxy(QObject):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self._current_layer = 0
|
||||||
|
self._controller = Application.getInstance().getController()
|
||||||
|
self._controller.activeViewChanged.connect(self._onActiveViewChanged)
|
||||||
|
self._onActiveViewChanged()
|
||||||
|
|
||||||
|
self.is_simulationView_selected = False
|
||||||
|
|
||||||
|
currentLayerChanged = pyqtSignal()
|
||||||
|
currentPathChanged = pyqtSignal()
|
||||||
|
maxLayersChanged = pyqtSignal()
|
||||||
|
maxPathsChanged = pyqtSignal()
|
||||||
|
activityChanged = pyqtSignal()
|
||||||
|
globalStackChanged = pyqtSignal()
|
||||||
|
preferencesChanged = pyqtSignal()
|
||||||
|
busyChanged = pyqtSignal()
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify=activityChanged)
|
||||||
|
def layerActivity(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getActivity()
|
||||||
|
return False
|
||||||
|
|
||||||
|
@pyqtProperty(int, notify=maxLayersChanged)
|
||||||
|
def numLayers(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getMaxLayers()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@pyqtProperty(int, notify=currentLayerChanged)
|
||||||
|
def currentLayer(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getCurrentLayer()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@pyqtProperty(int, notify=currentLayerChanged)
|
||||||
|
def minimumLayer(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getMinimumLayer()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@pyqtProperty(int, notify=maxPathsChanged)
|
||||||
|
def numPaths(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getMaxPaths()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@pyqtProperty(int, notify=currentPathChanged)
|
||||||
|
def currentPath(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getCurrentPath()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@pyqtProperty(int, notify=currentPathChanged)
|
||||||
|
def minimumPath(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getMinimumPath()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify=busyChanged)
|
||||||
|
def busy(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.isBusy()
|
||||||
|
return False
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify=preferencesChanged)
|
||||||
|
def compatibilityMode(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getCompatibilityMode()
|
||||||
|
return False
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def setCurrentLayer(self, layer_num):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
active_view.setLayer(layer_num)
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def setMinimumLayer(self, layer_num):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
active_view.setMinimumLayer(layer_num)
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def setCurrentPath(self, path_num):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
active_view.setPath(path_num)
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def setMinimumPath(self, path_num):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
active_view.setMinimumPath(path_num)
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def setSimulationViewType(self, layer_view_type):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
active_view.setSimulationViewisinstance(layer_view_type)
|
||||||
|
|
||||||
|
@pyqtSlot(result=int)
|
||||||
|
def getSimulationViewType(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getSimulationViewType()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
|
def setSimulationRunning(self, running):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
active_view.setSimulationRunning(running)
|
||||||
|
|
||||||
|
@pyqtSlot(result=bool)
|
||||||
|
def getSimulationRunning(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.isSimulationRunning()
|
||||||
|
return False
|
||||||
|
|
||||||
|
@pyqtSlot(result=float)
|
||||||
|
def getMinFeedrate(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getMinFeedrate()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@pyqtSlot(result=float)
|
||||||
|
def getMaxFeedrate(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getMaxFeedrate()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@pyqtSlot(result=float)
|
||||||
|
def getMinThickness(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getMinThickness()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@pyqtSlot(result=float)
|
||||||
|
def getMaxThickness(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getMaxThickness()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Opacity 0..1
|
||||||
|
@pyqtSlot(int, float)
|
||||||
|
def setExtruderOpacity(self, extruder_nr, opacity):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
active_view.setExtruderOpacity(extruder_nr, opacity)
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def setShowTravelMoves(self, show):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
active_view.setShowTravelMoves(show)
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def setShowHelpers(self, show):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
active_view.setShowHelpers(show)
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def setShowSkin(self, show):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
active_view.setShowSkin(show)
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def setShowInfill(self, show):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
active_view.setShowInfill(show)
|
||||||
|
|
||||||
|
@pyqtProperty(int, notify=globalStackChanged)
|
||||||
|
def extruderCount(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
return active_view.getExtruderCount()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _layerActivityChanged(self):
|
||||||
|
self.activityChanged.emit()
|
||||||
|
|
||||||
|
def _onLayerChanged(self):
|
||||||
|
self.currentLayerChanged.emit()
|
||||||
|
self._layerActivityChanged()
|
||||||
|
|
||||||
|
def _onPathChanged(self):
|
||||||
|
self.currentPathChanged.emit()
|
||||||
|
self._layerActivityChanged()
|
||||||
|
|
||||||
|
def _onMaxLayersChanged(self):
|
||||||
|
self.maxLayersChanged.emit()
|
||||||
|
|
||||||
|
def _onMaxPathsChanged(self):
|
||||||
|
self.maxPathsChanged.emit()
|
||||||
|
|
||||||
|
def _onBusyChanged(self):
|
||||||
|
self.busyChanged.emit()
|
||||||
|
|
||||||
|
def _onActivityChanged(self):
|
||||||
|
self.activityChanged.emit()
|
||||||
|
|
||||||
|
def _onGlobalStackChanged(self):
|
||||||
|
self.globalStackChanged.emit()
|
||||||
|
|
||||||
|
def _onPreferencesChanged(self):
|
||||||
|
self.preferencesChanged.emit()
|
||||||
|
|
||||||
|
def _onActiveViewChanged(self):
|
||||||
|
active_view = self._controller.getActiveView()
|
||||||
|
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
|
||||||
|
|
||||||
|
# remove other connection if once the SimulationView was created.
|
||||||
|
if self.is_simulationView_selected:
|
||||||
|
active_view.currentLayerNumChanged.disconnect(self._onLayerChanged)
|
||||||
|
active_view.currentPathNumChanged.disconnect(self._onPathChanged)
|
||||||
|
active_view.maxLayersChanged.disconnect(self._onMaxLayersChanged)
|
||||||
|
active_view.maxPathsChanged.disconnect(self._onMaxPathsChanged)
|
||||||
|
active_view.busyChanged.disconnect(self._onBusyChanged)
|
||||||
|
active_view.activityChanged.disconnect(self._onActivityChanged)
|
||||||
|
active_view.globalStackChanged.disconnect(self._onGlobalStackChanged)
|
||||||
|
active_view.preferencesChanged.disconnect(self._onPreferencesChanged)
|
||||||
|
|
||||||
|
self.is_simulationView_selected = True
|
||||||
|
active_view.currentLayerNumChanged.connect(self._onLayerChanged)
|
||||||
|
active_view.currentPathNumChanged.connect(self._onPathChanged)
|
||||||
|
active_view.maxLayersChanged.connect(self._onMaxLayersChanged)
|
||||||
|
active_view.maxPathsChanged.connect(self._onMaxPathsChanged)
|
||||||
|
active_view.busyChanged.connect(self._onBusyChanged)
|
||||||
|
active_view.activityChanged.connect(self._onActivityChanged)
|
||||||
|
active_view.globalStackChanged.connect(self._onGlobalStackChanged)
|
||||||
|
active_view.preferencesChanged.connect(self._onPreferencesChanged)
|
26
plugins/SimulationView/__init__.py
Normal file
26
plugins/SimulationView/__init__.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Copyright (c) 2017 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from PyQt5.QtQml import qmlRegisterSingletonType
|
||||||
|
|
||||||
|
from UM.i18n import i18nCatalog
|
||||||
|
from . import SimulationViewProxy, SimulationView
|
||||||
|
|
||||||
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
def getMetaData():
|
||||||
|
return {
|
||||||
|
"view": {
|
||||||
|
"name": catalog.i18nc("@item:inlistbox", "Simulation view"),
|
||||||
|
"view_panel": "SimulationView.qml",
|
||||||
|
"weight": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def createSimulationViewProxy(engine, script_engine):
|
||||||
|
return SimulationViewProxy.SimulatorViewProxy()
|
||||||
|
|
||||||
|
def register(app):
|
||||||
|
simulation_view = SimulationView.SimulationView()
|
||||||
|
qmlRegisterSingletonType(SimulationViewProxy.SimulationViewProxy, "UM", 1, 0, "SimulationView", simulation_view.getProxy)
|
||||||
|
return { "view": SimulationView.SimulationView()}
|
0
plugins/LayerView/layers.shader → plugins/SimulationView/layers.shader
Executable file → Normal file
0
plugins/LayerView/layers.shader → plugins/SimulationView/layers.shader
Executable file → Normal file
29
plugins/LayerView/layers3d.shader → plugins/SimulationView/layers3d.shader
Executable file → Normal file
29
plugins/LayerView/layers3d.shader → plugins/SimulationView/layers3d.shader
Executable file → Normal file
|
@ -6,6 +6,10 @@ vertex41core =
|
||||||
uniform highp mat4 u_modelMatrix;
|
uniform highp mat4 u_modelMatrix;
|
||||||
uniform highp mat4 u_viewProjectionMatrix;
|
uniform highp mat4 u_viewProjectionMatrix;
|
||||||
uniform lowp float u_active_extruder;
|
uniform lowp float u_active_extruder;
|
||||||
|
uniform lowp float u_max_feedrate;
|
||||||
|
uniform lowp float u_min_feedrate;
|
||||||
|
uniform lowp float u_max_thickness;
|
||||||
|
uniform lowp float u_min_thickness;
|
||||||
uniform lowp int u_layer_view_type;
|
uniform lowp int u_layer_view_type;
|
||||||
uniform lowp vec4 u_extruder_opacity; // currently only for max 4 extruders, others always visible
|
uniform lowp vec4 u_extruder_opacity; // currently only for max 4 extruders, others always visible
|
||||||
|
|
||||||
|
@ -18,6 +22,8 @@ vertex41core =
|
||||||
in highp vec2 a_line_dim; // line width and thickness
|
in highp vec2 a_line_dim; // line width and thickness
|
||||||
in highp float a_extruder;
|
in highp float a_extruder;
|
||||||
in highp float a_line_type;
|
in highp float a_line_type;
|
||||||
|
in highp float a_feedrate;
|
||||||
|
in highp float a_thickness;
|
||||||
|
|
||||||
out lowp vec4 v_color;
|
out lowp vec4 v_color;
|
||||||
|
|
||||||
|
@ -32,6 +38,15 @@ vertex41core =
|
||||||
out highp vec3 f_vertex;
|
out highp vec3 f_vertex;
|
||||||
out highp vec3 f_normal;
|
out highp vec3 f_normal;
|
||||||
|
|
||||||
|
vec4 gradientColor(float abs_value, float min_value, float max_value)
|
||||||
|
{
|
||||||
|
float value = (abs_value - min_value)/(max_value - min_value);
|
||||||
|
float red = value;
|
||||||
|
float green = 1-abs(1-2*value);
|
||||||
|
float blue = 1-value;
|
||||||
|
return vec4(red, green, blue, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec4 v1_vertex = a_vertex;
|
vec4 v1_vertex = a_vertex;
|
||||||
|
@ -48,6 +63,12 @@ vertex41core =
|
||||||
case 1: // "Line type"
|
case 1: // "Line type"
|
||||||
v_color = a_color;
|
v_color = a_color;
|
||||||
break;
|
break;
|
||||||
|
case 2: // "Feedrate"
|
||||||
|
v_color = gradientColor(a_feedrate, u_min_feedrate, u_max_feedrate);
|
||||||
|
break;
|
||||||
|
case 3: // "Layer thickness"
|
||||||
|
v_color = gradientColor(a_line_dim.y, u_min_thickness, u_max_thickness);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
v_vertex = world_space_vert.xyz;
|
v_vertex = world_space_vert.xyz;
|
||||||
|
@ -247,6 +268,12 @@ u_show_helpers = 1
|
||||||
u_show_skin = 1
|
u_show_skin = 1
|
||||||
u_show_infill = 1
|
u_show_infill = 1
|
||||||
|
|
||||||
|
u_min_feedrate = 0
|
||||||
|
u_max_feedrate = 1
|
||||||
|
|
||||||
|
u_min_thickness = 0
|
||||||
|
u_max_thickness = 1
|
||||||
|
|
||||||
[bindings]
|
[bindings]
|
||||||
u_modelViewProjectionMatrix = model_view_projection_matrix
|
u_modelViewProjectionMatrix = model_view_projection_matrix
|
||||||
u_modelMatrix = model_matrix
|
u_modelMatrix = model_matrix
|
||||||
|
@ -262,3 +289,5 @@ a_line_dim = line_dim
|
||||||
a_extruder = extruder
|
a_extruder = extruder
|
||||||
a_material_color = material_color
|
a_material_color = material_color
|
||||||
a_line_type = line_type
|
a_line_type = line_type
|
||||||
|
a_feedrate = feedrate
|
||||||
|
a_thickness = thickness
|
256
plugins/SimulationView/layers3d_shadow.shader
Normal file
256
plugins/SimulationView/layers3d_shadow.shader
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
[shaders]
|
||||||
|
vertex41core =
|
||||||
|
#version 410
|
||||||
|
uniform highp mat4 u_modelViewProjectionMatrix;
|
||||||
|
|
||||||
|
uniform highp mat4 u_modelMatrix;
|
||||||
|
uniform highp mat4 u_viewProjectionMatrix;
|
||||||
|
uniform lowp float u_active_extruder;
|
||||||
|
uniform lowp vec4 u_extruder_opacity; // currently only for max 4 extruders, others always visible
|
||||||
|
|
||||||
|
uniform highp mat4 u_normalMatrix;
|
||||||
|
|
||||||
|
in highp vec4 a_vertex;
|
||||||
|
in lowp vec4 a_color;
|
||||||
|
in lowp vec4 a_grayColor;
|
||||||
|
in lowp vec4 a_material_color;
|
||||||
|
in highp vec4 a_normal;
|
||||||
|
in highp vec2 a_line_dim; // line width and thickness
|
||||||
|
in highp float a_extruder;
|
||||||
|
in highp float a_line_type;
|
||||||
|
|
||||||
|
out lowp vec4 v_color;
|
||||||
|
|
||||||
|
out highp vec3 v_vertex;
|
||||||
|
out highp vec3 v_normal;
|
||||||
|
out lowp vec2 v_line_dim;
|
||||||
|
out highp int v_extruder;
|
||||||
|
out highp vec4 v_extruder_opacity;
|
||||||
|
out float v_line_type;
|
||||||
|
|
||||||
|
out lowp vec4 f_color;
|
||||||
|
out highp vec3 f_vertex;
|
||||||
|
out highp vec3 f_normal;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 v1_vertex = a_vertex;
|
||||||
|
v1_vertex.y -= a_line_dim.y / 2; // half layer down
|
||||||
|
|
||||||
|
vec4 world_space_vert = u_modelMatrix * v1_vertex;
|
||||||
|
gl_Position = world_space_vert;
|
||||||
|
// shade the color depending on the extruder index stored in the alpha component of the color
|
||||||
|
|
||||||
|
v_color = vec4(0.4, 0.4, 0.4, 0.9); // default color for not current layer
|
||||||
|
v_vertex = world_space_vert.xyz;
|
||||||
|
v_normal = (u_normalMatrix * normalize(a_normal)).xyz;
|
||||||
|
v_line_dim = a_line_dim;
|
||||||
|
v_extruder = int(a_extruder);
|
||||||
|
v_line_type = a_line_type;
|
||||||
|
v_extruder_opacity = u_extruder_opacity;
|
||||||
|
|
||||||
|
// for testing without geometry shader
|
||||||
|
f_color = v_color;
|
||||||
|
f_vertex = v_vertex;
|
||||||
|
f_normal = v_normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
geometry41core =
|
||||||
|
#version 410
|
||||||
|
|
||||||
|
uniform highp mat4 u_viewProjectionMatrix;
|
||||||
|
uniform int u_show_travel_moves;
|
||||||
|
uniform int u_show_helpers;
|
||||||
|
uniform int u_show_skin;
|
||||||
|
uniform int u_show_infill;
|
||||||
|
|
||||||
|
layout(lines) in;
|
||||||
|
layout(triangle_strip, max_vertices = 26) out;
|
||||||
|
|
||||||
|
in vec4 v_color[];
|
||||||
|
in vec3 v_vertex[];
|
||||||
|
in vec3 v_normal[];
|
||||||
|
in vec2 v_line_dim[];
|
||||||
|
in int v_extruder[];
|
||||||
|
in vec4 v_extruder_opacity[];
|
||||||
|
in float v_line_type[];
|
||||||
|
|
||||||
|
out vec4 f_color;
|
||||||
|
out vec3 f_normal;
|
||||||
|
out vec3 f_vertex;
|
||||||
|
|
||||||
|
// Set the set of variables and EmitVertex
|
||||||
|
void myEmitVertex(vec3 vertex, vec4 color, vec3 normal, vec4 pos) {
|
||||||
|
f_vertex = vertex;
|
||||||
|
f_color = color;
|
||||||
|
f_normal = normal;
|
||||||
|
gl_Position = pos;
|
||||||
|
EmitVertex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 g_vertex_delta;
|
||||||
|
vec3 g_vertex_normal_horz; // horizontal and vertical in respect to layers
|
||||||
|
vec4 g_vertex_offset_horz; // vec4 to match gl_in[x].gl_Position
|
||||||
|
vec3 g_vertex_normal_vert;
|
||||||
|
vec4 g_vertex_offset_vert;
|
||||||
|
vec3 g_vertex_normal_horz_head;
|
||||||
|
vec4 g_vertex_offset_horz_head;
|
||||||
|
|
||||||
|
float size_x;
|
||||||
|
float size_y;
|
||||||
|
|
||||||
|
if ((v_extruder_opacity[0][v_extruder[0]] == 0.0) && (v_line_type[0] != 8) && (v_line_type[0] != 9)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// See LayerPolygon; 8 is MoveCombingType, 9 is RetractionType
|
||||||
|
if ((u_show_travel_moves == 0) && ((v_line_type[0] == 8) || (v_line_type[0] == 9))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((u_show_helpers == 0) && ((v_line_type[0] == 4) || (v_line_type[0] == 5) || (v_line_type[0] == 7) || (v_line_type[0] == 10))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((u_show_skin == 0) && ((v_line_type[0] == 1) || (v_line_type[0] == 2) || (v_line_type[0] == 3))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((u_show_infill == 0) && (v_line_type[0] == 6)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((v_line_type[0] == 8) || (v_line_type[0] == 9)) {
|
||||||
|
// fixed size for movements
|
||||||
|
size_x = 0.05;
|
||||||
|
} else {
|
||||||
|
size_x = v_line_dim[1].x / 2 + 0.01; // radius, and make it nicely overlapping
|
||||||
|
}
|
||||||
|
size_y = v_line_dim[1].y / 2 + 0.01;
|
||||||
|
|
||||||
|
g_vertex_delta = gl_in[1].gl_Position - gl_in[0].gl_Position;
|
||||||
|
g_vertex_normal_horz_head = normalize(vec3(-g_vertex_delta.x, -g_vertex_delta.y, -g_vertex_delta.z));
|
||||||
|
g_vertex_offset_horz_head = vec4(g_vertex_normal_horz_head * size_x, 0.0);
|
||||||
|
|
||||||
|
g_vertex_normal_horz = normalize(vec3(g_vertex_delta.z, g_vertex_delta.y, -g_vertex_delta.x));
|
||||||
|
|
||||||
|
g_vertex_offset_horz = vec4(g_vertex_normal_horz * size_x, 0.0); //size * g_vertex_normal_horz;
|
||||||
|
g_vertex_normal_vert = vec3(0.0, 1.0, 0.0);
|
||||||
|
g_vertex_offset_vert = vec4(g_vertex_normal_vert * size_y, 0.0);
|
||||||
|
|
||||||
|
if ((v_line_type[0] == 8) || (v_line_type[0] == 9)) {
|
||||||
|
// Travels: flat plane with pointy ends
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head + g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head + g_vertex_offset_vert));
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
} else {
|
||||||
|
// All normal lines are rendered as 3d tubes.
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
// left side
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head));
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head));
|
||||||
|
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
// right side
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head));
|
||||||
|
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment41core =
|
||||||
|
#version 410
|
||||||
|
in lowp vec4 f_color;
|
||||||
|
in lowp vec3 f_normal;
|
||||||
|
in lowp vec3 f_vertex;
|
||||||
|
|
||||||
|
out vec4 frag_color;
|
||||||
|
|
||||||
|
uniform mediump vec4 u_ambientColor;
|
||||||
|
uniform highp vec3 u_lightPosition;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
mediump vec4 finalColor = vec4(0.0);
|
||||||
|
float alpha = f_color.a;
|
||||||
|
|
||||||
|
finalColor.rgb += f_color.rgb * 0.3;
|
||||||
|
|
||||||
|
highp vec3 normal = normalize(f_normal);
|
||||||
|
highp vec3 light_dir = normalize(u_lightPosition - f_vertex);
|
||||||
|
|
||||||
|
// Diffuse Component
|
||||||
|
highp float NdotL = clamp(dot(normal, light_dir), 0.0, 1.0);
|
||||||
|
finalColor += (NdotL * f_color);
|
||||||
|
finalColor.a = alpha; // Do not change alpha in any way
|
||||||
|
|
||||||
|
frag_color = finalColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[defaults]
|
||||||
|
u_active_extruder = 0.0
|
||||||
|
u_extruder_opacity = [1.0, 1.0, 1.0, 1.0]
|
||||||
|
|
||||||
|
u_specularColor = [0.4, 0.4, 0.4, 1.0]
|
||||||
|
u_ambientColor = [0.3, 0.3, 0.3, 0.0]
|
||||||
|
u_diffuseColor = [1.0, 0.79, 0.14, 1.0]
|
||||||
|
u_shininess = 20.0
|
||||||
|
|
||||||
|
u_show_travel_moves = 0
|
||||||
|
u_show_helpers = 1
|
||||||
|
u_show_skin = 1
|
||||||
|
u_show_infill = 1
|
||||||
|
|
||||||
|
[bindings]
|
||||||
|
u_modelViewProjectionMatrix = model_view_projection_matrix
|
||||||
|
u_modelMatrix = model_matrix
|
||||||
|
u_viewProjectionMatrix = view_projection_matrix
|
||||||
|
u_normalMatrix = normal_matrix
|
||||||
|
u_lightPosition = light_0_position
|
||||||
|
|
||||||
|
[attributes]
|
||||||
|
a_vertex = vertex
|
||||||
|
a_color = color
|
||||||
|
a_grayColor = vec4(0.87, 0.12, 0.45, 1.0)
|
||||||
|
a_normal = normal
|
||||||
|
a_line_dim = line_dim
|
||||||
|
a_extruder = extruder
|
||||||
|
a_material_color = material_color
|
||||||
|
a_line_type = line_type
|
156
plugins/SimulationView/layers_shadow.shader
Normal file
156
plugins/SimulationView/layers_shadow.shader
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
[shaders]
|
||||||
|
vertex =
|
||||||
|
uniform highp mat4 u_modelViewProjectionMatrix;
|
||||||
|
uniform lowp float u_active_extruder;
|
||||||
|
uniform lowp float u_shade_factor;
|
||||||
|
uniform highp int u_layer_view_type;
|
||||||
|
|
||||||
|
attribute highp float a_extruder;
|
||||||
|
attribute highp float a_line_type;
|
||||||
|
attribute highp vec4 a_vertex;
|
||||||
|
attribute lowp vec4 a_color;
|
||||||
|
attribute lowp vec4 a_material_color;
|
||||||
|
|
||||||
|
varying lowp vec4 v_color;
|
||||||
|
varying float v_line_type;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = u_modelViewProjectionMatrix * a_vertex;
|
||||||
|
// shade the color depending on the extruder index
|
||||||
|
v_color = vec4(0.4, 0.4, 0.4, 0.9); // default color for not current layer;
|
||||||
|
// 8 and 9 are travel moves
|
||||||
|
// if ((a_line_type != 8.0) && (a_line_type != 9.0)) {
|
||||||
|
// v_color = (a_extruder == u_active_extruder) ? v_color : vec4(u_shade_factor * v_color.rgb, v_color.a);
|
||||||
|
// }
|
||||||
|
|
||||||
|
v_line_type = a_line_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment =
|
||||||
|
varying lowp vec4 v_color;
|
||||||
|
varying float v_line_type;
|
||||||
|
|
||||||
|
uniform int u_show_travel_moves;
|
||||||
|
uniform int u_show_helpers;
|
||||||
|
uniform int u_show_skin;
|
||||||
|
uniform int u_show_infill;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
if ((u_show_travel_moves == 0) && (v_line_type >= 7.5) && (v_line_type <= 9.5)) { // actually, 8 and 9
|
||||||
|
// discard movements
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
// support: 4, 5, 7, 10
|
||||||
|
if ((u_show_helpers == 0) && (
|
||||||
|
((v_line_type >= 3.5) && (v_line_type <= 4.5)) ||
|
||||||
|
((v_line_type >= 6.5) && (v_line_type <= 7.5)) ||
|
||||||
|
((v_line_type >= 9.5) && (v_line_type <= 10.5)) ||
|
||||||
|
((v_line_type >= 4.5) && (v_line_type <= 5.5))
|
||||||
|
)) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
// skin: 1, 2, 3
|
||||||
|
if ((u_show_skin == 0) && (
|
||||||
|
(v_line_type >= 0.5) && (v_line_type <= 3.5)
|
||||||
|
)) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
// infill:
|
||||||
|
if ((u_show_infill == 0) && (v_line_type >= 5.5) && (v_line_type <= 6.5)) {
|
||||||
|
// discard movements
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = v_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex41core =
|
||||||
|
#version 410
|
||||||
|
uniform highp mat4 u_modelViewProjectionMatrix;
|
||||||
|
uniform lowp float u_active_extruder;
|
||||||
|
uniform lowp float u_shade_factor;
|
||||||
|
uniform highp int u_layer_view_type;
|
||||||
|
|
||||||
|
in highp float a_extruder;
|
||||||
|
in highp float a_line_type;
|
||||||
|
in highp vec4 a_vertex;
|
||||||
|
in lowp vec4 a_color;
|
||||||
|
in lowp vec4 a_material_color;
|
||||||
|
|
||||||
|
out lowp vec4 v_color;
|
||||||
|
out float v_line_type;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = u_modelViewProjectionMatrix * a_vertex;
|
||||||
|
v_color = vec4(0.4, 0.4, 0.4, 0.9); // default color for not current layer
|
||||||
|
// if ((a_line_type != 8) && (a_line_type != 9)) {
|
||||||
|
// v_color = (a_extruder == u_active_extruder) ? v_color : vec4(u_shade_factor * v_color.rgb, v_color.a);
|
||||||
|
// }
|
||||||
|
|
||||||
|
v_line_type = a_line_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment41core =
|
||||||
|
#version 410
|
||||||
|
in lowp vec4 v_color;
|
||||||
|
in float v_line_type;
|
||||||
|
out vec4 frag_color;
|
||||||
|
|
||||||
|
uniform int u_show_travel_moves;
|
||||||
|
uniform int u_show_helpers;
|
||||||
|
uniform int u_show_skin;
|
||||||
|
uniform int u_show_infill;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
if ((u_show_travel_moves == 0) && (v_line_type >= 7.5) && (v_line_type <= 9.5)) { // actually, 8 and 9
|
||||||
|
// discard movements
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
// helpers: 4, 5, 7, 10
|
||||||
|
if ((u_show_helpers == 0) && (
|
||||||
|
((v_line_type >= 3.5) && (v_line_type <= 4.5)) ||
|
||||||
|
((v_line_type >= 6.5) && (v_line_type <= 7.5)) ||
|
||||||
|
((v_line_type >= 9.5) && (v_line_type <= 10.5)) ||
|
||||||
|
((v_line_type >= 4.5) && (v_line_type <= 5.5))
|
||||||
|
)) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
// skin: 1, 2, 3
|
||||||
|
if ((u_show_skin == 0) && (
|
||||||
|
(v_line_type >= 0.5) && (v_line_type <= 3.5)
|
||||||
|
)) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
// infill:
|
||||||
|
if ((u_show_infill == 0) && (v_line_type >= 5.5) && (v_line_type <= 6.5)) {
|
||||||
|
// discard movements
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
frag_color = v_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
[defaults]
|
||||||
|
u_active_extruder = 0.0
|
||||||
|
u_shade_factor = 0.60
|
||||||
|
u_layer_view_type = 0
|
||||||
|
u_extruder_opacity = [1.0, 1.0, 1.0, 1.0]
|
||||||
|
|
||||||
|
u_show_travel_moves = 0
|
||||||
|
u_show_helpers = 1
|
||||||
|
u_show_skin = 1
|
||||||
|
u_show_infill = 1
|
||||||
|
|
||||||
|
[bindings]
|
||||||
|
u_modelViewProjectionMatrix = model_view_projection_matrix
|
||||||
|
|
||||||
|
[attributes]
|
||||||
|
a_vertex = vertex
|
||||||
|
a_color = color
|
||||||
|
a_extruder = extruder
|
||||||
|
a_line_type = line_type
|
||||||
|
a_material_color = material_color
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "Layer View",
|
"name": "Simulation View",
|
||||||
"author": "Ultimaker B.V.",
|
"author": "Ultimaker B.V.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Provides the Layer view.",
|
"description": "Provides the Simulation view.",
|
||||||
"api": 4,
|
"api": 4,
|
||||||
"i18n-catalog": "cura"
|
"i18n-catalog": "cura"
|
||||||
}
|
}
|
BIN
plugins/SimulationView/resources/nozzle.stl
Normal file
BIN
plugins/SimulationView/resources/nozzle.stl
Normal file
Binary file not shown.
79
plugins/SimulationView/resources/simulation_pause.svg
Normal file
79
plugins/SimulationView/resources/simulation_pause.svg
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
viewBox="0 0 30 30"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4620"
|
||||||
|
sodipodi:docname="simulation_pause.svg"
|
||||||
|
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)">
|
||||||
|
<metadata
|
||||||
|
id="metadata4626">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs4624" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1137"
|
||||||
|
id="namedview4622"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:zoom="22.250293"
|
||||||
|
inkscape:cx="8.1879003"
|
||||||
|
inkscape:cy="12.643765"
|
||||||
|
inkscape:window-x="2872"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg4620">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15,15"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide4628"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,0,255)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15,15"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide4630"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,0,255)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect5192"
|
||||||
|
width="2"
|
||||||
|
height="20"
|
||||||
|
x="19"
|
||||||
|
y="5" />
|
||||||
|
<rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect5192-5"
|
||||||
|
width="2"
|
||||||
|
height="20"
|
||||||
|
x="9"
|
||||||
|
y="5" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
82
plugins/SimulationView/resources/simulation_resume.svg
Normal file
82
plugins/SimulationView/resources/simulation_resume.svg
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
viewBox="0 0 30 30"
|
||||||
|
version="1.1"
|
||||||
|
id="svg3765"
|
||||||
|
sodipodi:docname="simulation_resume.svg"
|
||||||
|
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)">
|
||||||
|
<metadata
|
||||||
|
id="metadata3771">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs3769" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1137"
|
||||||
|
id="namedview3767"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:zoom="23.327047"
|
||||||
|
inkscape:cx="10.788646"
|
||||||
|
inkscape:cy="14.67951"
|
||||||
|
inkscape:window-x="2872"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg3765">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15,15"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide4592"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,0,255)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15,15"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide4594"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,0,255)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
id="path3783"
|
||||||
|
sodipodi:sides="3"
|
||||||
|
sodipodi:cx="12.732001"
|
||||||
|
sodipodi:cy="14.695877"
|
||||||
|
sodipodi:r1="13.891838"
|
||||||
|
sodipodi:r2="6.945919"
|
||||||
|
sodipodi:arg1="0"
|
||||||
|
sodipodi:arg2="1.0471976"
|
||||||
|
inkscape:flatsided="true"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="m 26.623839,14.695877 -20.8377567,12.030685 0,-24.0613696 z"
|
||||||
|
inkscape:transform-center-x="-2.9211205"
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.32790732;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
transform="matrix(0.84110413,0,0,0.87756418,1.775541,2.1034247)" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -133,6 +133,7 @@
|
||||||
"slider_groove_fill": [245, 245, 245, 255],
|
"slider_groove_fill": [245, 245, 245, 255],
|
||||||
"slider_handle": [255, 255, 255, 255],
|
"slider_handle": [255, 255, 255, 255],
|
||||||
"slider_handle_hover": [77, 182, 226, 255],
|
"slider_handle_hover": [77, 182, 226, 255],
|
||||||
|
"slider_handle_active": [68, 192, 255, 255],
|
||||||
"slider_handle_border": [39, 44, 48, 255],
|
"slider_handle_border": [39, 44, 48, 255],
|
||||||
"slider_text_background": [255, 255, 255, 255],
|
"slider_text_background": [255, 255, 255, 255],
|
||||||
|
|
||||||
|
@ -194,6 +195,7 @@
|
||||||
"layerview_move_combing": [0, 0, 255, 255],
|
"layerview_move_combing": [0, 0, 255, 255],
|
||||||
"layerview_move_retraction": [128, 128, 255, 255],
|
"layerview_move_retraction": [128, 128, 255, 255],
|
||||||
"layerview_support_interface": [64, 192, 255, 255],
|
"layerview_support_interface": [64, 192, 255, 255],
|
||||||
|
"layerview_nozzle": [181, 166, 66, 120],
|
||||||
|
|
||||||
"material_compatibility_warning": [255, 255, 255, 255],
|
"material_compatibility_warning": [255, 255, 255, 255],
|
||||||
|
|
||||||
|
|
|
@ -269,6 +269,7 @@ QtObject {
|
||||||
arrowSize: Theme.getSize("button_tooltip_arrow").width
|
arrowSize: Theme.getSize("button_tooltip_arrow").width
|
||||||
color: Theme.getColor("button_tooltip")
|
color: Theme.getColor("button_tooltip")
|
||||||
opacity: control.hovered ? 1.0 : 0.0;
|
opacity: control.hovered ? 1.0 : 0.0;
|
||||||
|
visible: control.text != ""
|
||||||
|
|
||||||
width: control.hovered ? button_tip.width + Theme.getSize("button_tooltip").width : 0
|
width: control.hovered ? button_tip.width + Theme.getSize("button_tooltip").width : 0
|
||||||
height: Theme.getSize("button_tooltip").height
|
height: Theme.getSize("button_tooltip").height
|
||||||
|
|
|
@ -183,6 +183,7 @@
|
||||||
"slider_groove_fill": [127, 127, 127, 255],
|
"slider_groove_fill": [127, 127, 127, 255],
|
||||||
"slider_handle": [0, 0, 0, 255],
|
"slider_handle": [0, 0, 0, 255],
|
||||||
"slider_handle_hover": [77, 182, 226, 255],
|
"slider_handle_hover": [77, 182, 226, 255],
|
||||||
|
"slider_handle_active": [68, 192, 255, 255],
|
||||||
"slider_handle_border": [39, 44, 48, 255],
|
"slider_handle_border": [39, 44, 48, 255],
|
||||||
"slider_text_background": [255, 255, 255, 255],
|
"slider_text_background": [255, 255, 255, 255],
|
||||||
|
|
||||||
|
@ -271,7 +272,8 @@
|
||||||
"layerview_support_infill": [0, 255, 255, 255],
|
"layerview_support_infill": [0, 255, 255, 255],
|
||||||
"layerview_move_combing": [0, 0, 255, 255],
|
"layerview_move_combing": [0, 0, 255, 255],
|
||||||
"layerview_move_retraction": [128, 128, 255, 255],
|
"layerview_move_retraction": [128, 128, 255, 255],
|
||||||
"layerview_support_interface": [64, 192, 255, 255]
|
"layerview_support_interface": [64, 192, 255, 255],
|
||||||
|
"layerview_nozzle": [181, 166, 66, 50]
|
||||||
},
|
},
|
||||||
|
|
||||||
"sizes": {
|
"sizes": {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue