Assembly: Simulation implementation

This commit is contained in:
Aik-Siong Koh
2024-08-28 10:01:11 -06:00
committed by Max Wilfinger
parent 3e70f7244d
commit 5d143d1f59
28 changed files with 6187 additions and 15 deletions

View File

@@ -33,6 +33,7 @@
#include "BomGroup.h"
#include "JointGroup.h"
#include "ViewGroup.h"
#include "SimulationGroup.h"
namespace Assembly
@@ -68,6 +69,7 @@ PyMOD_INIT_FUNC(AssemblyApp)
Assembly::BomGroup ::init();
Assembly::JointGroup ::init();
Assembly::ViewGroup ::init();
Assembly::SimulationGroup ::init();
PyMOD_Return(mod);
}

View File

@@ -13,7 +13,12 @@
<Author Licence="LGPL" Name="Ondsel" EMail="development@ondsel.com" />
<UserDocu>This class handles document objects in Assembly</UserDocu>
</Documentation>
<Attribute Name="Joints" ReadOnly="true">
<Documentation>
<UserDocu>A list of all joints this assembly link has.</UserDocu>
</Documentation>
<Parameter Name="Joints" Type="List"/>
</Attribute>
<CustomAttributes />
</PythonExport>
</GenerateModel>

View File

@@ -45,3 +45,15 @@ int AssemblyLinkPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}
Py::List AssemblyLinkPy::getJoints() const
{
Py::List ret;
std::vector<App::DocumentObject*> list = getAssemblyLinkPtr()->getJoints();
for (auto It : list) {
ret.append(Py::Object(It->getPyObject(), true));
}
return ret;
}

View File

