Skip to content

Commit e6d3e39

Browse files
authored
[algorithm] Fix problem with gap in coupling points when puncturing tissue with high stiffness (#82)
* [algorithm] Modified scene to illustrate tight puncture bug * [algorithm] Fix works only for tip distance equal to the line segments * [algorithm] Renamed lastCp * [algorithm] Renamed tip2Pt * [algorithm] Renamed newCp * [algorithm] Renamed p0toCp * [algorithm] Adopted CP abbreviation for coupling point * [algorithm] Renamed m_couplingPts to m_CPs * [algorithm] Renamed edgeNormal * [algorithm] Renamed dotProd * [algorithm] Commented code * [algorithm] Format code * [algorithm] Remove old part of the code - tip-based detection * [algorithm] Finding location of last CP relies on shaft edge direction instead of tipToLastCP * [algorithm] Enabled addition of coupling points even if the spacing is smaller than the edge length * [scene] Adjusted parameters in the scene * [algorithm] Revert back to m_couplingPts naming * [algorithm] Using the ContainsInPoint operation
1 parent aacf5bd commit e6d3e39

File tree

2 files changed

+71
-21
lines changed

2 files changed

+71
-21
lines changed

scenes/NeedleInsertion.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
g_needleRadius = 0.001 #(m)
77
g_needleMechanicalParameters = {
88
"radius":g_needleRadius,
9-
"youngModulus":1e11,
9+
"youngModulus":1e12,
1010
"poissonRatio":0.3
1111
}
1212
g_needleTotalMass=0.01
@@ -17,7 +17,7 @@
1717
"max":[0.125, 0.125, -0.100]
1818
} #Again all in mm
1919
g_gelMechanicalParameters = {
20-
"youngModulus":8e5,
20+
"youngModulus":1e6,
2121
"poissonRatio":0.45,
2222
"method":"large"
2323
}
@@ -60,7 +60,7 @@ def createScene(root):
6060
root.addObject("ConstraintAttachButtonSetting")
6161
root.addObject("VisualStyle", displayFlags="showVisualModels hideBehaviorModels showCollisionModels hideMappings hideForceFields showWireframe showInteractionForceFields" )
6262
root.addObject("FreeMotionAnimationLoop")
63-
root.addObject("GenericConstraintSolver", tolerance=0.00001, maxIt=5000)
63+
root.addObject("GenericConstraintSolver", tolerance=1e-5, maxIt=5000)
6464
root.addObject("CollisionLoop")
6565

6666
needleBaseMaster = root.addChild("NeedleBaseMaster")
@@ -189,7 +189,7 @@ def createScene(root):
189189
surfGeom="@Volume/collision/geom_tri",
190190
shaftGeom="@Needle/bodyCollision/geom_body",
191191
volGeom="@Volume/geom_tetra",
192-
punctureForceThreshold=2.,
192+
punctureForceThreshold=20,
193193
tipDistThreshold=0.003,
194194
drawcollision=True,
195195
drawPointsScale=0.0001
@@ -199,4 +199,4 @@ def createScene(root):
199199
root.addObject("ConstraintUnilateral",input="@InsertionAlgo.collisionOutput",directions="@punctureDirection",draw_scale=0.001)
200200

201201
root.addObject("FirstDirection",name="bindDirection", handler="@Needle/bodyCollision/NeedleBeams")
202-
root.addObject("ConstraintInsertion",input="@InsertionAlgo.insertionOutput", directions="@bindDirection",draw_scale=0.002, frictionCoeff=0.05)
202+
root.addObject("ConstraintInsertion",input="@InsertionAlgo.insertionOutput", directions="@bindDirection",draw_scale=0.002, frictionCoeff=0.01)

src/sofa/collisionAlgorithm/algorithm/InsertionAlgorithm.h

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace sofa::collisionAlgorithm
1818

