Forum Replies Created
-
AuthorPosts
-
jnbrunetModerator
Hi Erwan,
It seems the merge function is only used by
MergeVectors<VecT>::update()
(SofaGeneralEngine/MergeVectors.inl). In this case, theData<T>& out
variable you see will be of typeData<VecT>
. So, when expending the templates, theout.push_back
is in fact callingVecT.push_back()
since the WriteOnlyAccessor is creating a bridge betweenData<T>.push_back()
andT.push_back()
.I’m pretty sure that if you call this merge function with a
T
template that does not define apush_back()
function somewhere, the compilation will fail.For your second question,
Data<T>
can be of any type T, be it a vector or not.vectoData<T>
is of typevector<Data<T>>
which is always a vector. BUT, in the case of this merge function, theData<T>
must be of type vector since the template must have the push_back function. So here,Data<T>
is a vector andvectorData<T>
is a vector of vector.This merge function is what is often call a flattening function :
[[0,1],[2,3]] will become [0,1,2,3].
Hope that helps.
jnbrunetModeratorJust to add a little bit to Vincent’s answer.
The
BaseClass::decodeTemplateName
will use the abi to demangle the type name of your class and then parse what is between the first ‘<‘ and the last ‘>’. The type name of your class’s template is obtained through thestd::string::Name()
function which you will need to define.If you want to assign
std::string
instead of the demangled type name of the later, you could also override the methodMyTemplatedComponent::templateName()
to return the string std::string.Something like this :
template<class T> class MyTemplatedComponent { ... template<> static std::string templateName(const MyTemplatedComponent<std::string>* ptr= NULL) { return "std::string"; } ... }
jnbrunetModeratorHey Bassam!
I just want to add that, as Hugo said, I think the best way would be to use two
MechanicalObject
, one with the reduced space and one with the full DOFs, instead of subclassing directly theMechanicalObject
class. In this case, you would only need to create a forcefield component in your code, and use mapping between the two default Sofa’s MechanicalObject components. I’m really not sure how the model reduction works, but I guess you could have a look at theSubsetMapping
of Sofa, either for using it directly if it does the job right, or take ideas from it to create your own mapping component. You can specify a radius attribute of SubsetMapping to find the closest indice of the full model (mapping’s input) from a given reduced indice (mapping’s output). You would then have something like this:<Node name="simulation"> <Node name="full_model"> <MeshObjLoader name="full_meshLoader" filename="full_dof.obj" /> <MechanicalObject name="full_dof" src="@full_meshLoader"/> <Node name="reduced_model"> <MeshObjLoader name="reduced_meshLoader" filename="reduced_dof.obj" /> <MechanicalObject name="reduced_dof" src="@reduced_meshLoader" /> <YourForceField /> <SubsetMapping input="@../full_dof" output="@reduced_dof" radius="1" /> </Node> </Node> </Node>
I hope that helps.
jnbrunetModeratorHello Erwan,
This is a good question and I think it would be a good idea to document it a little bit more since it is not that trivial to see it in the sofa’s code.
I’ve just had a quick look up and here is what I think is going on.
-
First, in one of the cpp file of your plugin’s code, you should have registered your object by calling
sofa::core::RegisterObject::add
which will create and register for you asofa::core::ObjectFactory::Creator
with the right template. Exemple:int MyTemplatedComponentClass = sofa::core::RegisterObject("My templated component description") .add< MyTemplatedComponent<sofa::defaulttype::Vec3dTypes> >(true) ;
The add function should be called for every templates your component needs to make available to the simulation.
Note that the add function can take a boolean argument to make the given template the default template in case you did not specify it in the scene’s xml node.
To get to right template name, the
sofa::core::RegisterObject::add
function will call your object’sMyTemplatedComponent::templateName
function which, if it is not overridden, will callsofa::core::objectmodel::Base::templateName
function (that your object’s class should inherit). This will in turn simply callBaseClass::decodeTemplateName(typeid(MyTemplatedComponent<Vec3dTypes>))
to get the templates. -
At this point, there is an object creator registered for your object with the available templates. Next, the
sofa::simulation:Simulation
loads the scene and parse the xml of each node withsofa::simulation::xml::ObjectElement
.The
sofa::core::ObjectFactory
will be called to create the object by finding the rightObjectCreator
that you usually will have declared in the first step. To do so, it will try to get thetemplate
xml attribute. If it is not found, it will get the default template of your object (passed with the boolean argument of the add function in the first step). You can look at sofa/core/ObjectFactory.cpp:115 (ObjectFactory::createObject
function) for more information about this. -
Finally, once the right
sofa::core::ObjectCreator
(which should be of typesofa::core::ObjectCreator<MyTemplatedComponent<sofa::defaulttype::Vec3dTypes>>
is created, the creator will call your component’s functionMyTemplatedComponent<sofa::defaulttype::Vec3dTypes>::create
, which again should simply inherits thesofa::core::objectmodel::Base::create
function to complete the component creation.
Let me know if this is still not very clear, I’ll try to detail it a little bit more.
Edit: formating
jnbrunetModeratorHey Noura,
Is there a reason why you call the
MeshSTLLoader::load
function on each call toupdateOnBeginStep
?Looking at the
MeshSTLLoader::load
function, we can see that the vertices, normals and triangles are appended at the end of the internal data container. That means that each time you call theload
function, it re-parse your entire mesh file from your disk and append the same data to the end of your already loaded data vector from the last step! This is why each time you call it, your container’s size become larger (by duplicating the data of your mesh file at the end).Parsing the entire mesh file from your disk is extremely slow, if you need to take into account changes in the mesh, maybe you could try to find a way that doesn’t involve disk I/O operations.
As for the
TDestType
template of the Link class, it really should inherits the Base class. TheMeshSTLLoader
andOglModel
inheritance looks like this:MeshSTLLoader -> MeshLoader -> BaseLoader -> BaseObject -> Base OglModel -> VisualModelImpl-> VisualModel -> BaseObject -> Base
They actually need to inherit BaseObject in order to represent a scene object (node).
For your second question concerning the translation, could you try without passing by a Data object? Something like this:
Vector3 myVec = ldr->getTranslation();
For your third question, I’m not quite sure what you want to do. Normally you would fill a mechanical state with your mesh positions, and do a rigid mapping between the mechanical state (which contains the updated positions at each time step) and your visual state (OglModel).
Keep us updated on your progress!
Jean-NicojnbrunetModeratorHey Noura,
I believe that in order to be used by a linked relationship, the object linked must inherit the
Base
class, which isn’t the case with asofa::helper::io::Mesh
.Also, note that in your scene node, you do not have any
sofa::helper::io::Mesh
. You do have asofa::component::loader::MeshSTLLoader
which is named “loader” and asofa::component::topology::MeshTopology
represented by the alias “Mesh” (the Mesh node).The “mesh_link” attribute of your component should be linked to the
sofa::component::topology::MeshTopology
, a.k.a “Mesh” node.Now, for the SingleLinked vs Data question, I could not explicitly tell you all differences between those 2 types as I really doesn’t know them very much, but I guess Link types can resolved automatically a “Node” by a given path (“@../the/node”), for which you would need to do manually with a Data type. I may be wrong, but I think Link type can also resolve a Node’s attribute by a given path (“@../the/node.attribute”) There is also some sort of Master – Slave relationship with linked types, so maybe there is a critical path of the order of initialisation between the scene nodes. And there may also be some sort of sync between those linked attributes.
But as I said, those are all guesses. Maybe someone with more knowledge of the Link types could shed some light on what exactly a linked attribute or node can offer us.
Hope that helped.
Jean-NicojnbrunetModeratorI would advise not to use the
handletopologychange
function since it is marked as deprecated and could be removed eventually.jnbrunetModeratorHey Brian,
Developer edition should not be a problem, the warning is only there to remind you that you cannot distribute your software in a commercial manner.
When you launch the Geomagic diagnostic tool, you can see the device move and correctly calibrate it?
Also, in the scene file, can you try with a completely random device name, just to make sure that the plugin is correctly outputting an error message when it cannot find a valid device.
jnbrunetModeratorTry with the scene file “applications/plugins/Geomagic/scenes/Geomagic-FEMLiver.scn”.
Make sure the
deviceName
match the name of your device (as shown in the Geomagic diagnostic tool).<GeomagicDriver name="GeomagicDevice" deviceName="Default Device" scale="1" drawDeviceFrame="1" positionBase="0 0 0" orientationBase="0 0.707 0 -0.707" />
Paste here the output of SOFA with this scene (please use the “code” markup instead of a picture of your terminal).
jnbrunetModeratorHi boland3,
Did you make sure you set the good device name in your scene file? It should match the one shown in the Geomagic diagnostic tool.
Since you can see that the plugin is fully loaded in SOFA, the problem is probably elsewhere. Can you paste here the output of SOFA?
jnbrunetModerator– And i can also increase to more vertices,right?
Yes, absolutely.– Now I want to extract the whole edge of the liver as the constraint
You can use the BoxROI component :<BoxROI name="fixed_indices" box="2 4.7 -2 -4 5 2" drawBoxes="1"/> <FixedConstraint name="FixedConstraint" indices="@fixed_indices.indices" />
In SOFA, check the Behavior Model view option to draw the box. The BoxROI parameter ‘box’ is set like this:
box="x1 y1 z1 x2 y2 z2"
where (x1,y1,z1) and (x2 y2 z2) are two opposite vertices of the region of interest box.jnbrunetModeratorHi Lujain,
I’m not quite sure what you are asking here. Since you are in the “Using SOFA” subforum, I’m guessing that you want to get the indices of the FixedConstraint component linked in another component directly in the scene file? Something like this:
<FixedConstraint name=”FixedConstraint” indices=”3 39 64″ /> <PointsFromIndices name="fixed_constraint_positions" indices="@FixedConstraint.indices"/>
This will get you the positions of the constraint vertices.
If you want to get them with c++ code, you can do something like this:
sofa::component::projectiveconstraintset::FixedConstraint<DataTypes>* fc; this->getContext()->get(fc); if (fc) std::cout<<"Indices from '"<<fc->getPathName()<<"' are '"<<fc->f_indices<<"'"<<std::endl;
Output:
Indices from '/cube/FixedConstraint' are '3 39 64'
jnbrunetModeratorThe proper way to do it would probably be by creating your own topological engine that would create an unique index for each tetra and receive topological change events from the mesh topology.
Here is an example of a simple component that create an unique id for each tetra and then print out the unique id of a tetra when it is created/deleted:
#include <sofa/core/objectmodel/BaseObject.h> #include <sofa/core/topology/BaseMeshTopology.h> #include <SofaBaseTopology/TopologyData.h> #include <SofaBaseTopology/TopologyData.inl> #include <SofaBaseTopology/TopologyDataHandler.h> #include <sofa/core/ObjectFactory.h> class ElementListener : public sofa::core::objectmodel::BaseObject { public: SOFA_CLASS(ElementListener, sofa::core::objectmodel::BaseObject); typedef typename sofa::defaulttype::Vec3dTypes DataTypes; typedef typename DataTypes::VecCoord VecCoord; class TetrahedronHandler : public virtual sofa::component::topology::TopologyDataHandler<sofa::core::topology::BaseMeshTopology::Tetrahedron, sofa::helper::vector<unsigned int> > { typedef typename sofa::component::topology::TopologyDataHandler<sofa::core::topology::BaseMeshTopology::Tetrahedron, sofa::helper::vector<unsigned int> > Inherit; public : TetrahedronHandler(sofa::component::topology::TetrahedronData<sofa::helper::vector<unsigned int> >* data) : Inherit(data) {} void applyCreateFunction(unsigned int idx, unsigned int &unique_id, const sofa::core::topology::BaseMeshTopology::Tetrahedron &tetra, const sofa::helper::vector<unsigned int> &ancestors, const sofa::helper::vector<double> &coefs) { static unsigned int tetra_count = 0; unique_id = tetra_count++; std::cout<<"Tetra #"<<unique_id<<" created"<<std::endl; } virtual void applyDestroyFunction(unsigned int idx, unsigned int& unique_id) { std::cout<<"Tetra #"<<unique_id<<" deleted"<<std::endl; } }; virtual void init() { _topology = this->getContext()->getMeshTopology(); if (_topology->getNbTetrahedra()==0) { serr << "No tetrahedral set topology found."<<sendl; return; } reinit(); } virtual void reinit() { sofa::helper::vector<unsigned int>& tetrahedronInf = *(tetrahedronInfo.beginEdit()); tetrahedronInf.resize(_topology->getNbTetrahedra()); for (int i=0; i<_topology->getNbTetrahedra(); ++i) { tetrahedronHandler->applyCreateFunction(i, tetrahedronInf[i], _topology->getTetrahedron(i), (const std::vector< unsigned int > )0, (const std::vector< double >)0); } tetrahedronInfo.createTopologicalEngine(_topology,tetrahedronHandler); tetrahedronInfo.registerTopologicalData(); tetrahedronInfo.endEdit(); } protected: ElementListener() : tetrahedronInfo(initData(&tetrahedronInfo, "tetrahedronInfo", "Internal tetrahedron data")) { tetrahedronHandler = new TetrahedronHandler(&tetrahedronInfo); } private: TetrahedronHandler* tetrahedronHandler; sofa::component::topology::TetrahedronData<sofa::helper::vector<unsigned int> > tetrahedronInfo; sofa::core::topology::BaseMeshTopology* _topology; }; int ElementListenerClass = sofa::core::RegisterObject("Simple element listener") .add<ElementListener>(); SOFA_DECL_CLASS(ElementListener)
If you add this component to your scene, you should receive a console message whenever a tetrahedron get created or deleted.
jnbrunetModeratorI will make a long shot guess here, but since you are using a dynamic simulation scheme (time integration), by only setting the position of your new vertex, you could be experiencing the consequence of a huge jump in velocity.
I can see two ways of testing this:
1. Manually set the velocity of your new vertex with the value of a nearby point.
2. Use the ancestors and coef parameters of the addPoints function. You choose a set of nearby points of your new vertex as the ancestors and choose some coefficients for each one. I think there is also a way to simply give a tetrahedron as the ancestor and the nearby points will be the element’s vertices and the coefs will be set automatically with the barycentric coordinates. Then, the position and the velocity of your new vertex should be initialized automatically (I think!).Like I said, this is a wild guess of mine, I may be wrong. But I think it’s worth a try.
Keep us updated.
jnbrunetModeratorThere is a tetra collision model implemented in Sofa but it seems it can only collide with ray and point models:
modules/SofaMiscCollision/TetrahedronDiscreteIntersection.cpp
TetrahedronDiscreteIntersection::TetrahedronDiscreteIntersection(DiscreteIntersection* object) : intersection(object) { intersection->intersectors.add<TetrahedronModel, PointModel, TetrahedronDiscreteIntersection> (this); intersection->intersectors.add<RayModel, TetrahedronModel, TetrahedronDiscreteIntersection> (this); }
You can look at the following files for more information:
modules/SofaMiscCollision/TetrahedronDiscreteIntersection.cpp
modules/SofaMiscCollision/TetrahedronModel.cppThere also is BarycentricDistanceLMConstraintContact, BarycentricPenalityContact and FrictionContact added for the tetra to tetra collision response, but I don’t know if you can get them to work without a proper intersector between two tetrahedra. Maybe someone with more knowledge about collisions in Sofa could clear this up for us.
jnbrunetModeratorHi jjcasmar,
To me, all those link errors related to std::string& arguments make me think of an c++ ABI version mismatch (see here for more details).
Can you try with a more recent compiler? (GCC 5.3+ or LLVM/clang 3.7+)
If this doesn’t work, you could play with the “_GLIBCXX_USE_CXX11_ABI” macro by setting it to 1 or 0.
With CMake, you can do this with
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1)
in the related Sofa modules that failed to link (in your case, SofaHelper).You should also disable the SOFA_BUILD_TESTS cmake option if you don’t plan to use it.
Jean-Nicolas
jnbrunetModeratorYes, you understood perfectly.
I only had in my head the case where the boundary’s vertices of the two meshes are to be merged and are aligned, which is an easy case. But you are right, handling other cases (for example, when the element of the two meshes overlaps, or like you said, with different boundaries size) would demand more work.
At that point, I will probably use an external tool to build the surface and to control the meshing of the volume of my different scenarios. The idea was just to stay from A to Z in SOFA and to build rapidly (and intuitively) different scenarios in a couple of lines like you would do in some software like COMSOL. I did however underestimate the task 😉
Well I was just curious, thanks for your inputs!
jnbrunetModeratorHi Alex!
I’m actually using regular grids as building blocks of my final mesh. It is a lazy way of building my mesh since you can easily, with regular grid, set the number of DOFs.
Once I’m satisfied with the resulting “building blocks” (regular grids) ‘s topology, I’m using MergeMeshes to merge them into one single mesh on which the simulation will run.
I think it would be a cool feature to add a little boolean on the MergeMeshes component to automatically merge duplicated vertices. Then I can build quickly some scenario for my scene in a couple of lines (regular grids until I’m satisfied with the meshing, and then the MergeMeshes into a complete mesh).
Hope that makes sense.
17 January 2017 at 20:48 in reply to: [SOLVED] Error with CImg: plugin with dependency to skeleton.h does not compile #8398jnbrunetModeratorHi Rosalie!
For the Python problem:
Issue: https://github.com/sofa-framework/sofa/issues/135
PR : https://github.com/sofa-framework/sofa/pull/137For the image’s headers installation path
Issue: https://github.com/sofa-framework/sofa/issues/136
PR : https://github.com/sofa-framework/sofa/pull/138Jean-Nico
16 January 2017 at 18:56 in reply to: [SOLVED] Error while compiling SOFA in Mac OS Yosemite:"GL Shader support requires GLEW." #8396jnbrunetModeratorHi Puren,
Just a quick question, did you re-run the cmake command of SOFA after installing glew? (ie. in your SOFA’s build directory, run cmake and then make)
If so, can you please reply back with the result of the following commands (assuming you are on a osx system):
$ locate libGLEW.dylib $ locate glew.h $ cmake --version
16 January 2017 at 14:24 in reply to: [SOLVED] Error with CImg: plugin with dependency to skeleton.h does not compile #8394jnbrunetModeratorHi Rosalie!
My guess would be at the difference between your CMake version and the plugin’s owner.
It seems that in your case, the install command of the image/extlibs/CImg/CMakeLists.txt does not preserve the file’s directory hierarchy.
Can you try changing image/extlibs/CImg/CMakeLists.txt :
install(FILES CImg.h SOFACImg.h plugins/add_fileformat.h plugins/chlpca.h plugins/draw_gradient.h plugins/ipl_alt.h plugins/jpeg_buffer.h plugins/matlab.h plugins/patchmatch.h plugins/tiff_stream.h plugins/vrml.h plugins/bayer.h plugins/cvMat.h plugins/inpaint.h plugins/ipl.h plugins/loop_macros.h plugins/nlmeans.h plugins/skeleton.h plugins/tinymatwriter.h plugins/vtk.h DESTINATION include/CImg COMPONENT CImg_headers)
To:
install(FILES CImg.h SOFACImg.h DESTINATION include/CImg COMPONENT CImg_headers) install(DIRECTORY plugins DESTINATION include/CImg COMPONENT CImg_headers)
And try again the compilation and the install?
Edit: Actually, you could only run the install command in your sofa’s build directory after the modification, no need to relauch the compilation.
jnbrunetModeratorAwesome!
jnbrunetModeratorHey Yannie,
I had an error close to this last year. Look at the first post on this thread:
And try to look at the patch I submitted for the functions p1Free, p2Free and p3Free.
24 November 2016 at 22:48 in reply to: [SOLVED] The problem of writing scene in C++ by loading XML file #7985jnbrunetModeratorHi Coco,
I will need more details in order to help you further. I just tried your code and it worked fine on my side. The SofaGui start and I got the liver scene loading well and animating correctly.
Can you paste here the output of Sofa? Maybe you could run your application in debug and try to find where it crashes?
Also, I’m not quite sure what you try to do here since your code does exactly the same as the executable runSofa.
Jean-Nico
24 November 2016 at 14:26 in reply to: [SOLVED] The problem of writing scene in C++ by loading XML file #7982jnbrunetModeratorHey Coco,
The fact that you got the output “Failed to open examples/Demos/liver.scn” tells me that your application may have not found the file “examples/Demos/liver.scn”, which is located inside your sofa’s source directory.
If I’m right, you have two solutions for that :
1. Use the full path to the scene file: change “examples/Demos/liver.scn” for “/COMPLETE_PATH_TO_SOFA/examples/Demos/liver.scn”
2. Use the SOFA_ROOT environment variable when starting your application as I said in my previous post
SOFA_ROOT=/home/toto/sofa ./SuperSimulation
There is no compilation involved in #2 solution.
Hope that worked
Jean-Nico -
First, in one of the cpp file of your plugin’s code, you should have registered your object by calling
-
AuthorPosts