@@ -29,6 +29,9 @@
#include <unordered_map>
#endif
#include <thread>
#include <chrono>
#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObjectGroup.h>
@@ -67,10 +70,15 @@
#include <OndselSolver/ASMTRackPinionJoint.h>
#include <OndselSolver/ASMTRotationLimit.h>
#include <OndselSolver/ASMTTranslationLimit.h>
#include <OndselSolver/ASMTRotationalMotion.h>
#include <OndselSolver/ASMTTranslationalMotion.h>
#include <OndselSolver/ASMTGeneralMotion.h>
#include <OndselSolver/ASMTScrewJoint.h>
#include <OndselSolver/ASMTSphSphJoint.h>
#include <OndselSolver/ASMTTime.h>
#include <OndselSolver/ASMTConstantGravity.h>
#include <OndselSolver/ExternalSystem.h>
#include <OndselSolver/enum.h>
#include "AssemblyLink.h"
#include "AssemblyObject.h"
@@ -78,6 +86,7 @@
#include "AssemblyUtils.h"
#include "JointGroup.h"
#include "ViewGroup.h"
#include "SimulationGroup.h"
FC_LOG_LEVEL_INIT("Assembly", true, true, true)
@@ -110,7 +119,9 @@ PROPERTY_SOURCE(Assembly::AssemblyObject, App::Part)
AssemblyObject::AssemblyObject()
: mbdAssembly(std::make_shared<ASMTAssembly>())
, bundleFixed(false)
{}
{
mbdAssembly->externalSystem->freecadAssemblyObject = this;
}
AssemblyObject::~AssemblyObject() = default;
@@ -141,6 +152,7 @@ int AssemblyObject::solve(bool enableRedo, bool updateJCS)
mbdAssembly = makeMbdAssembly();
objectPartMap.clear();
motions.clear();
std::vector<App::DocumentObject*> groundedObjs = fixGroundedParts();
if (groundedObjs.empty()) {
@@ -159,7 +171,8 @@ int AssemblyObject::solve(bool enableRedo, bool updateJCS)
}
try {
mbdAssembly->runPreDrag(); // solve() is causing some issues with limits.
// mbdAssembly->runPreDrag(); // solve() is causing some issues with limits.
mbdAssembly->runKINEMATIC();
}
catch (const std::exception& e) {
FC_ERR("Solve failed: " << e.what());
@@ -177,6 +190,79 @@ int AssemblyObject::solve(bool enableRedo, bool updateJCS)
return 0;
}
int AssemblyObject::generateSimulation(App::DocumentObject* sim)
{
mbdAssembly = makeMbdAssembly();
objectPartMap.clear();
motions = getMotionsFromSimulation(sim);
std::vector<App::DocumentObject*> groundedObjs = fixGroundedParts();
if (groundedObjs.empty()) {
// If no part fixed we can't solve.
return -6;
}
std::vector<App::DocumentObject*> joints = getJoints();
removeUnconnectedJoints(joints, groundedObjs);
jointParts(joints);
create_mbdSimulationParameters(sim);
try {
mbdAssembly->runKINEMATIC();
}
catch (...) {
Base::Console().Error("Generation of simulation failed\n");
motions.clear();
return -1;
}
motions.clear();
return 0;
}
std::vector<App::DocumentObject*> AssemblyObject::getMotionsFromSimulation(App::DocumentObject* sim)
{
if (!sim) {
return {};
}
auto* prop = dynamic_cast<App::PropertyLinkList*>(sim->getPropertyByName("Group"));
if (!prop) {
return {};
}
return prop->getValue();
}
int Assembly::AssemblyObject::updateForFrame(size_t index, bool updateJCS)
{
if (!mbdAssembly) {
return -1;
}
auto nfrms = mbdAssembly->numberOfFrames();
if (index >= nfrms) {
return -1;
}
mbdAssembly->updateForFrame(index);
setNewPlacements();
auto jointDocs = getJoints(updateJCS);
redrawJointPlacements(jointDocs);
return 0;
}
size_t Assembly::AssemblyObject::numberOfFrames()
{
return mbdAssembly->numberOfFrames();
}
void AssemblyObject::preDrag(std::vector<App::DocumentObject*> dragParts)
{
bundleFixed = true;
@@ -480,6 +566,7 @@ void AssemblyObject::recomputeJointPlacements(std::vector<App::DocumentObject*>
std::shared_ptr<ASMTAssembly> AssemblyObject::makeMbdAssembly()
{
auto assembly = CREATE<ASMTAssembly>::With();
assembly->externalSystem->freecadAssemblyObject = this;
assembly->setName("OndselAssembly");
ParameterGrp::handle hPgr = App::GetApplication().GetParameterGroupByPath(
@@ -521,6 +608,23 @@ App::DocumentObject* AssemblyObject::getJointOfPartConnectingToGround(App::Docum
return nullptr;
}
template<typename T>
T* AssemblyObject::getGroup()
{
App::Document* doc = getDocument();
std::vector<DocumentObject*> groups = doc->getObjectsOfType(T::getClassTypeId());
if (groups.empty()) {
return nullptr;
}
for (auto group : groups) {
if (hasObject(group)) {
return dynamic_cast<T*>(group);
}
}
return nullptr;
}
JointGroup* AssemblyObject::getJointGroup() const
{
return Assembly::getJointGroup(this);
@@ -964,6 +1068,27 @@ void AssemblyObject::jointParts(std::vector<App::DocumentObject*> joints)
}
}
void Assembly::AssemblyObject::create_mbdSimulationParameters(App::DocumentObject* sim)
{
auto mbdSim = mbdAssembly->simulationParameters;
if (!sim) {
return;
}
auto valueOf = [](DocumentObject* docObj, const char* propName) {
auto* prop = dynamic_cast<App::PropertyFloat*>(docObj->getPropertyByName(propName));
if (!prop) {
return 0.0;
}
return prop->getValue();
};
mbdSim->settstart(valueOf(sim, "aTimeStart"));
mbdSim->settend(valueOf(sim, "bTimeEnd"));
mbdSim->sethout(valueOf(sim, "cTimeStepOutput"));
mbdSim->sethmin(1.0e-9);
mbdSim->sethmax(1.0);
mbdSim->seterrorTol(valueOf(sim, "fGlobalErrorTolerance"));
}
std::shared_ptr<ASMTJoint> AssemblyObject::makeMbdJointOfType(App::DocumentObject* joint,
JointType type)
{
@@ -1351,7 +1476,7 @@ AssemblyObject::makeMbdJoint(App::DocumentObject* joint)
if (maxEnabled) {
auto limit2 = ASMTRotationLimit::With();
limit2->setName(joint->getFullName() + "-LimiRotMax");
limit2->setName(joint->getFullName() + "-LimitRotMax");
limit2->setMarkerI(fullMarkerNameI);
limit2->setMarkerJ(fullMarkerNameJ);
limit2->settype("=<");
@@ -1361,6 +1486,90 @@ AssemblyObject::makeMbdJoint(App::DocumentObject* joint)
}
}
}
std::vector<App::DocumentObject*> done;
// Add motions if needed
for (auto* motion : motions) {
if (std::find(done.begin(), done.end(), motion) != done.end()) {
continue; // don't process twice (can happen in case of cylindrical)
}
auto* pJoint = dynamic_cast<App::PropertyXLinkSub*>(motion->getPropertyByName("Joint"));
if (!pJoint) {
continue;
}
App::DocumentObject* motionJoint = pJoint->getValue();
if (joint != motionJoint) {
continue;
}
auto* pType =
dynamic_cast<App::PropertyEnumeration*>(motion->getPropertyByName("MotionType"));
auto* pFormula = dynamic_cast<App::PropertyString*>(motion->getPropertyByName("Formula"));
if (!pType || !pFormula) {
continue;
}
std::string formula = pFormula->getValue();
if (formula == "") {
continue;
}
std::string motionType = pType->getValueAsString();
// check if there is a second motion as cylindrical can have both,
// in which case the solver needs a general motion.
for (auto* motion2 : motions) {
pJoint = dynamic_cast<App::PropertyXLinkSub*>(motion2->getPropertyByName("Joint"));
if (!pJoint) {
continue;
}
motionJoint = pJoint->getValue();
if (joint != motionJoint || motion2 == motion) {
continue;
}
auto* pType2 =
dynamic_cast<App::PropertyEnumeration*>(motion2->getPropertyByName("MotionType"));
auto* pFormula2 =
dynamic_cast<App::PropertyString*>(motion2->getPropertyByName("Formula"));
if (!pType2 || !pFormula2) {
continue;
}
std::string formula2 = pFormula2->getValue();
if (formula2 == "") {
continue;
}
std::string motionType2 = pType2->getValueAsString();
if (motionType2 == motionType) {
continue; // only if both motions are different. ie one angular and one linear.
}
auto ASMTmotion = CREATE<ASMTGeneralMotion>::With();
ASMTmotion->setName(joint->getFullName() + "-ScrewMotion");
ASMTmotion->setMarkerI(fullMarkerNameI);
ASMTmotion->setMarkerJ(fullMarkerNameJ);
ASMTmotion->rIJI->atiput(2, motionType == "Angular" ? formula2 : formula);
ASMTmotion->angIJJ->atiput(2, motionType == "Angular" ? formula : formula2);
mbdAssembly->addMotion(ASMTmotion);
done.push_back(motion2);
}
if (motionType == "Angular") {
auto ASMTmotion = CREATE<ASMTRotationalMotion>::With();
ASMTmotion->setName(joint->getFullName() + "-AngularMotion");
ASMTmotion->setMarkerI(fullMarkerNameI);
ASMTmotion->setMarkerJ(fullMarkerNameJ);
ASMTmotion->setRotationZ(formula);
mbdAssembly->addMotion(ASMTmotion);
}
else if (motionType == "Linear") {
auto ASMTmotion = CREATE<ASMTTranslationalMotion>::With();
ASMTmotion->setName(joint->getFullName() + "-LinearMotion");
ASMTmotion->setMarkerI(fullMarkerNameI);
ASMTmotion->setMarkerJ(fullMarkerNameJ);
ASMTmotion->setTranslationZ(formula);
mbdAssembly->addMotion(ASMTmotion);
}
}
return {mbdJoint};
}
@@ -1565,7 +1774,7 @@ AssemblyObject::MbDPartData AssemblyObject::getMbDData(App::DocumentObject* part
MbDPartData data = {mbdPart, Base::Placement()};
objectPartMap[part] = data; // Store the association
// Associate other objects conneted with fixed joints
// Associate other objects connected with fixed joints
if (bundleFixed) {
auto addConnectedFixedParts = [&](App::DocumentObject* currentPart, auto& self) -> void {
std::vector<App::DocumentObject*> joints = getJointsOfPart(currentPart);

View File

@@ -31,6 +31,9 @@
#include <App/FeaturePython.h>
#include <App/Part.h>
#include <App/PropertyLinks.h>
#include "SimulationGroup.h"
#include <3rdParty/OndselSolver/OndselSolver/enum.h>
namespace MbD
{
@@ -90,6 +93,9 @@ public:
and redraw the joints Args : enableRedo : This store initial positions to enable undo while
being in an active transaction (joint creation).*/
int solve(bool enableRedo = false, bool updateJCS = true);
int generateSimulation(App::DocumentObject* sim);
int updateForFrame(size_t index, bool updateJCS = true);
size_t numberOfFrames();
void preDrag(std::vector<App::DocumentObject*> dragParts);
void doDragStep();
void postDrag();
@@ -111,6 +117,7 @@ public:
// Ondsel Solver interface
std::shared_ptr<MbD::ASMTAssembly> makeMbdAssembly();
void create_mbdSimulationParameters(App::DocumentObject* sim);
std::shared_ptr<MbD::ASMTPart>
makeMbdPart(std::string& name, Base::Placement plc = Base::Placement(), double mass = 1.0);
std::shared_ptr<MbD::ASMTPart> getMbDPart(App::DocumentObject* obj);
@@ -140,6 +147,9 @@ public:
void jointParts(std::vector<App::DocumentObject*> joints);
JointGroup* getJointGroup() const;
ViewGroup* getExplodedViewGroup() const;
template<typename T>
T* getGroup();
std::vector<App::DocumentObject*>
getJoints(bool updateJCS = true, bool delBadJoints = false, bool subJoints = true);
std::vector<App::DocumentObject*> getGroundedJoints();
@@ -178,12 +188,15 @@ public:
std::vector<AssemblyLink*> getSubAssemblies();
void updateGroundedJointsPlacements();
std::vector<App::DocumentObject*> getMotionsFromSimulation(App::DocumentObject* sim);
private:
std::shared_ptr<MbD::ASMTAssembly> mbdAssembly;
std::unordered_map<App::DocumentObject*, MbDPartData> objectPartMap;
std::vector<std::pair<App::DocumentObject*, double>> objMasses;
std::vector<App::DocumentObject*> draggedParts;
std::vector<App::DocumentObject*> motions;
std::vector<std::pair<App::DocumentObject*, Base::Placement>> previousPositions;

View File

@@ -38,6 +38,52 @@
</UserDocu>
</Documentation>
</Methode>
<Methode Name="generateSimulation">
<Documentation>
<UserDocu>
Generate the simulation.
solve(simulationObject) -> int
Args:
simulationObject: The simulation Object.
Returns:
0 in case of success, otherwise the following codes in this order of
priority:
-6 if no parts are fixed.
-4 if over-constrained,
-3 if conflicting constraints,
-5 if malformed constraints
-1 if solver error,
-2 if redundant constraints.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="updateForFrame">
<Documentation>
<UserDocu>
Update entire assembly to frame number specified.
updateForFrame(index)
Args: index of frame.
Returns: None
</UserDocu>
</Documentation>
</Methode>
<Methode Name="numberOfFrames">
<Documentation>
<UserDocu>
numberOfFrames()
Args: None
Returns: Number of frames
</UserDocu>
</Documentation>
</Methode>
<Methode Name="undoSolve">
<Documentation>
<UserDocu>
@@ -125,7 +171,12 @@
</UserDocu>
</Documentation>
</Methode>
<Attribute Name="Joints" ReadOnly="true">
<Documentation>
<UserDocu>A list of all joints this assembly has.</UserDocu>
</Documentation>
<Parameter Name="Joints" Type="List"/>
</Attribute>
<CustomAttributes />
</PythonExport>
</GenerateModel>

View File

@@ -68,6 +68,18 @@ PyObject* AssemblyObjectPy::solve(PyObject* args)
return Py_BuildValue("i", ret);
}
PyObject* AssemblyObjectPy::generateSimulation(PyObject* args)
{
PyObject* pyobj;
if (!PyArg_ParseTuple(args, "O", &pyobj)) {
return nullptr;
}
auto* obj = static_cast<App::DocumentObjectPy*>(pyobj)->getDocumentObjectPtr();
int ret = this->getAssemblyObjectPtr()->generateSimulation(obj);
return Py_BuildValue("i", ret);
}
PyObject* AssemblyObjectPy::ensureIdentityPlacements(PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
@@ -77,6 +89,31 @@ PyObject* AssemblyObjectPy::ensureIdentityPlacements(PyObject* args)
Py_Return;
}
PyObject* AssemblyObjectPy::updateForFrame(PyObject* args)
{
unsigned long index {};
if (!PyArg_ParseTuple(args, "k", &index)) {
throw Py::RuntimeError("updateForFrame requires an integer index");
}
PY_TRY
{
this->getAssemblyObjectPtr()->updateForFrame(index);
}
PY_CATCH;
Py_Return;
}
PyObject* AssemblyObjectPy::numberOfFrames(PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
size_t ret = this->getAssemblyObjectPtr()->numberOfFrames();
return Py_BuildValue("k", ret);
}
PyObject* AssemblyObjectPy::undoSolve(PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
@@ -151,3 +188,15 @@ PyObject* AssemblyObjectPy::exportAsASMT(PyObject* args)
Py_Return;
}
Py::List AssemblyObjectPy::getJoints() const
{
Py::List ret;
std::vector<App::DocumentObject*> list = getAssemblyObjectPtr()->getJoints();
for (auto It : list) {
ret.append(Py::Object(It->getPyObject(), true));
}
return ret;
}

View File

@@ -28,6 +28,7 @@ generate_from_xml(BomObjectPy)
generate_from_xml(BomGroupPy)
generate_from_xml(JointGroupPy)
generate_from_xml(ViewGroupPy)
generate_from_xml(SimulationGroupPy)
SET(Python_SRCS
AssemblyObjectPy.xml
@@ -42,6 +43,8 @@ SET(Python_SRCS
JointGroupPyImp.cpp
ViewGroupPy.xml
ViewGroupPyImp.cpp
SimulationGroupPy.xml
SimulationGroupPyImp.cpp
)
SOURCE_GROUP("Python" FILES ${Python_SRCS})
@@ -68,6 +71,8 @@ SET(Assembly_SRCS
JointGroup.h
ViewGroup.cpp
ViewGroup.h
SimulationGroup.cpp
SimulationGroup.h
${Module_SRCS}
${Python_SRCS}
)

View File

@@ -0,0 +1,55 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2024 Ondsel <development@ondsel.com> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#endif
#include <App/Application.h>
#include <App/Document.h>
#include <App/FeaturePythonPyImp.h>
#include <App/PropertyPythonObject.h>
#include <Base/Console.h>
#include <Base/Tools.h>
#include "SimulationGroup.h"
#include "SimulationGroupPy.h"
using namespace Assembly;
PROPERTY_SOURCE(Assembly::SimulationGroup, App::DocumentObjectGroup)
SimulationGroup::SimulationGroup()
{}
SimulationGroup::~SimulationGroup() = default;
PyObject* SimulationGroup::getPyObject()
{
if (PythonObject.is(Py::_None())) {
// ref counter is set to 1
PythonObject = Py::Object(new SimulationGroupPy(this), true);
}
return Py::new_reference_to(PythonObject);
}

View File

@@ -0,0 +1,58 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2024 Ondsel <development@ondsel.com> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#ifndef ASSEMBLY_SimulationGroup_H
#define ASSEMBLY_SimulationGroup_H
#include <Mod/Assembly/AssemblyGlobal.h>
#include <App/DocumentObjectGroup.h>
#include <App/PropertyLinks.h>
namespace Assembly
{
class AssemblyExport SimulationGroup: public App::DocumentObjectGroup
{
PROPERTY_HEADER_WITH_OVERRIDE(Assembly::SimulationGroup);
public:
SimulationGroup();
~SimulationGroup() override;
PyObject* getPyObject() override;
/// returns the type name of the ViewProvider
const char* getViewProviderName() const override
{
return "AssemblyGui::ViewProviderSimulationGroup";
}
};
} // namespace Assembly
#endif // ASSEMBLY_SimulationGroup_H

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="DocumentObjectGroupPy"
Name="SimulationGroupPy"
Twin="SimulationGroup"
TwinPointer="SimulationGroup"
Include="Mod/Assembly/App/SimulationGroup.h"
Namespace="Assembly"
FatherInclude="App/DocumentObjectGroupPy.h"
FatherNamespace="App">
<Documentation>
<Author Licence="LGPL" Name="Ondsel" EMail="development@ondsel.com" />
<UserDocu>This class is a group subclass for joints.</UserDocu>
</Documentation>
<CustomAttributes />
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,47 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2024 Ondsel <development@ondsel.com> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#include "PreCompiled.h"
// inclusion of the generated files (generated out of SimulationGroup.xml)
#include "SimulationGroupPy.h"
#include "SimulationGroupPy.cpp"
using namespace Assembly;
// returns a string which represents the object e.g. when printed in python
std::string SimulationGroupPy::representation() const
{
return {"<Simulation Group>"};
}
PyObject* SimulationGroupPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int SimulationGroupPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}

View File

@@ -13,6 +13,7 @@ set(Assembly_Scripts
CommandSolveAssembly.py
CommandCreateJoint.py
CommandCreateView.py
CommandCreateSimulation.py
CommandExportASMT.py
TestAssemblyWorkbench.py
JointObject.py

File diff suppressed because it is too large Load Diff

View File

@@ -33,6 +33,7 @@
#include "ViewProviderBomGroup.h"
#include "ViewProviderJointGroup.h"
#include "ViewProviderViewGroup.h"
#include "ViewProviderSimulationGroup.h"
namespace AssemblyGui
@@ -66,6 +67,7 @@ PyMOD_INIT_FUNC(AssemblyGui)
AssemblyGui::ViewProviderBomGroup::init();
AssemblyGui::ViewProviderJointGroup::init();
AssemblyGui::ViewProviderViewGroup::init();
AssemblyGui::ViewProviderSimulationGroup::init();
PyMOD_Return(mod);
}

View File

@@ -28,6 +28,15 @@ SOURCE_GROUP("Resources" FILES ${AssemblyResource_SRCS})
generate_from_xml(ViewProviderAssemblyPy)
set(AssemblyGui_UIC_SRCS
TaskAssemblyCreateBom.ui
TaskAssemblyCreateJoint.ui
TaskAssemblyCreateSimulation.ui
TaskAssemblyCreateView.ui
TaskAssemblyInsertLink.ui
Assembly.ui
)
SET(Python_SRCS
ViewProviderAssemblyPy.xml
ViewProviderAssemblyPyImp.cpp
@@ -51,6 +60,8 @@ SET(AssemblyGui_SRCS_Module
ViewProviderJointGroup.h
ViewProviderViewGroup.cpp
ViewProviderViewGroup.h
ViewProviderSimulationGroup.cpp
ViewProviderSimulationGroup.h
${Assembly_QRC_SRCS}
)

View File

@@ -26,10 +26,13 @@
<file>icons/Assembly_JointGroup.svg</file>
<file>icons/Assembly_ExplodedView.svg</file>
<file>icons/Assembly_ExplodedViewGroup.svg</file>
<file>icons/Assembly_CreateSimulation.svg</file>
<file>icons/Assembly_SimulationGroup.svg</file>
<file>panels/TaskAssemblyCreateBom.ui</file>
<file>panels/TaskAssemblyCreateJoint.ui</file>
<file>panels/TaskAssemblyInsertLink.ui</file>
<file>panels/TaskAssemblyCreateView.ui</file>
<file>panels/TaskAssemblyCreateSimulation.ui</file>
<file>preferences/Assembly.ui</file>
<file>icons/Assembly_CreateJointDistance.svg</file>
<file>icons/AssemblyWorkbench.svg</file>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 80 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -0,0 +1,339 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TaskAssemblyCreateSimulation</class>
<widget class="QDialog" name="TaskAssemblyCreateSimulation">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>602</height>
</rect>
</property>
<property name="windowTitle">
<string>Create Simulation</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox_motions">
<property name="title">
<string>Motions</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QListWidget" name="motionList">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>75</height>
</size>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="motion_buttonsLayout">
<item>
<widget class="QToolButton" name="AddButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Add a prescribed motion</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="Icons/resource.qrc">
<normaloff>:/icons/list-add.svg</normaloff>:/icons/list-add.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="RemoveButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Delete selected motions</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="Icons/resource.qrc">
<normaloff>:/icons/edit-delete.svg</normaloff>:/icons/edit-delete.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_settings">
<property name="title">
<string>Simulation settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="TimeStartLabel">
<property name="text">
<string>Start</string>
</property>
<property name="toolTip">
<string>Start time of the simulation</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Gui::PrefQuantitySpinBox" name="TimeStartSpinBox">
<property name="toolTip">
<string>Start time of the simulation</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="TimeEndLabel">
<property name="text">
<string>End</string>
</property>
<property name="toolTip">
<string>End time of the simulation</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::PrefQuantitySpinBox" name="TimeEndSpinBox">
<property name="toolTip">
<string>End time of the simulation</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="TimeStepOutputLabel">
<property name="text">
<string>Step</string>
</property>
<property name="toolTip">
<string>Time Step</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::PrefQuantitySpinBox" name="TimeStepOutputSpinBox">
<property name="toolTip">
<string>Time Step</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="GlobalErrorToleranceLabel">
<property name="text">
<string>Tolerance</string>
</property>
<property name="toolTip">
<string>Global Error Tolerance</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="Gui::PrefQuantitySpinBox" name="GlobalErrorToleranceSpinBox">
<property name="toolTip">
<string>Global Error Tolerance</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="RunKinematicsButton">
<property name="text">
<string>Generate</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_player">
<property name="title">
<string>Animation player</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="FrameLabel">
<property name="text">
<string>Frame</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSlider" name="frameSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="FrameTimeLabel">
<property name="text">
<string>0.00 s</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="fps_layout">
<item>
<widget class="QLabel" name="FramesPerSecondLabel">
<property name="text">
<string>Frames Per Second</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="FramesPerSecondSpinBox"/>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="controls_layout">
<item>
<widget class="QToolButton" name="StepBackwardButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Step backward</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="Icons/resource.qrc">
<normaloff>:/icons/media-playback-step-back.svg</normaloff>:/icons/media-playback-step-back.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="PlayBackwardButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Play backward</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="Icons/resource.qrc">
<normaloff>:/icons/media-playback-start-back.svg</normaloff>:/icons/media-playback-start-back.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="StopButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Stop</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="Icons/resource.qrc">
<normaloff>:/icons/media-playback-stop.svg</normaloff>:/icons/media-playback-stop.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="PlayForwardButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Play forward</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="Icons/resource.qrc">
<normaloff>:/icons/media-playback-start.svg</normaloff>:/icons/media-playback-start.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="StepForwardButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Step forward</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="Icons/resource.qrc">
<normaloff>:/icons/media-playback-step.svg</normaloff>:/icons/media-playback-step.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::QuantitySpinBox</class>
<extends>QWidget</extends>
<header>Gui/QuantitySpinBox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,49 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2024 Ondsel <development@ondsel.com> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#endif
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <Gui/Application.h>
#include <Gui/BitmapFactory.h>
#include "ViewProviderSimulationGroup.h"
using namespace AssemblyGui;
PROPERTY_SOURCE(AssemblyGui::ViewProviderSimulationGroup, Gui::ViewProviderDocumentObjectGroup)
ViewProviderSimulationGroup::ViewProviderSimulationGroup()
{}
ViewProviderSimulationGroup::~ViewProviderSimulationGroup() = default;
QIcon ViewProviderSimulationGroup::getIcon() const
{
return Gui::BitmapFactory().pixmap("Assembly_SimulationGroup.svg");
}

View File

@@ -0,0 +1,63 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2024 Ondsel <development@ondsel.com> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#ifndef ASSEMBLYGUI_VIEWPROVIDER_ViewProviderSimulationGroup_H
#define ASSEMBLYGUI_VIEWPROVIDER_ViewProviderSimulationGroup_H
#include <Mod/Assembly/AssemblyGlobal.h>
#include <Gui/ViewProviderDocumentObjectGroup.h>
namespace AssemblyGui
{
class AssemblyGuiExport ViewProviderSimulationGroup: public Gui::ViewProviderDocumentObjectGroup
{
PROPERTY_HEADER_WITH_OVERRIDE(AssemblyGui::ViewProviderSimulationGroup);
public:
ViewProviderSimulationGroup();
~ViewProviderSimulationGroup() override;
/// deliver the icon shown in the tree view. Override from ViewProvider.h
QIcon getIcon() const override;
// Prevent dragging of the joints and dropping things inside the joint group.
bool canDragObjects() const override
{
return false;
};
bool canDropObjects() const override
{
return false;
};
bool canDragAndDropObject(App::DocumentObject*) const override
{
return false;
};
};
} // namespace AssemblyGui
#endif // ASSEMBLYGUI_VIEWPROVIDER_ViewProviderSimulationGroup_H

View File

@@ -63,7 +63,7 @@ class AssemblyWorkbench(Workbench):
# load the builtin modules
from PySide import QtCore, QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
import CommandCreateAssembly, CommandInsertLink, CommandInsertNewPart, CommandCreateJoint, CommandSolveAssembly, CommandExportASMT, CommandCreateView, CommandCreateBom
import CommandCreateAssembly, CommandInsertLink, CommandInsertNewPart, CommandCreateJoint, CommandSolveAssembly, CommandExportASMT, CommandCreateView, CommandCreateSimulation, CommandCreateBom
import Preferences
FreeCADGui.addLanguagePath(":/translations")
@@ -79,6 +79,7 @@ class AssemblyWorkbench(Workbench):
"Assembly_Insert",
"Assembly_SolveAssembly",
"Assembly_CreateView",
"Assembly_CreateSimulation",
"Assembly_CreateBom",
]

View File

@@ -80,18 +80,44 @@ def isDocTemporary(doc):
def assembly_has_at_least_n_parts(n):
assembly = activeAssembly()
i = 0
if not assembly:
assembly = activePart()
if not assembly:
return False
for obj in assembly.OutList:
# note : groundedJoints comes in the outlist so we filter those out.
if hasattr(obj, "Placement") and not hasattr(obj, "ObjectToGround"):
i = i + 1
if i == n:
return True
return False
i = number_of_components_in(assembly)
return i >= n
def number_of_components_in(assembly):
if not assembly:
return 0
i = 0
for obj in assembly.Group:
if isLinkGroup(obj):
i = i + obj.ElementCount
continue
if obj.isDerivedFrom("Assembly::AssemblyObject") or obj.isDerivedFrom(
"Assembly::AssemblyLink"
):
i = i + number_of_components_in(obj)
continue
if obj.isDerivedFrom("App::Link"):
obj = obj.getLinkedObject()
if not obj.isDerivedFrom("App::GeoFeature"):
continue
# if obj.isDerivedFrom("App::DatumElement") or obj.isDerivedFrom("App::LocalCoordinateSystem"):
if obj.isDerivedFrom("App::Origin"):
# after https://github.com/FreeCAD/FreeCAD/pull/16675 merges,
# replace the App::Origin test by the one above
continue
i = i + 1
return i
def isLink(obj):
@@ -546,6 +572,20 @@ def color_from_unsigned(c):
]
def getJointsOfType(asm, jointTypes):
if not (
asm.isDerivedFrom("Assembly::AssemblyObject") or asm.isDerivedFrom("Assembly::AssemblyLink")
):
return []
joints = []
allJoints = asm.Joints
for joint in allJoints:
if joint.JointType in jointTypes:
joints.append(joint)
return joints
def getBomGroup(assembly):
bom_group = None
@@ -588,6 +628,20 @@ def getViewGroup(assembly):
return view_group
def getSimulationGroup(assembly):
sim_group = None
for obj in assembly.OutList:
if obj.TypeId == "Assembly::SimulationGroup":
sim_group = obj
break
if not sim_group:
sim_group = assembly.newObject("Assembly::SimulationGroup", "Simulations")
return sim_group
def isAssemblyGrounded():
assembly = activeAssembly()
if not assembly: