66#include < CollisionAlgorithm/operations/CreateCenterProximity.h>
77#include < CollisionAlgorithm/operations/FindClosestProximity.h>
88#include < CollisionAlgorithm/operations/Project.h>
9+ #include < CollisionAlgorithm/operations/ContainsPoint.h>
10+ #include < CollisionAlgorithm/operations/NeedleOperations.h>
911#include < CollisionAlgorithm/proximity/EdgeProximity.h>
10- #include < CollisionAlgorithm/proximity/TetrahedronProximity.h>
1112#include < sofa/component/constraint/lagrangian/solver/ConstraintSolverImpl.h>
1213#include < sofa/component/statecontainer/MechanicalObject.h>
1314
@@ -16,7 +17,7 @@ namespace sofa::collisionalgorithm
1617
1718class InsertionAlgorithm : public BaseAlgorithm
1819{
19- public:
20+ public:
2021 SOFA_CLASS (InsertionAlgorithm, BaseAlgorithm);
2122
2223 typedef core::behavior::MechanicalState<defaulttype::Vec3Types> MechStateTipType;
@@ -215,70 +216,87 @@ class InsertionAlgorithm : public BaseAlgorithm
215216 const BaseProximity::SPtr tipProx = createTipProximity (itTip->element ());
216217 if (!tipProx) return ;
217218
218- // 2.1 Check whether coupling point should be added
219- const type::Vec3 tip2Pt = m_couplingPts.back ()->getPosition () - tipProx->getPosition ();
220- if (tip2Pt.norm () > d_tipDistThreshold.getValue ())
219+ type::Vec3 lastCP = m_couplingPts.back ()->getPosition ();
220+ const SReal tipDistThreshold = this ->d_tipDistThreshold .getValue ();
221+
222+ // Vector from tip to last coupling point; used for distance and directional checks
223+ const type::Vec3 tipToLastCP = lastCP - tipProx->getPosition ();
224+
225+ // Only add a new coupling point if the needle tip has advanced far enough
226+ if (tipToLastCP.norm () > tipDistThreshold)
221227 {
228+ // Prepare the operations before entering the loop
229+ auto createShaftProximity =
230+ Operations::CreateCenterProximity::Operation::get (l_shaftGeom->getTypeInfo ());
231+ auto projectOnShaft = Operations::Project::Operation::get (l_shaftGeom);
222232 auto findClosestProxOnVol =
223233 Operations::FindClosestProximity::Operation::get (l_volGeom);
224234 auto projectOnVol = Operations::Project::Operation::get (l_volGeom);
225- const BaseProximity::SPtr volProx =
226- findClosestProxOnVol (tipProx, l_volGeom. get (), projectOnVol, getFilterFunc () );
227- // Proximity can be detected before the tip enters the tetra (e.g. near a boundary face)
228- // Only accept proximities if the tip is inside the tetra during insertion
229- if (volProx )
235+ auto containsPointInVol =
236+ Operations::ContainsPointInProximity::Operation:: get (l_volGeom );
237+
238+ // Iterate over shaft segments to find which one contains the next candidate CP
239+ for ( auto itShaft = l_shaftGeom-> begin (); itShaft != l_shaftGeom-> end (); itShaft++ )
230240 {
231- TetrahedronProximity::SPtr tetProx =
232- dynamic_pointer_cast<TetrahedronProximity>(volProx);
233- if (tetProx)
241+ BaseProximity::SPtr shaftProx = createShaftProximity (itShaft->element ());
242+ if (!shaftProx) continue ;
243+
244+ const EdgeProximity::SPtr edgeProx =
245+ dynamic_pointer_cast<EdgeProximity>(shaftProx);
246+ if (!edgeProx) continue ;
247+
248+ const type::Vec3 p0 = edgeProx->element ()->getP0 ()->getPosition ();
249+ const type::Vec3 p1 = edgeProx->element ()->getP1 ()->getPosition ();
250+ const type::Vec3 shaftEdgeDir = (p1 - p0).normalized ();
251+ const type::Vec3 lastCPToP1 = p1 - lastCP;
252+
253+ // Skip if last CP lies after edge end point
254+ if (dot (shaftEdgeDir, lastCPToP1) < 0_sreal) continue ;
255+
256+ const int numCPs = floor (lastCPToP1.norm () / tipDistThreshold);
257+
258+ for (int idCP = 0 ; idCP < numCPs ; idCP++)
234259 {
235- double f0 (tetProx->f0 ()), f1 (tetProx->f1 ()), f2 (tetProx->f2 ()),
236- f3 (tetProx->f3 ());
237- bool isInTetra = toolbox::TetrahedronToolBox::isInTetra (
238- tipProx->getPosition (), tetProx->element ()->getTetrahedronInfo (), f0,
239- f1, f2, f3);
240- if (isInTetra)
260+ // Candidate coupling point along shaft segment
261+ const type::Vec3 candidateCP = lastCP + tipDistThreshold * shaftEdgeDir;
262+
263+ // Project candidate CP onto the edge element and compute scalar coordinate
264+ // along segment
265+ const SReal edgeSegmentLength = (p1 - p0).norm ();
266+ const type::Vec3 p0ToCandidateCP = candidateCP - p0;
267+ const SReal projPtOnEdge = dot (p0ToCandidateCP, shaftEdgeDir);
268+
269+ // Skip if candidate CP is outside current edge segment
270+ if (projPtOnEdge < 0_sreal || projPtOnEdge > edgeSegmentLength) break ;
271+
272+ // Project candidate CP onto shaft geometry ...
273+ shaftProx = projectOnShaft (candidateCP, itShaft->element ()).prox ;
274+ if (!shaftProx) continue ;
275+
276+ // ... then find nearest volume proximity
277+ const BaseProximity::SPtr volProx = findClosestProxOnVol (
278+ shaftProx, l_volGeom.get (), projectOnVol, getFilterFunc ());
279+ if (!volProx) continue ;
280+
281+ // Proximity can be detected before the tip enters the tetra (e.g. near a
282+ // boundary face) Only accept proximities if the tip is inside the tetra
283+ // during insertion
284+ if (containsPointInVol (shaftProx->getPosition (), volProx))
241285 {
242286 volProx->normalize ();
243287 m_couplingPts.push_back (volProx);
288+ lastCP = volProx->getPosition ();
244289 }
245290 }
246291 }
247292 }
248293 else // Don't bother with removing the point that was just added
249294 {
250- // 2.2. Check whether coupling point should be removed
295+ // Remove coupling points that are ahead of the tip in the insertion direction
251296 ElementIterator::SPtr itShaft = l_shaftGeom->begin (l_shaftGeom->getSize () - 2 );
252- auto createShaftProximity =
253- Operations::CreateCenterProximity::Operation::get (itShaft->getTypeInfo ());
254- const BaseProximity::SPtr shaftProx = createShaftProximity (itShaft->element ());
255- if (shaftProx)
256- {
257- const EdgeProximity::SPtr edgeProx =
258- dynamic_pointer_cast<EdgeProximity>(shaftProx);
259- if (edgeProx)
260- {
261- const type::Vec3 normal = (edgeProx->element ()->getP1 ()->getPosition () -
262- edgeProx->element ()->getP0 ()->getPosition ())
263- .normalized ();
264- // If the (last) coupling point lies ahead of the tip (positive dot product),
265- // the needle is retreating. Thus, that point is removed.
266- if (dot (tip2Pt, normal) > 0_sreal)
267- {
268- m_couplingPts.pop_back ();
269- }
270- }
271- else
272- {
273- msg_warning () << " shaftGeom: " << l_shaftGeom->getName ()
274- << " is not an EdgeGeometry. Point removal is disabled" ;
275- }
276- }
277- else
278- {
279- msg_warning () << " Cannot create proximity from shaftGeom: "
280- << l_shaftGeom->getName () << " - point removal is disabled" ;
281- }
297+ auto prunePointsAheadOfTip =
298+ Operations::Needle::PrunePointsAheadOfTip::get (itShaft->getTypeInfo ());
299+ prunePointsAheadOfTip (m_couplingPts, itShaft->element ());
282300 }
283301 }
284302
@@ -299,8 +317,7 @@ class InsertionAlgorithm : public BaseAlgorithm
299317 // This is a final-frontier check: If there are coupling points stored, but the
300318 // findClosestProxOnShaf operation yields no proximities on the shaft, it could be
301319 // because the needle has exited abruptly. Thus, we clear the coupling points.
302- if (insertionOutput.size () == 0 )
303- m_couplingPts.clear ();
320+ if (insertionOutput.size () == 0 ) m_couplingPts.clear ();
304321 }
305322
306323 d_collisionOutput.endEdit ();
0 commit comments