Assembly: Remove Lock from labels and add lock to 3dView

This commit is contained in:
PaddleStroke
2024-03-15 15:40:36 +01:00
committed by Yorik van Havre
parent 7327f8011a
commit cf81b18d21
3 changed files with 184 additions and 54 deletions

View File

@@ -246,7 +246,6 @@ def createGroundedJoint(obj):
joint_group = UtilsAssembly.getJointGroup(assembly)
obj.Label = obj.Label + " 🔒"
ground = joint_group.newObject("App::FeaturePython", "GroundedJoint")
JointObject.GroundedJoint(ground, obj)
JointObject.ViewProviderGroundedJoint(ground.ViewObject)
@@ -310,9 +309,6 @@ class CommandToggleGrounded:
hasattr(joint, "ObjectToGround")
and joint.ObjectToGround == part_containing_obj
):
# Remove grounded tag.
if part_containing_obj.Label.endswith(" 🔒"):
part_containing_obj.Label = part_containing_obj.Label[:-2]
doc = App.ActiveDocument
doc.removeObject(joint.Name)
doc.recompute()

View File

@@ -173,14 +173,6 @@ bool ViewProviderAssembly::canDragObject(App::DocumentObject* obj) const
}
Gui::Command::commitCommand();
// Remove grounded tag if any. (as it is not done in jointObject.py onDelete)
std::string label = obj->Label.getValue();
if (label.size() >= 4 && label.substr(label.size() - 2) == " 🔒") {
label = label.substr(0, label.size() - 2);
obj->Label.setValue(label.c_str());
}
return true;
}
@@ -512,9 +504,17 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection
if (assemblyPart->hasObject(obj, true)) {
auto* propPlacement =
dynamic_cast<App::PropertyPlacement*>(obj->getPropertyByName("Placement"));
if (propPlacement) {
docsToMove.emplace_back(obj, propPlacement->getValue());
if (!propPlacement) {
continue;
}
// We have to exclude Grounded joints as they happen to have a Placement prop
auto* propLink =
dynamic_cast<App::PropertyLink*>(obj->getPropertyByName("ObjectToGround"));
if (propLink) {
continue;
}
docsToMove.emplace_back(obj, propPlacement->getValue());
}
}
}
@@ -544,7 +544,12 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection
auto* propPlacement = dynamic_cast<App::PropertyPlacement*>(
preselectedObj->getPropertyByName("Placement"));
if (propPlacement) {
docsToMove.emplace_back(preselectedObj, propPlacement->getValue());
// We have to exclude Grounded joints as they happen to have a Placement prop
auto* propLink = dynamic_cast<App::PropertyLink*>(
preselectedObj->getPropertyByName("ObjectToGround"));
if (!propLink) {
docsToMove.emplace_back(preselectedObj, propPlacement->getValue());
}
}
}
}
@@ -607,7 +612,7 @@ App::DocumentObject* ViewProviderAssembly::getObjectFromSubNames(std::vector<std
continue;
}
else if (obj->getTypeId().isDerivedFrom(App::Part::getClassTypeId())
|| obj->getTypeId().isDerivedFrom(PartDesign::Body::getClassTypeId())) {
|| obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
return obj;
}
else if (obj->getTypeId().isDerivedFrom(App::Link::getClassTypeId())) {
@@ -619,16 +624,13 @@ App::DocumentObject* ViewProviderAssembly::getObjectFromSubNames(std::vector<std
}
if (linkedObj->getTypeId().isDerivedFrom(App::Part::getClassTypeId())
|| linkedObj->getTypeId().isDerivedFrom(PartDesign::Body::getClassTypeId())) {
|| linkedObj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
return obj;
}
}
}
// then its neither a part or body or a link to a part or body. So it is something like
// assembly.box.face1
objName = subNames[subNames.size() - 2];
return appDoc->getObject(objName.c_str());
return nullptr;
}
ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode()

View File

