mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-05-03 01:04:35 +08:00
Merge pull request #7971 from Ultimaker/CURA-7106-Speedup-multiple-objects-on-build-plate
CURA-7106 Speedup multiple objects on build plate
This commit is contained in:
commit
c6fd25e7e9
@ -1,5 +1,6 @@
|
|||||||
# Copyright (c) 2019 Ultimaker B.V.
|
# Copyright (c) 2019 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.
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
from UM.Job import Job
|
from UM.Job import Job
|
||||||
@ -94,6 +95,7 @@ class ArrangeObjectsJob(Job):
|
|||||||
|
|
||||||
status_message.setProgress((idx + 1) / len(nodes_arr) * 100)
|
status_message.setProgress((idx + 1) / len(nodes_arr) * 100)
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
QCoreApplication.processEvents()
|
||||||
|
|
||||||
grouped_operation.push()
|
grouped_operation.push()
|
||||||
|
|
||||||
|
@ -92,6 +92,8 @@ class BuildVolume(SceneNode):
|
|||||||
self._adhesion_type = None # type: Any
|
self._adhesion_type = None # type: Any
|
||||||
self._platform = Platform(self)
|
self._platform = Platform(self)
|
||||||
|
|
||||||
|
self._edge_disallowed_size = None
|
||||||
|
|
||||||
self._build_volume_message = Message(catalog.i18nc("@info:status",
|
self._build_volume_message = Message(catalog.i18nc("@info:status",
|
||||||
"The build volume height has been reduced due to the value of the"
|
"The build volume height has been reduced due to the value of the"
|
||||||
" \"Print Sequence\" setting to prevent the gantry from colliding"
|
" \"Print Sequence\" setting to prevent the gantry from colliding"
|
||||||
@ -106,8 +108,6 @@ class BuildVolume(SceneNode):
|
|||||||
|
|
||||||
self._application.globalContainerStackChanged.connect(self._onStackChanged)
|
self._application.globalContainerStackChanged.connect(self._onStackChanged)
|
||||||
|
|
||||||
self._onStackChanged()
|
|
||||||
|
|
||||||
self._engine_ready = False
|
self._engine_ready = False
|
||||||
self._application.engineCreatedSignal.connect(self._onEngineCreated)
|
self._application.engineCreatedSignal.connect(self._onEngineCreated)
|
||||||
|
|
||||||
@ -118,7 +118,7 @@ class BuildVolume(SceneNode):
|
|||||||
self._scene_objects = set() # type: Set[SceneNode]
|
self._scene_objects = set() # type: Set[SceneNode]
|
||||||
|
|
||||||
self._scene_change_timer = QTimer()
|
self._scene_change_timer = QTimer()
|
||||||
self._scene_change_timer.setInterval(100)
|
self._scene_change_timer.setInterval(200)
|
||||||
self._scene_change_timer.setSingleShot(True)
|
self._scene_change_timer.setSingleShot(True)
|
||||||
self._scene_change_timer.timeout.connect(self._onSceneChangeTimerFinished)
|
self._scene_change_timer.timeout.connect(self._onSceneChangeTimerFinished)
|
||||||
|
|
||||||
@ -747,6 +747,7 @@ class BuildVolume(SceneNode):
|
|||||||
self._error_areas = []
|
self._error_areas = []
|
||||||
|
|
||||||
used_extruders = ExtruderManager.getInstance().getUsedExtruderStacks()
|
used_extruders = ExtruderManager.getInstance().getUsedExtruderStacks()
|
||||||
|
self._edge_disallowed_size = None # Force a recalculation
|
||||||
disallowed_border_size = self.getEdgeDisallowedSize()
|
disallowed_border_size = self.getEdgeDisallowedSize()
|
||||||
|
|
||||||
result_areas = self._computeDisallowedAreasStatic(disallowed_border_size, used_extruders) # Normal machine disallowed areas can always be added.
|
result_areas = self._computeDisallowedAreasStatic(disallowed_border_size, used_extruders) # Normal machine disallowed areas can always be added.
|
||||||
@ -1124,6 +1125,9 @@ class BuildVolume(SceneNode):
|
|||||||
if not self._global_container_stack or not self._global_container_stack.extruderList:
|
if not self._global_container_stack or not self._global_container_stack.extruderList:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
if self._edge_disallowed_size is not None:
|
||||||
|
return self._edge_disallowed_size
|
||||||
|
|
||||||
container_stack = self._global_container_stack
|
container_stack = self._global_container_stack
|
||||||
used_extruders = ExtruderManager.getInstance().getUsedExtruderStacks()
|
used_extruders = ExtruderManager.getInstance().getUsedExtruderStacks()
|
||||||
|
|
||||||
@ -1139,8 +1143,8 @@ class BuildVolume(SceneNode):
|
|||||||
# Now combine our different pieces of data to get the final border size.
|
# Now combine our different pieces of data to get the final border size.
|
||||||
# Support expansion is added to the bed adhesion, since the bed adhesion goes around support.
|
# Support expansion is added to the bed adhesion, since the bed adhesion goes around support.
|
||||||
# Support expansion is added to farthest shield distance, since the shields go around support.
|
# Support expansion is added to farthest shield distance, since the shields go around support.
|
||||||
border_size = max(move_from_wall_radius, support_expansion + farthest_shield_distance, support_expansion + bed_adhesion_size)
|
self._edge_disallowed_size = max(move_from_wall_radius, support_expansion + farthest_shield_distance, support_expansion + bed_adhesion_size)
|
||||||
return border_size
|
return self._edge_disallowed_size
|
||||||
|
|
||||||
def _clamp(self, value, min_value, max_value):
|
def _clamp(self, value, min_value, max_value):
|
||||||
return max(min(value, max_value), min_value)
|
return max(min(value, max_value), min_value)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2018 Ultimaker B.V.
|
# Copyright (c) 2020 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 time
|
import time
|
||||||
@ -14,6 +14,7 @@ from UM.Settings.Validator import ValidatorState
|
|||||||
|
|
||||||
import cura.CuraApplication
|
import cura.CuraApplication
|
||||||
|
|
||||||
|
|
||||||
class MachineErrorChecker(QObject):
|
class MachineErrorChecker(QObject):
|
||||||
"""This class performs setting error checks for the currently active machine.
|
"""This class performs setting error checks for the currently active machine.
|
||||||
|
|
||||||
@ -50,6 +51,8 @@ class MachineErrorChecker(QObject):
|
|||||||
self._error_check_timer.setInterval(100)
|
self._error_check_timer.setInterval(100)
|
||||||
self._error_check_timer.setSingleShot(True)
|
self._error_check_timer.setSingleShot(True)
|
||||||
|
|
||||||
|
self._keys_to_check = set() # type: Set[str]
|
||||||
|
|
||||||
def initialize(self) -> None:
|
def initialize(self) -> None:
|
||||||
self._error_check_timer.timeout.connect(self._rescheduleCheck)
|
self._error_check_timer.timeout.connect(self._rescheduleCheck)
|
||||||
|
|
||||||
@ -103,6 +106,7 @@ class MachineErrorChecker(QObject):
|
|||||||
|
|
||||||
if property_name != "value":
|
if property_name != "value":
|
||||||
return
|
return
|
||||||
|
self._keys_to_check.add(key)
|
||||||
self.startErrorCheck()
|
self.startErrorCheck()
|
||||||
|
|
||||||
def startErrorCheck(self, *args: Any) -> None:
|
def startErrorCheck(self, *args: Any) -> None:
|
||||||
@ -140,7 +144,10 @@ class MachineErrorChecker(QObject):
|
|||||||
# Populate the (stack, key) tuples to check
|
# Populate the (stack, key) tuples to check
|
||||||
self._stacks_and_keys_to_check = deque()
|
self._stacks_and_keys_to_check = deque()
|
||||||
for stack in global_stack.extruderList:
|
for stack in global_stack.extruderList:
|
||||||
for key in stack.getAllKeys():
|
if not self._keys_to_check:
|
||||||
|
self._keys_to_check = stack.getAllKeys()
|
||||||
|
|
||||||
|
for key in self._keys_to_check:
|
||||||
self._stacks_and_keys_to_check.append((stack, key))
|
self._stacks_and_keys_to_check.append((stack, key))
|
||||||
|
|
||||||
self._application.callLater(self._checkStack)
|
self._application.callLater(self._checkStack)
|
||||||
@ -181,18 +188,25 @@ class MachineErrorChecker(QObject):
|
|||||||
validator = validator_type(key)
|
validator = validator_type(key)
|
||||||
validation_state = validator(stack)
|
validation_state = validator(stack)
|
||||||
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid):
|
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid):
|
||||||
# Finish
|
# Since we don't know if any of the settings we didn't check is has an error value, store the list for the
|
||||||
self._setResult(True)
|
# next check.
|
||||||
|
keys_to_recheck = {setting_key for stack, setting_key in self._stacks_and_keys_to_check}
|
||||||
|
keys_to_recheck.add(key)
|
||||||
|
self._setResult(True, keys_to_recheck = keys_to_recheck)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Schedule the check for the next key
|
# Schedule the check for the next key
|
||||||
self._application.callLater(self._checkStack)
|
self._application.callLater(self._checkStack)
|
||||||
|
|
||||||
def _setResult(self, result: bool) -> None:
|
def _setResult(self, result: bool, keys_to_recheck = None) -> None:
|
||||||
if result != self._has_errors:
|
if result != self._has_errors:
|
||||||
self._has_errors = result
|
self._has_errors = result
|
||||||
self.hasErrorUpdated.emit()
|
self.hasErrorUpdated.emit()
|
||||||
self._machine_manager.stacksValidationChanged.emit()
|
self._machine_manager.stacksValidationChanged.emit()
|
||||||
|
if keys_to_recheck is None:
|
||||||
|
self._keys_to_check = set()
|
||||||
|
else:
|
||||||
|
self._keys_to_check = keys_to_recheck
|
||||||
self._need_to_check = False
|
self._need_to_check = False
|
||||||
self._check_in_progress = False
|
self._check_in_progress = False
|
||||||
self.needToWaitForResultChanged.emit()
|
self.needToWaitForResultChanged.emit()
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
import copy
|
import copy
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
from UM.Job import Job
|
from UM.Job import Job
|
||||||
from UM.Operations.GroupedOperation import GroupedOperation
|
from UM.Operations.GroupedOperation import GroupedOperation
|
||||||
from UM.Message import Message
|
from UM.Message import Message
|
||||||
@ -93,8 +95,9 @@ class MultiplyObjectsJob(Job):
|
|||||||
nodes.append(new_node)
|
nodes.append(new_node)
|
||||||
current_progress += 1
|
current_progress += 1
|
||||||
status_message.setProgress((current_progress / total_progress) * 100)
|
status_message.setProgress((current_progress / total_progress) * 100)
|
||||||
|
QCoreApplication.processEvents()
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
QCoreApplication.processEvents()
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
|
||||||
if nodes:
|
if nodes:
|
||||||
|
@ -54,7 +54,7 @@ class PickingPass(RenderPass):
|
|||||||
# Fill up the batch with objects that can be sliced. `
|
# Fill up the batch with objects that can be sliced. `
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
|
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
|
||||||
if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
|
if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
|
||||||
batch.addItem(node.getWorldTransformation(), node.getMeshData())
|
batch.addItem(node.getWorldTransformation(copy = False), node.getMeshData())
|
||||||
|
|
||||||
self.bind()
|
self.bind()
|
||||||
batch.render(self._scene.getActiveCamera())
|
batch.render(self._scene.getActiveCamera())
|
||||||
|
@ -114,12 +114,12 @@ class PreviewPass(RenderPass):
|
|||||||
1.0]
|
1.0]
|
||||||
uniforms["diffuse_color"] = prettier_color(diffuse_color)
|
uniforms["diffuse_color"] = prettier_color(diffuse_color)
|
||||||
uniforms["diffuse_color_2"] = diffuse_color2
|
uniforms["diffuse_color_2"] = diffuse_color2
|
||||||
batch_support_mesh.addItem(node.getWorldTransformation(), node.getMeshData(), uniforms = uniforms)
|
batch_support_mesh.addItem(node.getWorldTransformation(copy = False), node.getMeshData(), uniforms = uniforms)
|
||||||
else:
|
else:
|
||||||
# Normal scene node
|
# Normal scene node
|
||||||
uniforms = {}
|
uniforms = {}
|
||||||
uniforms["diffuse_color"] = prettier_color(cast(CuraSceneNode, node).getDiffuseColor())
|
uniforms["diffuse_color"] = prettier_color(cast(CuraSceneNode, node).getDiffuseColor())
|
||||||
batch.addItem(node.getWorldTransformation(), node.getMeshData(), uniforms = uniforms)
|
batch.addItem(node.getWorldTransformation(copy = False), node.getMeshData(), uniforms = uniforms)
|
||||||
|
|
||||||
self.bind()
|
self.bind()
|
||||||
|
|
||||||
|
@ -53,6 +53,8 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||||||
CuraApplication.getInstance().getController().toolOperationStarted.connect(self._onChanged)
|
CuraApplication.getInstance().getController().toolOperationStarted.connect(self._onChanged)
|
||||||
CuraApplication.getInstance().getController().toolOperationStopped.connect(self._onChanged)
|
CuraApplication.getInstance().getController().toolOperationStopped.connect(self._onChanged)
|
||||||
|
|
||||||
|
self._root = Application.getInstance().getController().getScene().getRoot()
|
||||||
|
|
||||||
self._onGlobalStackChanged()
|
self._onGlobalStackChanged()
|
||||||
|
|
||||||
def createRecomputeConvexHullTimer(self) -> None:
|
def createRecomputeConvexHullTimer(self) -> None:
|
||||||
@ -198,23 +200,16 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||||||
CuraApplication.getInstance().callLater(self.recomputeConvexHullDelayed)
|
CuraApplication.getInstance().callLater(self.recomputeConvexHullDelayed)
|
||||||
|
|
||||||
def recomputeConvexHull(self) -> None:
|
def recomputeConvexHull(self) -> None:
|
||||||
controller = Application.getInstance().getController()
|
if self._node is None or not self.__isDescendant(self._root, self._node):
|
||||||
root = controller.getScene().getRoot()
|
|
||||||
if self._node is None or controller.isToolOperationActive() or not self.__isDescendant(root, self._node):
|
|
||||||
# If the tool operation is still active, we need to compute the convex hull later after the controller is
|
|
||||||
# no longer active.
|
|
||||||
if controller.isToolOperationActive():
|
|
||||||
self.recomputeConvexHullDelayed()
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._convex_hull_node:
|
if self._convex_hull_node:
|
||||||
|
# Convex hull node still exists, but the node is removed or no longer in the scene.
|
||||||
self._convex_hull_node.setParent(None)
|
self._convex_hull_node.setParent(None)
|
||||||
self._convex_hull_node = None
|
self._convex_hull_node = None
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._convex_hull_node:
|
if self._convex_hull_node:
|
||||||
self._convex_hull_node.setParent(None)
|
self._convex_hull_node.setParent(None)
|
||||||
hull_node = ConvexHullNode.ConvexHullNode(self._node, self.getPrintingArea(), self._raft_thickness, root)
|
hull_node = ConvexHullNode.ConvexHullNode(self._node, self.getPrintingArea(), self._raft_thickness, self._root)
|
||||||
self._convex_hull_node = hull_node
|
self._convex_hull_node = hull_node
|
||||||
|
|
||||||
def _onSettingValueChanged(self, key: str, property_name: str) -> None:
|
def _onSettingValueChanged(self, key: str, property_name: str) -> None:
|
||||||
@ -273,7 +268,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||||||
if mesh is None:
|
if mesh is None:
|
||||||
return Polygon([]) # Node has no mesh data, so just return an empty Polygon.
|
return Polygon([]) # Node has no mesh data, so just return an empty Polygon.
|
||||||
|
|
||||||
world_transform = self._node.getWorldTransformation()
|
world_transform = self._node.getWorldTransformation(copy= False)
|
||||||
|
|
||||||
# Check the cache
|
# Check the cache
|
||||||
if mesh is self._2d_convex_hull_mesh and world_transform == self._2d_convex_hull_mesh_world_transform:
|
if mesh is self._2d_convex_hull_mesh and world_transform == self._2d_convex_hull_mesh_world_transform:
|
||||||
|
@ -57,13 +57,20 @@ class ConvexHullNode(SceneNode):
|
|||||||
self._hull = hull
|
self._hull = hull
|
||||||
if self._hull:
|
if self._hull:
|
||||||
hull_mesh_builder = MeshBuilder()
|
hull_mesh_builder = MeshBuilder()
|
||||||
|
if self._thickness == 0:
|
||||||
|
if hull_mesh_builder.addConvexPolygon(
|
||||||
|
self._hull.getPoints()[::], # bottom layer is reversed
|
||||||
|
self._mesh_height, color = self._color):
|
||||||
|
|
||||||
if hull_mesh_builder.addConvexPolygonExtrusion(
|
hull_mesh = hull_mesh_builder.build()
|
||||||
self._hull.getPoints()[::-1], # bottom layer is reversed
|
self.setMeshData(hull_mesh)
|
||||||
self._mesh_height - thickness, self._mesh_height, color = self._color):
|
else:
|
||||||
|
if hull_mesh_builder.addConvexPolygonExtrusion(
|
||||||
|
self._hull.getPoints()[::-1], # bottom layer is reversed
|
||||||
|
self._mesh_height - thickness, self._mesh_height, color = self._color):
|
||||||
|
|
||||||
hull_mesh = hull_mesh_builder.build()
|
hull_mesh = hull_mesh_builder.build()
|
||||||
self.setMeshData(hull_mesh)
|
self.setMeshData(hull_mesh)
|
||||||
|
|
||||||
def getHull(self):
|
def getHull(self):
|
||||||
return self._hull
|
return self._hull
|
||||||
@ -79,15 +86,15 @@ class ConvexHullNode(SceneNode):
|
|||||||
ConvexHullNode.shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader"))
|
ConvexHullNode.shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader"))
|
||||||
ConvexHullNode.shader.setUniformValue("u_diffuseColor", self._color)
|
ConvexHullNode.shader.setUniformValue("u_diffuseColor", self._color)
|
||||||
ConvexHullNode.shader.setUniformValue("u_opacity", 0.6)
|
ConvexHullNode.shader.setUniformValue("u_opacity", 0.6)
|
||||||
|
batch = renderer.getNamedBatch("convex_hull_node")
|
||||||
if self.getParent():
|
if not batch:
|
||||||
if self.getMeshData() and isinstance(self._node, SceneNode) and self._node.callDecoration("getBuildPlateNumber") == Application.getInstance().getMultiBuildPlateModel().activeBuildPlate:
|
batch = renderer.createRenderBatch(transparent = True, shader = ConvexHullNode.shader, backface_cull = True, sort = -8)
|
||||||
# The object itself (+ adhesion in one-at-a-time mode)
|
renderer.addRenderBatch(batch, name = "convex_hull_node")
|
||||||
renderer.queueNode(self, transparent = True, shader = ConvexHullNode.shader, backface_cull = True, sort = -8)
|
batch.addItem(self.getWorldTransformation(copy = False), self.getMeshData())
|
||||||
if self._convex_hull_head_mesh:
|
if self._convex_hull_head_mesh:
|
||||||
# The full head. Rendered as a hint to the user: If this area overlaps another object A; this object
|
# The full head. Rendered as a hint to the user: If this area overlaps another object A; this object
|
||||||
# cannot be printed after A, because the head would hit A while printing the current object
|
# cannot be printed after A, because the head would hit A while printing the current object
|
||||||
renderer.queueNode(self, shader = ConvexHullNode.shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8)
|
renderer.queueNode(self, shader = ConvexHullNode.shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -97,7 +104,3 @@ class ConvexHullNode(SceneNode):
|
|||||||
convex_hull_head_builder = MeshBuilder()
|
convex_hull_head_builder = MeshBuilder()
|
||||||
convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height - self._thickness)
|
convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height - self._thickness)
|
||||||
self._convex_hull_head_mesh = convex_hull_head_builder.build()
|
self._convex_hull_head_mesh = convex_hull_head_builder.build()
|
||||||
|
|
||||||
if not node:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ class CuraSceneNode(SceneNode):
|
|||||||
|
|
||||||
self._aabb = None
|
self._aabb = None
|
||||||
if self._mesh_data:
|
if self._mesh_data:
|
||||||
self._aabb = self._mesh_data.getExtents(self.getWorldTransformation())
|
self._aabb = self._mesh_data.getExtents(self.getWorldTransformation(copy = False))
|
||||||
else: # If there is no mesh_data, use a bounding box that encompasses the local (0,0,0)
|
else: # If there is no mesh_data, use a bounding box that encompasses the local (0,0,0)
|
||||||
position = self.getWorldPosition()
|
position = self.getWorldPosition()
|
||||||
self._aabb = AxisAlignedBox(minimum = position, maximum = position)
|
self._aabb = AxisAlignedBox(minimum = position, maximum = position)
|
||||||
@ -139,7 +139,7 @@ class CuraSceneNode(SceneNode):
|
|||||||
"""Taken from SceneNode, but replaced SceneNode with CuraSceneNode"""
|
"""Taken from SceneNode, but replaced SceneNode with CuraSceneNode"""
|
||||||
|
|
||||||
copy = CuraSceneNode(no_setting_override = True) # Setting override will be added later
|
copy = CuraSceneNode(no_setting_override = True) # Setting override will be added later
|
||||||
copy.setTransformation(self.getLocalTransformation())
|
copy.setTransformation(self.getLocalTransformation(copy= False))
|
||||||
copy.setMeshData(self._mesh_data)
|
copy.setMeshData(self._mesh_data)
|
||||||
copy.setVisible(cast(bool, deepcopy(self._visible, memo)))
|
copy.setVisible(cast(bool, deepcopy(self._visible, memo)))
|
||||||
copy._selectable = cast(bool, deepcopy(self._selectable, memo))
|
copy._selectable = cast(bool, deepcopy(self._selectable, memo))
|
||||||
|
@ -204,49 +204,50 @@ class ExtruderManager(QObject):
|
|||||||
# If no extruders are registered in the extruder manager yet, return an empty array
|
# If no extruders are registered in the extruder manager yet, return an empty array
|
||||||
if len(self.extruderIds) == 0:
|
if len(self.extruderIds) == 0:
|
||||||
return []
|
return []
|
||||||
|
number_active_extruders = len([extruder for extruder in self.getActiveExtruderStacks() if extruder.isEnabled])
|
||||||
|
|
||||||
# Get the extruders of all printable meshes in the scene
|
# Get the extruders of all printable meshes in the scene
|
||||||
meshes = [node for node in DepthFirstIterator(scene_root) if isinstance(node, SceneNode) and node.isSelectable()] #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
|
nodes = [node for node in DepthFirstIterator(scene_root) if node.isSelectable() and not node.callDecoration("isAntiOverhangMesh") and not node.callDecoration("isSupportMesh")] #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
|
||||||
|
|
||||||
# Exclude anti-overhang meshes
|
for node in nodes:
|
||||||
mesh_list = []
|
extruder_stack_id = node.callDecoration("getActiveExtruder")
|
||||||
for mesh in meshes:
|
|
||||||
stack = mesh.callDecoration("getStack")
|
|
||||||
if stack is not None and (stack.getProperty("anti_overhang_mesh", "value") or stack.getProperty("support_mesh", "value")):
|
|
||||||
continue
|
|
||||||
mesh_list.append(mesh)
|
|
||||||
|
|
||||||
for mesh in mesh_list:
|
|
||||||
extruder_stack_id = mesh.callDecoration("getActiveExtruder")
|
|
||||||
if not extruder_stack_id:
|
if not extruder_stack_id:
|
||||||
# No per-object settings for this node
|
# No per-object settings for this node
|
||||||
extruder_stack_id = self.extruderIds["0"]
|
extruder_stack_id = self.extruderIds["0"]
|
||||||
used_extruder_stack_ids.add(extruder_stack_id)
|
used_extruder_stack_ids.add(extruder_stack_id)
|
||||||
|
|
||||||
|
if len(used_extruder_stack_ids) == number_active_extruders:
|
||||||
|
# We're already done. Stop looking.
|
||||||
|
# Especially with a lot of models on the buildplate, this will speed up things rather dramatically.
|
||||||
|
break
|
||||||
|
|
||||||
# Get whether any of them use support.
|
# Get whether any of them use support.
|
||||||
stack_to_use = mesh.callDecoration("getStack") # if there is a per-mesh stack, we use it
|
stack_to_use = node.callDecoration("getStack") # if there is a per-mesh stack, we use it
|
||||||
if not stack_to_use:
|
if not stack_to_use:
|
||||||
# if there is no per-mesh stack, we use the build extruder for this mesh
|
# if there is no per-mesh stack, we use the build extruder for this mesh
|
||||||
stack_to_use = container_registry.findContainerStacks(id = extruder_stack_id)[0]
|
stack_to_use = container_registry.findContainerStacks(id = extruder_stack_id)[0]
|
||||||
|
|
||||||
support_enabled |= stack_to_use.getProperty("support_enable", "value")
|
if not support_enabled:
|
||||||
support_bottom_enabled |= stack_to_use.getProperty("support_bottom_enable", "value")
|
support_enabled |= stack_to_use.getProperty("support_enable", "value")
|
||||||
support_roof_enabled |= stack_to_use.getProperty("support_roof_enable", "value")
|
if not support_bottom_enabled:
|
||||||
|
support_bottom_enabled |= stack_to_use.getProperty("support_bottom_enable", "value")
|
||||||
|
if not support_roof_enabled:
|
||||||
|
support_roof_enabled |= stack_to_use.getProperty("support_roof_enable", "value")
|
||||||
|
|
||||||
# Check limit to extruders
|
# Check limit to extruders
|
||||||
limit_to_extruder_feature_list = ["wall_0_extruder_nr",
|
limit_to_extruder_feature_list = ["wall_0_extruder_nr",
|
||||||
"wall_x_extruder_nr",
|
"wall_x_extruder_nr",
|
||||||
"roofing_extruder_nr",
|
"roofing_extruder_nr",
|
||||||
"top_bottom_extruder_nr",
|
"top_bottom_extruder_nr",
|
||||||
"infill_extruder_nr",
|
"infill_extruder_nr",
|
||||||
]
|
]
|
||||||
for extruder_nr_feature_name in limit_to_extruder_feature_list:
|
for extruder_nr_feature_name in limit_to_extruder_feature_list:
|
||||||
extruder_nr = int(global_stack.getProperty(extruder_nr_feature_name, "value"))
|
extruder_nr = int(global_stack.getProperty(extruder_nr_feature_name, "value"))
|
||||||
if extruder_nr == -1:
|
if extruder_nr == -1:
|
||||||
continue
|
continue
|
||||||
if str(extruder_nr) not in self.extruderIds:
|
if str(extruder_nr) not in self.extruderIds:
|
||||||
extruder_nr = int(self._application.getMachineManager().defaultExtruderPosition)
|
extruder_nr = int(self._application.getMachineManager().defaultExtruderPosition)
|
||||||
used_extruder_stack_ids.add(self.extruderIds[str(extruder_nr)])
|
used_extruder_stack_ids.add(self.extruderIds[str(extruder_nr)])
|
||||||
|
|
||||||
# Check support extruders
|
# Check support extruders
|
||||||
if support_enabled:
|
if support_enabled:
|
||||||
|
@ -35,7 +35,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||||||
"""
|
"""
|
||||||
_non_thumbnail_visible_settings = {"anti_overhang_mesh", "infill_mesh", "cutting_mesh", "support_mesh"}
|
_non_thumbnail_visible_settings = {"anti_overhang_mesh", "infill_mesh", "cutting_mesh", "support_mesh"}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, *, force_update = True):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._stack = PerObjectContainerStack(container_id = "per_object_stack_" + str(id(self)))
|
self._stack = PerObjectContainerStack(container_id = "per_object_stack_" + str(id(self)))
|
||||||
self._stack.setDirty(False) # This stack does not need to be saved.
|
self._stack.setDirty(False) # This stack does not need to be saved.
|
||||||
@ -46,6 +46,10 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||||||
|
|
||||||
self._is_non_printing_mesh = False
|
self._is_non_printing_mesh = False
|
||||||
self._is_non_thumbnail_visible_mesh = False
|
self._is_non_thumbnail_visible_mesh = False
|
||||||
|
self._is_support_mesh = False
|
||||||
|
self._is_cutting_mesh = False
|
||||||
|
self._is_infill_mesh = False
|
||||||
|
self._is_anti_overhang_mesh = False
|
||||||
|
|
||||||
self._stack.propertyChanged.connect(self._onSettingChanged)
|
self._stack.propertyChanged.connect(self._onSettingChanged)
|
||||||
|
|
||||||
@ -53,13 +57,14 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||||||
|
|
||||||
Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack)
|
Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack)
|
||||||
self.activeExtruderChanged.connect(self._updateNextStack)
|
self.activeExtruderChanged.connect(self._updateNextStack)
|
||||||
self._updateNextStack()
|
if force_update:
|
||||||
|
self._updateNextStack()
|
||||||
|
|
||||||
def _generateUniqueName(self):
|
def _generateUniqueName(self):
|
||||||
return "SettingOverrideInstanceContainer-%s" % uuid.uuid1()
|
return "SettingOverrideInstanceContainer-%s" % uuid.uuid1()
|
||||||
|
|
||||||
def __deepcopy__(self, memo):
|
def __deepcopy__(self, memo):
|
||||||
deep_copy = SettingOverrideDecorator()
|
deep_copy = SettingOverrideDecorator(force_update = False)
|
||||||
"""Create a fresh decorator object"""
|
"""Create a fresh decorator object"""
|
||||||
|
|
||||||
instance_container = copy.deepcopy(self._stack.getContainer(0), memo)
|
instance_container = copy.deepcopy(self._stack.getContainer(0), memo)
|
||||||
@ -74,11 +79,6 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||||||
# Properly set the right extruder on the copy
|
# Properly set the right extruder on the copy
|
||||||
deep_copy.setActiveExtruder(self._extruder_stack)
|
deep_copy.setActiveExtruder(self._extruder_stack)
|
||||||
|
|
||||||
# use value from the stack because there can be a delay in signal triggering and "_is_non_printing_mesh"
|
|
||||||
# has not been updated yet.
|
|
||||||
deep_copy._is_non_printing_mesh = self._evaluateIsNonPrintingMesh()
|
|
||||||
deep_copy._is_non_thumbnail_visible_mesh = self._evaluateIsNonThumbnailVisibleMesh()
|
|
||||||
|
|
||||||
return deep_copy
|
return deep_copy
|
||||||
|
|
||||||
def getActiveExtruder(self):
|
def getActiveExtruder(self):
|
||||||
@ -104,7 +104,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# for support_meshes, always use the support_extruder
|
# for support_meshes, always use the support_extruder
|
||||||
if self.getStack().getProperty("support_mesh", "value"):
|
if self._is_support_mesh:
|
||||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||||
if global_container_stack:
|
if global_container_stack:
|
||||||
return str(global_container_stack.getProperty("support_extruder_nr", "value"))
|
return str(global_container_stack.getProperty("support_extruder_nr", "value"))
|
||||||
@ -114,6 +114,30 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||||||
container_stack = containers[0]
|
container_stack = containers[0]
|
||||||
return container_stack.getMetaDataEntry("position", default=None)
|
return container_stack.getMetaDataEntry("position", default=None)
|
||||||
|
|
||||||
|
def isCuttingMesh(self):
|
||||||
|
return self._is_cutting_mesh
|
||||||
|
|
||||||
|
def isSupportMesh(self):
|
||||||
|
return self._is_support_mesh
|
||||||
|
|
||||||
|
def isInfillMesh(self):
|
||||||
|
return self._is_infill_mesh
|
||||||
|
|
||||||
|
def isAntiOverhangMesh(self):
|
||||||
|
return self._is_anti_overhang_mesh
|
||||||
|
|
||||||
|
def _evaluateAntiOverhangMesh(self):
|
||||||
|
return bool(self._stack.userChanges.getProperty("anti_overhang_mesh", "value"))
|
||||||
|
|
||||||
|
def _evaluateIsCuttingMesh(self):
|
||||||
|
return bool(self._stack.userChanges.getProperty("cutting_mesh", "value"))
|
||||||
|
|
||||||
|
def _evaluateIsSupportMesh(self):
|
||||||
|
return bool(self._stack.userChanges.getProperty("support_mesh", "value"))
|
||||||
|
|
||||||
|
def _evaluateInfillMesh(self):
|
||||||
|
return bool(self._stack.userChanges.getProperty("infill_mesh", "value"))
|
||||||
|
|
||||||
def isNonPrintingMesh(self):
|
def isNonPrintingMesh(self):
|
||||||
return self._is_non_printing_mesh
|
return self._is_non_printing_mesh
|
||||||
|
|
||||||
@ -132,6 +156,16 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||||||
# Trigger slice/need slicing if the value has changed.
|
# Trigger slice/need slicing if the value has changed.
|
||||||
self._is_non_printing_mesh = self._evaluateIsNonPrintingMesh()
|
self._is_non_printing_mesh = self._evaluateIsNonPrintingMesh()
|
||||||
self._is_non_thumbnail_visible_mesh = self._evaluateIsNonThumbnailVisibleMesh()
|
self._is_non_thumbnail_visible_mesh = self._evaluateIsNonThumbnailVisibleMesh()
|
||||||
|
|
||||||
|
if setting_key == "anti_overhang_mesh":
|
||||||
|
self._is_anti_overhang_mesh = self._evaluateAntiOverhangMesh()
|
||||||
|
elif setting_key == "support_mesh":
|
||||||
|
self._is_support_mesh = self._evaluateIsSupportMesh()
|
||||||
|
elif setting_key == "cutting_mesh":
|
||||||
|
self._is_cutting_mesh = self._evaluateIsCuttingMesh()
|
||||||
|
elif setting_key == "infill_mesh":
|
||||||
|
self._is_infill_mesh = self._evaluateInfillMesh()
|
||||||
|
|
||||||
Application.getInstance().getBackend().needsSlicing()
|
Application.getInstance().getBackend().needsSlicing()
|
||||||
Application.getInstance().getBackend().tickle()
|
Application.getInstance().getBackend().tickle()
|
||||||
|
|
||||||
|
@ -98,7 +98,8 @@ class ObjectsModel(ListModel):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _renameNodes(self, node_info_dict: Dict[str, _NodeInfo]) -> List[SceneNode]:
|
@staticmethod
|
||||||
|
def _renameNodes(node_info_dict: Dict[str, _NodeInfo]) -> List[SceneNode]:
|
||||||
# Go through all names and find out the names for all nodes that need to be renamed.
|
# Go through all names and find out the names for all nodes that need to be renamed.
|
||||||
all_nodes = [] # type: List[SceneNode]
|
all_nodes = [] # type: List[SceneNode]
|
||||||
for name, node_info in node_info_dict.items():
|
for name, node_info in node_info_dict.items():
|
||||||
@ -118,9 +119,7 @@ class ObjectsModel(ListModel):
|
|||||||
else:
|
else:
|
||||||
new_group_name = "{0}#{1}".format(name, current_index)
|
new_group_name = "{0}#{1}".format(name, current_index)
|
||||||
|
|
||||||
old_name = node.getName()
|
|
||||||
node.setName(new_group_name)
|
node.setName(new_group_name)
|
||||||
Logger.log("d", "Node [%s] renamed to [%s]", old_name, new_group_name)
|
|
||||||
all_nodes.append(node)
|
all_nodes.append(node)
|
||||||
return all_nodes
|
return all_nodes
|
||||||
|
|
||||||
@ -186,11 +185,18 @@ class ObjectsModel(ListModel):
|
|||||||
if per_object_stack:
|
if per_object_stack:
|
||||||
per_object_settings_count = per_object_stack.getTop().getNumInstances()
|
per_object_settings_count = per_object_stack.getTop().getNumInstances()
|
||||||
|
|
||||||
for mesh_type in ["anti_overhang_mesh", "infill_mesh", "cutting_mesh", "support_mesh"]:
|
if node.callDecoration("isAntiOverhangMesh"):
|
||||||
if per_object_stack.getProperty(mesh_type, "value"):
|
node_mesh_type = "anti_overhang_mesh"
|
||||||
node_mesh_type = mesh_type
|
per_object_settings_count -= 1 # do not count this mesh type setting
|
||||||
per_object_settings_count -= 1 # do not count this mesh type setting
|
elif node.callDecoration("isSupportMesh"):
|
||||||
break
|
node_mesh_type = "support_mesh"
|
||||||
|
per_object_settings_count -= 1 # do not count this mesh type setting
|
||||||
|
elif node.callDecoration("isCuttingMesh"):
|
||||||
|
node_mesh_type = "cutting_mesh"
|
||||||
|
per_object_settings_count -= 1 # do not count this mesh type setting
|
||||||
|
elif node.callDecoration("isInfillMesh"):
|
||||||
|
node_mesh_type = "infill_mesh"
|
||||||
|
per_object_settings_count -= 1 # do not count this mesh type setting
|
||||||
|
|
||||||
if per_object_settings_count > 0:
|
if per_object_settings_count > 0:
|
||||||
if node_mesh_type == "support_mesh":
|
if node_mesh_type == "support_mesh":
|
||||||
|
@ -29,7 +29,7 @@ class XRayPass(RenderPass):
|
|||||||
batch = RenderBatch(self._shader, type = RenderBatch.RenderType.NoType, backface_cull = False, blend_mode = RenderBatch.BlendMode.Additive)
|
batch = RenderBatch(self._shader, type = RenderBatch.RenderType.NoType, backface_cull = False, blend_mode = RenderBatch.BlendMode.Additive)
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
if isinstance(node, CuraSceneNode) and node.getMeshData() and node.isVisible():
|
if isinstance(node, CuraSceneNode) and node.getMeshData() and node.isVisible():
|
||||||
batch.addItem(node.getWorldTransformation(), node.getMeshData())
|
batch.addItem(node.getWorldTransformation(copy = False), node.getMeshData())
|
||||||
|
|
||||||
self.bind()
|
self.bind()
|
||||||
|
|
||||||
|
@ -8,12 +8,14 @@ import time
|
|||||||
from typing import Any, cast, Dict, List, Optional, Set
|
from typing import Any, cast, Dict, List, Optional, Set
|
||||||
import re
|
import re
|
||||||
import Arcus #For typing.
|
import Arcus #For typing.
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
from UM.Job import Job
|
from UM.Job import Job
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from UM.Scene.SceneNode import SceneNode
|
||||||
from UM.Settings.ContainerStack import ContainerStack #For typing.
|
from UM.Settings.ContainerStack import ContainerStack #For typing.
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer
|
from UM.Settings.InstanceContainer import InstanceContainer
|
||||||
|
from UM.Settings.Interfaces import ContainerInterface
|
||||||
from UM.Settings.SettingDefinition import SettingDefinition
|
from UM.Settings.SettingDefinition import SettingDefinition
|
||||||
from UM.Settings.SettingRelation import SettingRelation #For typing.
|
from UM.Settings.SettingRelation import SettingRelation #For typing.
|
||||||
|
|
||||||
@ -352,8 +354,7 @@ class StartSliceJob(Job):
|
|||||||
|
|
||||||
result = {}
|
result = {}
|
||||||
for key in stack.getAllKeys():
|
for key in stack.getAllKeys():
|
||||||
value = stack.getProperty(key, "value")
|
result[key] = stack.getProperty(key, "value")
|
||||||
result[key] = value
|
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
|
||||||
result["print_bed_temperature"] = result["material_bed_temperature"] # Renamed settings.
|
result["print_bed_temperature"] = result["material_bed_temperature"] # Renamed settings.
|
||||||
@ -373,9 +374,11 @@ class StartSliceJob(Job):
|
|||||||
self._all_extruders_settings = {
|
self._all_extruders_settings = {
|
||||||
"-1": self._buildReplacementTokens(global_stack)
|
"-1": self._buildReplacementTokens(global_stack)
|
||||||
}
|
}
|
||||||
|
QCoreApplication.processEvents() # Ensure that the GUI does not freeze.
|
||||||
for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks():
|
for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks():
|
||||||
extruder_nr = extruder_stack.getProperty("extruder_nr", "value")
|
extruder_nr = extruder_stack.getProperty("extruder_nr", "value")
|
||||||
self._all_extruders_settings[str(extruder_nr)] = self._buildReplacementTokens(extruder_stack)
|
self._all_extruders_settings[str(extruder_nr)] = self._buildReplacementTokens(extruder_stack)
|
||||||
|
QCoreApplication.processEvents() # Ensure that the GUI does not freeze.
|
||||||
|
|
||||||
def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1) -> str:
|
def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1) -> str:
|
||||||
"""Replace setting tokens in a piece of g-code.
|
"""Replace setting tokens in a piece of g-code.
|
||||||
@ -420,10 +423,15 @@ class StartSliceJob(Job):
|
|||||||
settings["machine_extruder_start_code"] = self._expandGcodeTokens(settings["machine_extruder_start_code"], extruder_nr)
|
settings["machine_extruder_start_code"] = self._expandGcodeTokens(settings["machine_extruder_start_code"], extruder_nr)
|
||||||
settings["machine_extruder_end_code"] = self._expandGcodeTokens(settings["machine_extruder_end_code"], extruder_nr)
|
settings["machine_extruder_end_code"] = self._expandGcodeTokens(settings["machine_extruder_end_code"], extruder_nr)
|
||||||
|
|
||||||
|
global_definition = cast(ContainerInterface, cast(ContainerStack, stack.getNextStack()).getBottom())
|
||||||
|
own_definition = cast(ContainerInterface, stack.getBottom())
|
||||||
|
|
||||||
for key, value in settings.items():
|
for key, value in settings.items():
|
||||||
# Do not send settings that are not settable_per_extruder.
|
# Do not send settings that are not settable_per_extruder.
|
||||||
if not stack.getProperty(key, "settable_per_extruder"):
|
# Since these can only be set in definition files, we only have to ask there.
|
||||||
continue
|
if not global_definition.getProperty(key, "settable_per_extruder") and \
|
||||||
|
not own_definition.getProperty(key, "settable_per_extruder"):
|
||||||
|
continue
|
||||||
setting = message.getMessage("settings").addRepeatedMessage("settings")
|
setting = message.getMessage("settings").addRepeatedMessage("settings")
|
||||||
setting.name = key
|
setting.name = key
|
||||||
setting.value = str(value).encode("utf-8")
|
setting.value = str(value).encode("utf-8")
|
||||||
@ -454,11 +462,10 @@ class StartSliceJob(Job):
|
|||||||
print_temperature_settings = ["material_print_temperature", "material_print_temperature_layer_0", "default_material_print_temperature", "material_initial_print_temperature", "material_final_print_temperature", "material_standby_temperature"]
|
print_temperature_settings = ["material_print_temperature", "material_print_temperature_layer_0", "default_material_print_temperature", "material_initial_print_temperature", "material_final_print_temperature", "material_standby_temperature"]
|
||||||
pattern = r"\{(%s)(,\s?\w+)?\}" % "|".join(print_temperature_settings) # match {setting} as well as {setting, extruder_nr}
|
pattern = r"\{(%s)(,\s?\w+)?\}" % "|".join(print_temperature_settings) # match {setting} as well as {setting, extruder_nr}
|
||||||
settings["material_print_temp_prepend"] = re.search(pattern, start_gcode) == None
|
settings["material_print_temp_prepend"] = re.search(pattern, start_gcode) == None
|
||||||
|
|
||||||
# Replace the setting tokens in start and end g-code.
|
# Replace the setting tokens in start and end g-code.
|
||||||
# Use values from the first used extruder by default so we get the expected temperatures
|
# Use values from the first used extruder by default so we get the expected temperatures
|
||||||
initial_extruder_stack = CuraApplication.getInstance().getExtruderManager().getUsedExtruderStacks()[0]
|
initial_extruder_nr = CuraApplication.getInstance().getExtruderManager().getInitialExtruderNr()
|
||||||
initial_extruder_nr = initial_extruder_stack.getProperty("extruder_nr", "value")
|
|
||||||
|
|
||||||
settings["machine_start_gcode"] = self._expandGcodeTokens(settings["machine_start_gcode"], initial_extruder_nr)
|
settings["machine_start_gcode"] = self._expandGcodeTokens(settings["machine_start_gcode"], initial_extruder_nr)
|
||||||
settings["machine_end_gcode"] = self._expandGcodeTokens(settings["machine_end_gcode"], initial_extruder_nr)
|
settings["machine_end_gcode"] = self._expandGcodeTokens(settings["machine_end_gcode"], initial_extruder_nr)
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ class SliceInfo(QObject, Extension):
|
|||||||
"maximum": {"x": bounding_box.maximum.x,
|
"maximum": {"x": bounding_box.maximum.x,
|
||||||
"y": bounding_box.maximum.y,
|
"y": bounding_box.maximum.y,
|
||||||
"z": bounding_box.maximum.z}}
|
"z": bounding_box.maximum.z}}
|
||||||
model["transformation"] = {"data": str(node.getWorldTransformation().getData()).replace("\n", "")}
|
model["transformation"] = {"data": str(node.getWorldTransformation(copy = False).getData()).replace("\n", "")}
|
||||||
extruder_position = node.callDecoration("getActiveExtruderPosition")
|
extruder_position = node.callDecoration("getActiveExtruderPosition")
|
||||||
model["extruder"] = 0 if extruder_position is None else int(extruder_position)
|
model["extruder"] = 0 if extruder_position is None else int(extruder_position)
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ class SolidView(View):
|
|||||||
self._old_layer_bindings = None
|
self._old_layer_bindings = None
|
||||||
|
|
||||||
self._next_xray_checking_time = time.time()
|
self._next_xray_checking_time = time.time()
|
||||||
self._xray_checking_update_time = 1.0 # seconds
|
self._xray_checking_update_time = 30.0 # seconds
|
||||||
self._xray_warning_cooldown = 60 * 10 # reshow Model error message every 10 minutes
|
self._xray_warning_cooldown = 60 * 10 # reshow Model error message every 10 minutes
|
||||||
self._xray_warning_message = Message(
|
self._xray_warning_message = Message(
|
||||||
catalog.i18nc("@info:status", "Your model is not manifold. The highlighted areas indicate either missing or extraneous surfaces."),
|
catalog.i18nc("@info:status", "Your model is not manifold. The highlighted areas indicate either missing or extraneous surfaces."),
|
||||||
@ -103,7 +103,9 @@ class SolidView(View):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self._support_angle = support_angle_stack.getProperty("support_angle", "value")
|
angle = support_angle_stack.getProperty("support_angle", "value")
|
||||||
|
if angle is not None:
|
||||||
|
self._support_angle = angle
|
||||||
|
|
||||||
def _checkSetup(self):
|
def _checkSetup(self):
|
||||||
if not self._extruders_model:
|
if not self._extruders_model:
|
||||||
@ -178,77 +180,77 @@ class SolidView(View):
|
|||||||
if global_container_stack:
|
if global_container_stack:
|
||||||
if Application.getInstance().getPreferences().getValue("view/show_overhang"):
|
if Application.getInstance().getPreferences().getValue("view/show_overhang"):
|
||||||
# Make sure the overhang angle is valid before passing it to the shader
|
# Make sure the overhang angle is valid before passing it to the shader
|
||||||
if self._support_angle is not None and self._support_angle >= 0 and self._support_angle <= 90:
|
if self._support_angle >= 0 and self._support_angle <= 90:
|
||||||
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(90 - self._support_angle)))
|
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(90 - self._support_angle)))
|
||||||
else:
|
else:
|
||||||
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) #Overhang angle of 0 causes no area at all to be marked as overhang.
|
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) #Overhang angle of 0 causes no area at all to be marked as overhang.
|
||||||
else:
|
else:
|
||||||
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0)))
|
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0)))
|
||||||
|
disabled_batch = renderer.createRenderBatch(shader = self._disabled_shader)
|
||||||
|
normal_object_batch = renderer.createRenderBatch(shader = self._enabled_shader)
|
||||||
|
renderer.addRenderBatch(disabled_batch)
|
||||||
|
renderer.addRenderBatch(normal_object_batch)
|
||||||
for node in DepthFirstIterator(scene.getRoot()):
|
for node in DepthFirstIterator(scene.getRoot()):
|
||||||
if not node.render(renderer):
|
if node.render(renderer):
|
||||||
if node.getMeshData() and node.isVisible() and not node.callDecoration("getLayerData"):
|
continue
|
||||||
uniforms = {}
|
|
||||||
shade_factor = 1.0
|
|
||||||
|
|
||||||
per_mesh_stack = node.callDecoration("getStack")
|
if node.getMeshData() and node.isVisible():
|
||||||
|
uniforms = {}
|
||||||
|
shade_factor = 1.0
|
||||||
|
|
||||||
extruder_index = node.callDecoration("getActiveExtruderPosition")
|
per_mesh_stack = node.callDecoration("getStack")
|
||||||
if extruder_index is None:
|
|
||||||
extruder_index = "0"
|
|
||||||
extruder_index = int(extruder_index)
|
|
||||||
|
|
||||||
# Use the support extruder instead of the active extruder if this is a support_mesh
|
extruder_index = node.callDecoration("getActiveExtruderPosition")
|
||||||
if per_mesh_stack:
|
if extruder_index is None:
|
||||||
if per_mesh_stack.getProperty("support_mesh", "value"):
|
extruder_index = "0"
|
||||||
extruder_index = int(global_container_stack.getExtruderPositionValueWithDefault("support_extruder_nr"))
|
extruder_index = int(extruder_index)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
material_color = self._extruders_model.getItem(extruder_index)["color"]
|
material_color = self._extruders_model.getItem(extruder_index)["color"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
material_color = self._extruders_model.defaultColors[0]
|
material_color = self._extruders_model.defaultColors[0]
|
||||||
|
|
||||||
if extruder_index != ExtruderManager.getInstance().activeExtruderIndex:
|
if extruder_index != ExtruderManager.getInstance().activeExtruderIndex:
|
||||||
# Shade objects that are printed with the non-active extruder 25% darker
|
# Shade objects that are printed with the non-active extruder 25% darker
|
||||||
shade_factor = 0.6
|
shade_factor = 0.6
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs
|
# Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs
|
||||||
# an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0])
|
# an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0])
|
||||||
uniforms["diffuse_color"] = [
|
uniforms["diffuse_color"] = [
|
||||||
shade_factor * int(material_color[1:3], 16) / 255,
|
shade_factor * int(material_color[1:3], 16) / 255,
|
||||||
shade_factor * int(material_color[3:5], 16) / 255,
|
shade_factor * int(material_color[3:5], 16) / 255,
|
||||||
shade_factor * int(material_color[5:7], 16) / 255,
|
shade_factor * int(material_color[5:7], 16) / 255,
|
||||||
1.0
|
1.0
|
||||||
]
|
]
|
||||||
|
|
||||||
# Color the currently selected face-id. (Disable for now.)
|
# Color the currently selected face-id. (Disable for now.)
|
||||||
#face = Selection.getHoverFace()
|
#face = Selection.getHoverFace()
|
||||||
uniforms["hover_face"] = -1 #if not face or node != face[0] else face[1]
|
uniforms["hover_face"] = -1 #if not face or node != face[0] else face[1]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if node.callDecoration("isNonPrintingMesh"):
|
if node.callDecoration("isNonPrintingMesh"):
|
||||||
if per_mesh_stack and (per_mesh_stack.getProperty("infill_mesh", "value") or per_mesh_stack.getProperty("cutting_mesh", "value")):
|
if per_mesh_stack and (node.callDecoration("isInfillMesh") or node.callDecoration("isCuttingMesh")):
|
||||||
renderer.queueNode(node, shader = self._non_printing_shader, uniforms = uniforms, transparent = True)
|
renderer.queueNode(node, shader = self._non_printing_shader, uniforms = uniforms, transparent = True)
|
||||||
else:
|
|
||||||
renderer.queueNode(node, shader = self._non_printing_shader, transparent = True)
|
|
||||||
elif getattr(node, "_outside_buildarea", False):
|
|
||||||
renderer.queueNode(node, shader = self._disabled_shader)
|
|
||||||
elif per_mesh_stack and per_mesh_stack.getProperty("support_mesh", "value"):
|
|
||||||
# Render support meshes with a vertical stripe that is darker
|
|
||||||
shade_factor = 0.6
|
|
||||||
uniforms["diffuse_color_2"] = [
|
|
||||||
uniforms["diffuse_color"][0] * shade_factor,
|
|
||||||
uniforms["diffuse_color"][1] * shade_factor,
|
|
||||||
uniforms["diffuse_color"][2] * shade_factor,
|
|
||||||
1.0
|
|
||||||
]
|
|
||||||
renderer.queueNode(node, shader = self._support_mesh_shader, uniforms = uniforms)
|
|
||||||
else:
|
else:
|
||||||
renderer.queueNode(node, shader = self._enabled_shader, uniforms = uniforms)
|
renderer.queueNode(node, shader = self._non_printing_shader, transparent = True)
|
||||||
if node.callDecoration("isGroup") and Selection.isSelected(node):
|
elif getattr(node, "_outside_buildarea", False):
|
||||||
renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(), mode = RenderBatch.RenderMode.LineLoop)
|
disabled_batch.addItem(node.getWorldTransformation(copy = False), node.getMeshData())
|
||||||
|
elif per_mesh_stack and node.callDecoration("isSupportMesh"):
|
||||||
|
# Render support meshes with a vertical stripe that is darker
|
||||||
|
shade_factor = 0.6
|
||||||
|
uniforms["diffuse_color_2"] = [
|
||||||
|
uniforms["diffuse_color"][0] * shade_factor,
|
||||||
|
uniforms["diffuse_color"][1] * shade_factor,
|
||||||
|
uniforms["diffuse_color"][2] * shade_factor,
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
renderer.queueNode(node, shader = self._support_mesh_shader, uniforms = uniforms)
|
||||||
|
else:
|
||||||
|
normal_object_batch.addItem(node.getWorldTransformation(copy=False), node.getMeshData(), uniforms=uniforms)
|
||||||
|
if node.callDecoration("isGroup") and Selection.isSelected(node):
|
||||||
|
renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(), mode = RenderBatch.RenderMode.LineLoop)
|
||||||
|
|
||||||
def endRendering(self):
|
def endRendering(self):
|
||||||
# check whether the xray overlay is showing badness
|
# check whether the xray overlay is showing badness
|
||||||
|
Loading…
x
Reference in New Issue
Block a user