1919
class InsertionAlgorithm : public BaseAlgorithm
2020
{
21-
public:
21+
public:
2222
SOFA_CLASS(InsertionAlgorithm, BaseAlgorithm);
2323

2424
typedef core::behavior::MechanicalState<defaulttype::Vec3Types> MechStateTipType;
@@ -217,26 +217,77 @@ class InsertionAlgorithm : public BaseAlgorithm
217217
const BaseProximity::SPtr tipProx = createTipProximity(itTip->element());
218218
if (!tipProx) return;
219219

220-
// 2.1 Check whether coupling point should be added
221-
const type::Vec3 tip2Pt = m_couplingPts.back()->getPosition() - tipProx->getPosition();
222-
if (tip2Pt.norm() > d_tipDistThreshold.getValue())
220+
type::Vec3 lastCP = m_couplingPts.back()->getPosition();
221+
const SReal tipDistThreshold = this->d_tipDistThreshold.getValue();
222+
223+
// Vector from tip to last coupling point; used for distance and directional checks
224+
const type::Vec3 tipToLastCP = lastCP - tipProx->getPosition();
225+
226+
// Only add a new coupling point if the needle tip has advanced far enough
227+
if (tipToLastCP.norm() > tipDistThreshold)
223228
{
229+
// Prepare the operations before entering the loop
230+
auto createShaftProximity =
231+
Operations::CreateCenterProximity::Operation::get(l_shaftGeom->getTypeInfo());
232+
auto projectOnShaft = Operations::Project::Operation::get(l_shaftGeom);
224233
auto findClosestProxOnVol =
225234
Operations::FindClosestProximity::Operation::get(l_volGeom);
226235
auto projectOnVol = Operations::Project::Operation::get(l_volGeom);
227-
const BaseProximity::SPtr volProx =
228-
findClosestProxOnVol(tipProx, l_volGeom.get(), projectOnVol, getFilterFunc());
236+
auto containsPointInVol =
237+
Operations::ContainsPointInProximity::Operation::get(l_volGeom);
229238

230-
// Proximity can be detected before the tip enters the tetra (e.g. near a boundary face)
231-
// Only accept proximities if the tip is inside the tetra during insertion
232-
if (volProx)
239+
// Iterate over shaft segments to find which one contains the next candidate CP
240+
for (auto itShaft = l_shaftGeom->begin(); itShaft != l_shaftGeom->end(); itShaft++)
233241
{
234-
auto containsPointInVol = Operations::ContainsPointInProximity::Operation::get(
235-
l_volGeom->getTypeInfo());
236-
if(containsPointInVol(tipProx->getPosition(), volProx))
242+
BaseProximity::SPtr shaftProx = createShaftProximity(itShaft->element());
243+
if (!shaftProx) continue;
244+
245+
const EdgeProximity::SPtr edgeProx =
246+
dynamic_pointer_cast<EdgeProximity>(shaftProx);
247+
if (!edgeProx) continue;
248+
249+
const type::Vec3 p0 = edgeProx->element()->getP0()->getPosition();
250+
const type::Vec3 p1 = edgeProx->element()->getP1()->getPosition();
251+
const type::Vec3 shaftEdgeDir = (p1 - p0).normalized();
252+
const type::Vec3 lastCPToP1 = p1 - lastCP;
253+
254+
// Skip if last CP lies after edge end point
255+
if (dot(shaftEdgeDir, lastCPToP1) < 0_sreal) continue;
256+
257+
const int numCPs = floor(lastCPToP1.norm() / tipDistThreshold);
258+
259+
for(int idCP = 0 ; idCP < numCPs ; idCP++)
237260
{
238-
volProx->normalize();
239-
m_couplingPts.push_back(volProx);
261+
// Candidate coupling point along shaft segment
262+
const type::Vec3 candidateCP = lastCP + tipDistThreshold * shaftEdgeDir;
263+
264+
// Project candidate CP onto the edge element and compute scalar coordinate
265+
// along segment
266+
const SReal edgeSegmentLength = (p1 - p0).norm();
267+
const type::Vec3 p0ToCandidateCP = candidateCP - p0;
268+
const SReal projPtOnEdge = dot(p0ToCandidateCP, shaftEdgeDir);
269+
270+
// Skip if candidate CP is outside current edge segment
271+
if (projPtOnEdge < 0_sreal || projPtOnEdge > edgeSegmentLength) break;
272+
273+
// Project candidate CP onto shaft geometry ...
274+
shaftProx = projectOnShaft(candidateCP, itShaft->element()).prox;
275+
if (!shaftProx) continue;
276+
277+
// ... then find nearest volume proximity
278+
const BaseProximity::SPtr volProx = findClosestProxOnVol(
279+
shaftProx, l_volGeom.get(), projectOnVol, getFilterFunc());
280+
if (!volProx) continue;
281+
282+
// Proximity can be detected before the tip enters the tetra (e.g. near a
283+
// boundary face) Only accept proximities if the tip is inside the tetra
284+
// during insertion
285+
if (containsPointInVol(shaftProx->getPosition(), volProx))
286+
{
287+
volProx->normalize();
288+
m_couplingPts.push_back(volProx);
289+
lastCP = volProx->getPosition();
290+
}
240291
}
241292
}
242293
}
@@ -267,8 +318,7 @@ class InsertionAlgorithm : public BaseAlgorithm
267318
// This is a final-frontier check: If there are coupling points stored, but the
268319
// findClosestProxOnShaf operation yields no proximities on the shaft, it could be
269320
// because the needle has exited abruptly. Thus, we clear the coupling points.
270-
if (insertionOutput.size() == 0)
271-
m_couplingPts.clear();
321+
if (insertionOutput.size() == 0) m_couplingPts.clear();
272322
}
273323

274324
d_collisionOutput.endEdit();

0 commit comments

Comments
 (0)