@@ -92,6 +92,19 @@ def solveIfAllowed(assembly, storePrev=False):
assembly.solve(storePrev)
def get_camera_height(gui_doc):
camera = gui_doc.ActiveView.getCameraNode()
# Check if the camera is a perspective camera
if isinstance(camera, coin.SoPerspectiveCamera):
return camera.focalDistance.getValue()
elif isinstance(camera, coin.SoOrthographicCamera):
return camera.height.getValue()
else:
# Default value if camera type is unknown
return 200
# The joint object consists of 2 JCS (joint coordinate systems) and a Joint Type.
# A JCS is a placement that is computed (unless it is detached) from :
# - An Object name: this is the name of the solid. It can be any Part::Feature solid.
@@ -436,8 +449,14 @@ class Joint:
# in the current closest direction, ie either matched or flipped.
sameDir = self.areJcsSameDir(joint)
assembly = self.getAssembly(joint)
part1ConnectedByJoint = assembly.isJointConnectingPartToGround(joint, "Part1")
part2ConnectedByJoint = assembly.isJointConnectingPartToGround(joint, "Part2")
isAssembly = assembly.Type == "Assembly"
if isAssembly:
part1ConnectedByJoint = assembly.isJointConnectingPartToGround(joint, "Part1")
part2ConnectedByJoint = assembly.isJointConnectingPartToGround(joint, "Part2")
else:
part1ConnectedByJoint = False
part2ConnectedByJoint = True
if part2ConnectedByJoint:
if savePlc:
self.partMovedByPresolved = joint.Part2
@@ -509,15 +528,16 @@ class ViewProviderJoint:
self.z_axis_so_color = coin.SoBaseColor()
self.z_axis_so_color.rgb.setValue(UtilsAssembly.color_from_unsigned(param_z_axis_color))
camera = Gui.ActiveDocument.ActiveView.getCameraNode()
self.app_obj = vobj.Object
app_doc = self.app_obj.Document
self.gui_doc = Gui.getDocument(app_doc)
camera = self.gui_doc.ActiveView.getCameraNode()
self.cameraSensor = coin.SoFieldSensor(self.camera_callback, camera)
if isinstance(camera, coin.SoPerspectiveCamera):
self.cameraSensor.attach(camera.focalDistance)
elif isinstance(camera, coin.SoOrthographicCamera):
self.cameraSensor.attach(camera.height)
self.app_obj = vobj.Object
self.transform1 = coin.SoTransform()
self.transform2 = coin.SoTransform()
self.transform3 = coin.SoTransform()
@@ -616,18 +636,7 @@ class ViewProviderJoint:
return face_sep
def get_JCS_size(self):
camera = Gui.ActiveDocument.ActiveView.getCameraNode()
# Check if the camera is a perspective camera
if isinstance(camera, coin.SoPerspectiveCamera):
return camera.focalDistance.getValue() / 20
elif isinstance(camera, coin.SoOrthographicCamera):
return camera.height.getValue() / 20
else:
# Default value if camera type is unknown
return 10
return camera.height.getValue() / 20
return get_camera_height(self.gui_doc) / 20
def set_JCS_placement(self, soTransform, placement, objName, part):
# change plc to be relative to the origin of the document.
@@ -735,7 +744,7 @@ class ViewProviderJoint:
assembly = vobj.Object.InList[0]
if UtilsAssembly.activeAssembly() != assembly:
Gui.ActiveDocument.setEdit(assembly)
self.gui_doc.setEdit(assembly)
panel = TaskAssemblyCreateJoint(0, vobj.Object)
Gui.Control.showDialog(panel)
@@ -792,14 +801,127 @@ class ViewProviderGroundedJoint:
"""Set this object to the proxy object of the actual view provider"""
obj.Proxy = self
def attach(self, obj):
def attach(self, vobj):
"""Setup the scene sub-graph of the view provider, this method is mandatory"""
pass
app_obj = vobj.Object
if app_obj is None:
return
groundedObj = app_obj.ObjectToGround
if groundedObj is None:
return
lockpadColorInt = Preferences.preferences().GetUnsigned("AssemblyConstraints", 0xCC333300)
self.lockpadColor = coin.SoBaseColor()
self.lockpadColor.rgb.setValue(UtilsAssembly.color_from_unsigned(lockpadColorInt))
self.app_obj = vobj.Object
app_doc = self.app_obj.Document
self.gui_doc = Gui.getDocument(app_doc)
camera = self.gui_doc.ActiveView.getCameraNode()
self.cameraSensor = coin.SoFieldSensor(self.camera_callback, camera)
if isinstance(camera, coin.SoPerspectiveCamera):
self.cameraSensor.attach(camera.focalDistance)
elif isinstance(camera, coin.SoOrthographicCamera):
self.cameraSensor.attach(camera.height)
self.cameraSensorRot = coin.SoFieldSensor(self.camera_callback_rotation, camera)
self.cameraSensorRot.attach(camera.orientation)
factor = self.get_lock_factor()
self.scale = coin.SoScale()
self.scale.scaleFactor.setValue(factor, factor, factor)
self.draw_style = coin.SoDrawStyle()
self.draw_style.lineWidth = 5
# Create transformation (position and orientation)
self.transform = coin.SoTransform()
self.set_lock_position(groundedObj)
self.set_lock_rotation()
# Create the 2D components of the lockpad: a square and two arcs
# Creating a square
squareCoords = [
(-5, -4, 0),
(5, -4, 0),
(5, 4, 0),
(-5, 4, 0),
] # Simple square, adjust size as needed
self.square = coin.SoAnnotation()
squareVertices = coin.SoCoordinate3()
squareVertices.point.setValues(0, 4, squareCoords)
squareFace = coin.SoFaceSet()
squareFace.numVertices.setValue(4)
self.square.addChild(squareVertices)
self.square.addChild(squareFace)
# Creating the arcs (approximated with line segments)
self.arc = self.create_arc(0, 4, 3.5, 0, 180)
self.pick = coin.SoPickStyle()
self.pick.style.setValue(coin.SoPickStyle.SHAPE_ON_TOP)
# Assemble the parts into a scenegraph
self.lockpadSeparator = coin.SoAnnotation()
self.lockpadSeparator.addChild(self.pick)
self.lockpadSeparator.addChild(self.transform)
self.lockpadSeparator.addChild(self.scale)
self.lockpadSeparator.addChild(self.lockpadColor)
self.lockpadSeparator.addChild(self.square)
self.lockpadSeparator.addChild(self.arc)
# Attach the scenegraph to the view provider
vobj.addDisplayMode(self.lockpadSeparator, "Wireframe")
def create_arc(self, centerX, centerY, radius, startAngle, endAngle):
arc = coin.SoAnnotation()
coords = coin.SoCoordinate3()
points = []
for angle in range(startAngle, endAngle + 1): # Increment can be adjusted for smoother arcs
rad = math.radians(angle)
x = centerX + math.cos(rad) * radius
y = centerY + math.sin(rad) * radius
points.append((x, y, 0))
coords.point.setValues(0, len(points), points)
line = coin.SoLineSet()
line.numVertices.setValue(len(points))
arc.addChild(coords)
arc.addChild(self.draw_style)
arc.addChild(line)
return arc
def camera_callback(self, *args):
factor = self.get_lock_factor()
self.scale.scaleFactor.setValue(factor, factor, factor)
def camera_callback_rotation(self, *args):
self.set_lock_rotation()
def set_lock_rotation(self):
camera = self.gui_doc.ActiveView.getCameraNode()
rotation = camera.orientation.getValue()
q = rotation.getValue()
self.transform.rotation.setValue(q[0], q[1], q[2], q[3])
def get_lock_factor(self):
return get_camera_height(self.gui_doc) / 300
def set_lock_position(self, groundedObj):
bBox = groundedObj.ViewObject.getBoundingBox()
if bBox.isValid():
pos = bBox.Center
else:
pos = groundedObj.Placement.Base
self.transform.translation.setValue(pos.x, pos.y, pos.z)
def updateData(self, fp, prop):
"""If a property of the handled feature has changed we have the chance to handle this here"""
# fp is the handled feature, prop is the name of the property that has changed
pass
if prop == "Placement" and fp.ObjectToGround:
self.set_lock_position(fp.ObjectToGround)
def getDisplayModes(self, obj):
"""Return a list of display modes."""
@@ -815,18 +937,20 @@ class ViewProviderGroundedJoint:
# App.Console.PrintMessage("Change property: " + str(prop) + "\n")
pass
def onDelete(self, feature, subelements): # subelements is a tuple of strings
# Remove grounded tag.
if hasattr(feature.Object, "ObjectToGround"):
obj = feature.Object.ObjectToGround
if obj is not None and obj.Label.endswith(" 🔒"):
obj.Label = obj.Label[:-2]
return True # If False is returned the object won't be deleted
def getIcon(self):
return ":/icons/Assembly_ToggleGrounded.svg"
def dumps(self):
"""When saving the document this object gets stored using Python's json module.\
Since we have some un-serializable parts here -- the Coin stuff -- we must define this method\
to return a tuple of all serializable objects or None."""
return None
def loads(self, state):
"""When restoring the serialized object from document we have the chance to set some internals here.\
Since no data were serialized nothing needs to be done here."""
return None
class MakeJointSelGate:
def __init__(self, taskbox, assembly):
@@ -859,7 +983,13 @@ class MakeJointSelGate:
selected_object.isDerivedFrom("Part::Feature")
or selected_object.isDerivedFrom("App::Part")
):
return False
if selected_object.isDerivedFrom("App::Link"):
linked = selected_object.getLinkedObject()
if not (linked.isDerivedFrom("Part::Feature") or linked.isDerivedFrom("App::Part")):
return False
else:
return False
part_containing_selected_object = UtilsAssembly.getContainingPart(
full_element_name, selected_object, self.assembly
@@ -890,8 +1020,10 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
else:
self.activeType = "Assembly"
self.view = Gui.activeDocument().activeView()
self.doc = App.ActiveDocument
self.doc = self.assembly.Document
self.gui_doc = Gui.getDocument(doc)
self.view = self.gui_doc.activeView()
if not self.assembly or not self.view or not self.doc:
return