diff --git a/cura/CuraActions.py b/cura/CuraActions.py index 20c44c7916..3a1b35d987 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -3,15 +3,17 @@ from PyQt5.QtCore import QObject, QUrl from PyQt5.QtGui import QDesktopServices -from typing import List, cast +from typing import List, Optional, cast from UM.Event import CallFunctionEvent from UM.FlameProfiler import pyqtSlot +from UM.Math.Quaternion import Quaternion from UM.Math.Vector import Vector from UM.Scene.Selection import Selection from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation +from UM.Operations.RotateOperation import RotateOperation from UM.Operations.TranslateOperation import TranslateOperation import cura.CuraApplication @@ -73,6 +75,39 @@ class CuraActions(QObject): operation.addOperation(center_operation) operation.push() + # Rotate the selection, so that the face that the mouse-pointer is on, faces the build-plate. + @pyqtSlot() + def bottomFaceSelection(self) -> None: + selected_face = Selection.getSelectedFace() + if not selected_face: + Logger.log("e", "Bottom face operation shouldn't have been called without a selected face.") + return + + original_node, face_id = selected_face + meshdata = original_node.getMeshDataTransformed() + if not meshdata or face_id < 0 or face_id > Selection.getMaxFaceSelectionId(): + return + + rotation_point, face_normal = meshdata.getFacePlane(face_id) + rotation_point_vector = Vector(rotation_point[0], rotation_point[1], rotation_point[2]) + face_normal_vector = Vector(face_normal[0], face_normal[1], face_normal[2]) + rotation_quaternion = Quaternion.rotationTo(face_normal_vector.normalized(), Vector(0.0, -1.0, 0.0)) + + operation = GroupedOperation() + current_node = None # type: Optional[SceneNode] + for node in Selection.getAllSelectedObjects(): + current_node = node + parent_node = current_node.getParent() + while parent_node and parent_node.callDecoration("isGroup"): + current_node = parent_node + parent_node = current_node.getParent() + if current_node is None: + return + + rotate_operation = RotateOperation(current_node, rotation_quaternion, rotation_point_vector) + operation.addOperation(rotate_operation) + operation.push() + ## Multiply all objects in the selection # # \param count The number of times to multiply the selection. diff --git a/plugins/CuraProfileReader/CuraProfileReader.py b/plugins/CuraProfileReader/CuraProfileReader.py index 8ae7b7e0b0..5e2a4266c8 100644 --- a/plugins/CuraProfileReader/CuraProfileReader.py +++ b/plugins/CuraProfileReader/CuraProfileReader.py @@ -93,7 +93,11 @@ class CuraProfileReader(ProfileReader): Logger.log("e", "Error while trying to parse profile: %s", str(e)) return None - active_quality_definition = getMachineDefinitionIDForQualitySearch(CuraApplication.getInstance().getGlobalContainerStack().definition) + global_stack = CuraApplication.getInstance().getGlobalContainerStack() + if global_stack is None: + return None + + active_quality_definition = getMachineDefinitionIDForQualitySearch(global_stack.definition) if profile.getMetaDataEntry("definition") != active_quality_definition: profile.setMetaDataEntry("definition", active_quality_definition) return profile diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 4ce8ae7bc4..a8cff675d9 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -139,6 +139,10 @@ class SolidView(View): shade_factor * int(material_color[5:7], 16) / 255, 1.0 ] + + # Color the currently selected face-id. + face = Selection.getSelectedFace() + uniforms["selected_face"] = (Selection.getMaxFaceSelectionId() + 1) if not face or node != face[0] else face[1] except ValueError: pass diff --git a/resources/shaders/overhang.shader b/resources/shaders/overhang.shader index e1c03f7586..cb34f25893 100644 --- a/resources/shaders/overhang.shader +++ b/resources/shaders/overhang.shader @@ -32,6 +32,8 @@ fragment = uniform lowp float u_overhangAngle; uniform lowp vec4 u_overhangColor; + uniform lowp vec4 u_faceColor; + uniform highp int u_faceId; varying highp vec3 f_vertex; varying highp vec3 f_normal; @@ -58,7 +60,7 @@ fragment = highp float NdotR = clamp(dot(viewVector, reflectedLight), 0.0, 1.0); finalColor += pow(NdotR, u_shininess) * u_specularColor; - finalColor = (-normal.y > u_overhangAngle) ? u_overhangColor : finalColor; + finalColor = (u_faceId != gl_PrimitiveID) ? ((-normal.y > u_overhangAngle) ? u_overhangColor : finalColor) : u_faceColor; gl_FragColor = finalColor; gl_FragColor.a = 1.0; @@ -99,6 +101,8 @@ fragment41core = uniform lowp float u_overhangAngle; uniform lowp vec4 u_overhangColor; + uniform lowp vec4 u_faceColor; + uniform highp int u_faceId; in highp vec3 f_vertex; in highp vec3 f_normal; @@ -127,7 +131,7 @@ fragment41core = highp float NdotR = clamp(dot(viewVector, reflectedLight), 0.0, 1.0); finalColor += pow(NdotR, u_shininess) * u_specularColor; - finalColor = (-normal.y > u_overhangAngle) ? u_overhangColor : finalColor; + finalColor = (u_faceId != gl_PrimitiveID) ? ((-normal.y > u_overhangAngle) ? u_overhangColor : finalColor) : u_faceColor; frag_color = finalColor; frag_color.a = 1.0; @@ -138,6 +142,7 @@ u_ambientColor = [0.3, 0.3, 0.3, 1.0] u_diffuseColor = [1.0, 0.79, 0.14, 1.0] u_specularColor = [0.4, 0.4, 0.4, 1.0] u_overhangColor = [1.0, 0.0, 0.0, 1.0] +u_faceColor = [0.0, 0.0, 1.0, 1.0] u_shininess = 20.0 [bindings] @@ -148,6 +153,7 @@ u_normalMatrix = normal_matrix u_viewPosition = view_position u_lightPosition = light_0_position u_diffuseColor = diffuse_color +u_faceId = selected_face [attributes] a_vertex = vertex