feat(assembly): fixed reference planes + solver docs
Some checks failed
Build and Test / build (pull_request) Failing after 7m52s

Assembly Origin Planes:
- AssemblyObject::setupObject() relabels origin planes to
  Top (XY), Front (XZ), Right (YZ) on assembly creation
- CommandCreateAssembly.py makes origin planes visible by default
- AssemblyUtils.cpp getObjFromRef() resolves LocalCoordinateSystem
  to child datum elements for joint references to origin planes
- TestAssemblyOriginPlanes.py: 9 integration tests covering
  structure, labels, grounding, reference resolution, solver,
  and save/load round-trip

Solver Documentation:
- docs/src/solver/: 7 new pages covering architecture overview,
  expression DAG, constraints, solving algorithms, diagnostics,
  assembly integration, and writing custom solvers
- docs/src/SUMMARY.md: added Kindred Solver section
This commit is contained in:
forbes
2026-02-21 09:09:16 -06:00
parent 311b3ea4f1
commit acc255972d
15 changed files with 1303 additions and 10 deletions

View File

@@ -22,15 +22,14 @@
# **************************************************************************/
import FreeCAD as App
from PySide.QtCore import QT_TRANSLATE_NOOP
if App.GuiUp:
import FreeCADGui as Gui
from PySide import QtCore, QtGui, QtWidgets
import UtilsAssembly
import Preferences
import UtilsAssembly
translate = App.Qt.translate
@@ -78,14 +77,22 @@ class CommandCreateAssembly:
'assembly = activeAssembly.newObject("Assembly::AssemblyObject", "Assembly")\n'
)
else:
commands = (
'assembly = App.ActiveDocument.addObject("Assembly::AssemblyObject", "Assembly")\n'
)
commands = 'assembly = App.ActiveDocument.addObject("Assembly::AssemblyObject", "Assembly")\n'
commands = commands + 'assembly.Type = "Assembly"\n'
commands = commands + 'assembly.newObject("Assembly::JointGroup", "Joints")'
Gui.doCommand(commands)
# Make origin planes visible by default so they serve as
# reference geometry (like SolidWorks Front/Top/Right planes).
Gui.doCommandGui(
"assembly.Origin.ViewObject.Visibility = True\n"
"for feat in assembly.Origin.OriginFeatures:\n"
" if feat.isDerivedFrom('App::Plane'):\n"
" feat.ViewObject.Visibility = True\n"
)
if not activeAssembly:
Gui.doCommandGui("Gui.ActiveDocument.setEdit(assembly)")
@@ -98,7 +105,9 @@ class ActivateAssemblyTaskPanel:
def __init__(self, assemblies):
self.assemblies = assemblies
self.form = QtWidgets.QWidget()
self.form.setWindowTitle(translate("Assembly_ActivateAssembly", "Activate Assembly"))
self.form.setWindowTitle(
translate("Assembly_ActivateAssembly", "Activate Assembly")
)
layout = QtWidgets.QVBoxLayout(self.form)
label = QtWidgets.QLabel(
@@ -132,9 +141,12 @@ class CommandActivateAssembly:
def GetResources(self):
return {
"Pixmap": "Assembly_ActivateAssembly",
"MenuText": QT_TRANSLATE_NOOP("Assembly_ActivateAssembly", "Activate Assembly"),
"MenuText": QT_TRANSLATE_NOOP(
"Assembly_ActivateAssembly", "Activate Assembly"
),
"ToolTip": QT_TRANSLATE_NOOP(
"Assembly_ActivateAssembly", "Sets an assembly as the active one for editing."
"Assembly_ActivateAssembly",
"Sets an assembly as the active one for editing.",
),
"CmdType": "ForEdit",
}
@@ -156,7 +168,9 @@ class CommandActivateAssembly:
def Activated(self):
doc = App.ActiveDocument
assemblies = [o for o in doc.Objects if o.isDerivedFrom("Assembly::AssemblyObject")]
assemblies = [
o for o in doc.Objects if o.isDerivedFrom("Assembly::AssemblyObject")
]
if len(assemblies) == 1:
# If there's only one, activate it directly without showing a dialog