Skip to content

[Controller] Add MotionReplayController component to load a motion from csv file and apply it to a MechanicalObject#41

Open
rmolazem wants to merge 23 commits intoInfinyTech3D:mainfrom
rmolazem:add_motion_replay
Open

[Controller] Add MotionReplayController component to load a motion from csv file and apply it to a MechanicalObject#41
rmolazem wants to merge 23 commits intoInfinyTech3D:mainfrom
rmolazem:add_motion_replay

Conversation

@rmolazem
Copy link

This controller inherits from MechanicalStateController, allowing easy access to the mechanical object’s states after loading all frames at initialization from a CSV file.

The class introduces two new functions:

loadMotion() – Loads all frames from the CSV and stores them in a vector.

handleEvent() – Updates the mechanical object’s positions (grids) according to the loaded frames at each animation step.

However, while compiling SOFA (which includes the infiny toolkit), there is a linking issue: It seems it

cannot inherit from MechanicalStateController and the plugin does not compile. I might have missed some steps to properly register this component inside the InfinyToolkit.

infinyToolkit_linking_Error

P.S. I have updated SOFA from the master branch.


@rmolazem rmolazem closed this Feb 11, 2026
@epernod
Copy link
Contributor

epernod commented Feb 11, 2026

I guess the closing is a wrong manipulation?

@rmolazem
Copy link
Author

Since the PR was open, and I made some modifications, including changing the base class. I thought it might be cleaner to open a clean PR. Except if you want to follow the history of the changes.
I am going to test the component today with an example scene, then push the final version. I can either re-open this or make a new PR, as you wish.

@epernod
Copy link
Contributor

epernod commented Feb 12, 2026

You can keep the same PR as in any case we only review the change in files which is the final diff between your branch and master: https://github.com/InfinyTech3D/InfinyToolkit/pull/41/changes
And at the end we squash and merge the PR, so after the merge only 1 commit will be visible in the history even if the PR was done with 10 commits

@rmolazem rmolazem reopened this Feb 12, 2026
@rmolazem
Copy link
Author

I conclude that using the SOFA controller slightly increases the FPS.

I compared Scenario 2 for Asan#2 at three stages:

  • When the Judkins is in the middle of the vessel toward the aorta

  • When the guidewire is close to the aorta

  • When the guidewire is inside the right coronary arteries

--> The approximate FPS values are:

Python controller: 66, 37, 22

SOFA controller: 70, 39, 25

@epernod epernod changed the title Add motion replay controller component to the INFINYTOOLKIT [Controller] Add MotionReplayController component to load a motion from csv file and apply it to a MechanicalObject Feb 20, 2026
@rmolazem
Copy link
Author

Finishing the implementation of the controller depends on #44

@epernod
Copy link
Contributor

epernod commented Feb 24, 2026

The component is independent from the breathing as the goal is to make it generic. I think with the list of indices it is good.
I will double check the code and then we can merge it.

@rmolazem
Copy link
Author

The breathing option was added to the Motion Replay Controller through a boolean breathing flag. I think it is better to keep the breathing deformation within this controller, since it overwrites the grid's MO too.
Introducing separate controllers for heart motion and breathing, where both overwriting the grids, would require careful management of the controller instantiation hierarchy, which may not be very user-friendly in the complex scene files.
I will still do modifications to the controller for now to see if I can fix the problem of fixed points, then we can decide if we separate the breathing from the heart beating or not.

Copy link
Contributor

@epernod epernod left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

few last changes


double currentTime = this->getContext()->getTime();
// Compute frame index based on the current time and DVF time step
size_t dvfIndex = static_cast<size_t>(currentTime / d_dvfTimeStep.getValue());
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the time step: I initially planned to explicitly compare the scene time step with the one used for deformation recording and log a warning if they differed. But, I defined dvfIndex as it directly maps the current simulation time to the corresponding recorded frame, naturally handling any time step mismatch without requiring an explicit check. I think this way is better.

Copy link
Contributor

@epernod epernod left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there was an error, fixed point where still moving from DVF file
And fixed point should be an option

I wrote the code directly in github. I didn't compiled nor tested the code

Comment on lines +146 to +150
if (fixedIndices.empty())
{
msg_warning() << "No fixed indices provided.";
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (fixedIndices.empty())
{
msg_warning() << "No fixed indices provided.";
return;
}

for me this is just an additional feature so empty array is possible

Comment on lines +152 to +156
std::unordered_set<unsigned int> fixedSet(
fixedIndices.begin(),
fixedIndices.end()
);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
std::unordered_set<unsigned int> fixedSet(
fixedIndices.begin(),
fixedIndices.end()
);

no need to copy the array

Comment on lines +159 to +167
positions[i][0] = frames[currentIndex][i][0];
positions[i][1] = frames[currentIndex][i][1];
positions[i][2] = frames[currentIndex][i][2];

if (fixedSet.find(static_cast<unsigned int>(i)) == fixedSet.end())
{
int axis = d_displacementAxis.getValue(); // 0=X, 1=Y, 2=Z
positions[i][1] += offset;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
positions[i][0] = frames[currentIndex][i][0];
positions[i][1] = frames[currentIndex][i][1];
positions[i][2] = frames[currentIndex][i][2];
if (fixedSet.find(static_cast<unsigned int>(i)) == fixedSet.end())
{
int axis = d_displacementAxis.getValue(); // 0=X, 1=Y, 2=Z
positions[i][1] += offset;
}
auto it = std::find(fixedIndices.begin(), fixedIndices.end(), i);
if (it != fixedIndices.end())
continue;
positions[i][0] = frames[currentIndex][i][0];
positions[i][1] = frames[currentIndex][i][1];
positions[i][2] = frames[currentIndex][i][2];
if (offset != 0.0)
{
int axis = d_displacementAxis.getValue(); // 0=X, 1=Y, 2=Z
positions[i][axis] += offset;
}

you might need to add: #include

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants