@@ -126,124 +126,12 @@ void InsertionAlgorithm::doDetection()
126126 {
127127 // Insertion sequence
128128 sofa::helper::AdvancedTimer::stepBegin (" Needle insertion - " + this ->getName ());
129- if (!d_enableInsertion.getValue () || !l_tipGeom || !l_volGeom || !l_shaftGeom) return ;
130-
131- ElementIterator::SPtr itTip = l_tipGeom->begin ();
132- auto createTipProximity =
133- Operations::CreateCenterProximity::Operation::get (itTip->getTypeInfo ());
134- const BaseProximity::SPtr tipProx = createTipProximity (itTip->element ());
135- if (!tipProx) return ;
136-
137- // Remove coupling points that are ahead of the tip in the insertion direction
138- ElementIterator::SPtr itShaft = l_shaftGeom->begin (l_shaftGeom->getSize () - 2 );
139- auto prunePointsAheadOfTip =
140- Operations::Needle::PrunePointsAheadOfTip::get (itShaft->getTypeInfo ());
141- prunePointsAheadOfTip (m_couplingPts, itShaft->element ());
142-
143- if (m_couplingPts.empty ()) return ;
144-
145- type::Vec3 lastCP = m_couplingPts.back ()->getPosition ();
146- const SReal tipDistThreshold = this ->d_tipDistThreshold .getValue ();
147-
148- // Vector from tip to last coupling point; used for distance and directional checks
149- const type::Vec3 tipToLastCP = lastCP - tipProx->getPosition ();
150-
151- // Only add a new coupling point if the needle tip has advanced far enough
152- if (tipToLastCP.norm () > tipDistThreshold)
153- {
154- // Prepare the operations before entering the loop
155- auto createShaftProximity =
156- Operations::CreateCenterProximity::Operation::get (l_shaftGeom->getTypeInfo ());
157- auto projectOnShaft = Operations::Project::Operation::get (l_shaftGeom);
158- auto findClosestProxOnVol = Operations::FindClosestProximity::Operation::get (l_volGeom);
159- auto projectOnVol = Operations::Project::Operation::get (l_volGeom);
160- auto containsPointInVol =
161- Operations::ContainsPointInProximity::Operation::get (l_volGeom);
162-
163- // Iterate over shaft segments to find which one contains the next candidate CP
164- for (auto itShaft = l_shaftGeom->begin (); itShaft != l_shaftGeom->end (); itShaft++)
165- {
166- BaseProximity::SPtr shaftProx = createShaftProximity (itShaft->element ());
167- if (!shaftProx) continue ;
168-
169- const EdgeProximity::SPtr edgeProx = dynamic_pointer_cast<EdgeProximity>(shaftProx);
170- if (!edgeProx) continue ;
171-
172- const type::Vec3 p0 = edgeProx->element ()->getP0 ()->getPosition ();
173- const type::Vec3 p1 = edgeProx->element ()->getP1 ()->getPosition ();
174- const type::Vec3 shaftEdgeDir = (p1 - p0).normalized ();
175- const type::Vec3 lastCPToP1 = p1 - lastCP;
176-
177- // Skip if last CP lies after edge end point
178- if (dot (shaftEdgeDir, lastCPToP1) < 0_sreal) continue ;
179-
180- const int numCPs = floor (lastCPToP1.norm () / tipDistThreshold);
181-
182- for (int idCP = 0 ; idCP < numCPs; idCP++)
183- {
184- // Candidate coupling point along shaft segment
185- const type::Vec3 candidateCP = lastCP + tipDistThreshold * shaftEdgeDir;
186-
187- // Project candidate CP onto the edge element and compute scalar coordinate
188- // along segment
189- const SReal edgeSegmentLength = (p1 - p0).norm ();
190- const type::Vec3 p0ToCandidateCP = candidateCP - p0;
191- const SReal projPtOnEdge = dot (p0ToCandidateCP, shaftEdgeDir);
192-
193- // Skip if candidate CP is outside current edge segment
194- if (projPtOnEdge < 0_sreal || projPtOnEdge > edgeSegmentLength) break ;
195-
196- // Project candidate CP onto shaft geometry ...
197- shaftProx = projectOnShaft (candidateCP, itShaft->element ()).prox ;
198- if (!shaftProx) continue ;
199-
200- // ... then find nearest volume proximity
201- const BaseProximity::SPtr volProx = findClosestProxOnVol (
202- shaftProx, l_volGeom.get (), projectOnVol, getFilterFunc ());
203- if (!volProx) continue ;
204-
205- // Proximity can be detected before the tip enters the tetra (e.g. near a
206- // boundary face) Only accept proximities if the tip is inside the tetra
207- // during insertion
208- if (containsPointInVol (shaftProx->getPosition (), volProx))
209- {
210- volProx->normalize ();
211- m_couplingPts.push_back (volProx);
212- lastCP = volProx->getPosition ();
213- }
214- }
215- }
216- }
217- else // Don't bother with removing the point that was just added
218- {
219- // Remove coupling points that are ahead of the tip in the insertion direction
220- ElementIterator::SPtr itShaft = l_shaftGeom->begin (l_shaftGeom->getSize () - 2 );
221- auto prunePointsAheadOfTip =
222- Operations::Needle::PrunePointsAheadOfTip::get (itShaft->getTypeInfo ());
223- prunePointsAheadOfTip (m_couplingPts, itShaft->element ());
224- }
129+ if (!d_enableInsertion.getValue ()) insertionPhase ();
225130 sofa::helper::AdvancedTimer::stepEnd (" Needle insertion - " + this ->getName ());
226131 }
227132
228133 sofa::helper::AdvancedTimer::stepBegin (" Reproject coupling points - " + this ->getName ());
229- if (d_enableInsertion.getValue () && !m_couplingPts.empty () && l_shaftGeom)
230- {
231- // Reprojection on shaft geometry sequence
232- auto findClosestProxOnShaft = Operations::FindClosestProximity::Operation::get (l_shaftGeom);
233- auto projectOnShaft = Operations::Project::Operation::get (l_shaftGeom);
234- for (const auto & cp : m_couplingPts)
235- {
236- const BaseProximity::SPtr shaftProx =
237- findClosestProxOnShaft (cp, l_shaftGeom.get (), projectOnShaft, getFilterFunc ());
238- if (!shaftProx) continue ;
239- shaftProx->normalize ();
240- insertionOutput.add (shaftProx, cp);
241- }
242- // This is a final-frontier check: If there are coupling points stored, but the
243- // findClosestProxOnShaf operation yields no proximities on the shaft, it could be
244- // because the needle has exited abruptly. Thus, we clear the coupling points.
245- if (insertionOutput.size () == 0 ) m_couplingPts.clear ();
246- }
134+ if (d_enableInsertion.getValue ()) insertionOutput = reprojectCouplingPoints ();
247135 sofa::helper::AdvancedTimer::stepEnd (" Reproject coupling points - " + this ->getName ());
248136
249137 d_collisionOutput.endEdit ();
@@ -343,4 +231,128 @@ InsertionAlgorithm::AlgorithmOutput InsertionAlgorithm::shaftCollisionPhase()
343231 return shaftCollisionOutput;
344232}
345233
234+ void InsertionAlgorithm::insertionPhase ()
235+ {
236+ if (!l_tipGeom || !l_volGeom || !l_shaftGeom) return ;
237+
238+ ElementIterator::SPtr itTip = l_tipGeom->begin ();
239+ auto createTipProximity =
240+ Operations::CreateCenterProximity::Operation::get (itTip->getTypeInfo ());
241+ const BaseProximity::SPtr tipProx = createTipProximity (itTip->element ());
242+ if (!tipProx) return ;
243+
244+ // Remove coupling points that are ahead of the tip in the insertion direction
245+ ElementIterator::SPtr itShaft = l_shaftGeom->begin (l_shaftGeom->getSize () - 2 );
246+ auto prunePointsAheadOfTip =
247+ Operations::Needle::PrunePointsAheadOfTip::get (itShaft->getTypeInfo ());
248+ prunePointsAheadOfTip (m_couplingPts, itShaft->element ());
249+
250+ if (m_couplingPts.empty ()) return ;
251+
252+ type::Vec3 lastCP = m_couplingPts.back ()->getPosition ();
253+ const SReal tipDistThreshold = this ->d_tipDistThreshold .getValue ();
254+
255+ // Vector from tip to last coupling point; used for distance and directional checks
256+ const type::Vec3 tipToLastCP = lastCP - tipProx->getPosition ();
257+
258+ // Only add a new coupling point if the needle tip has advanced far enough
259+ if (tipToLastCP.norm () > tipDistThreshold)
260+ {
261+ // Prepare the operations before entering the loop
262+ auto createShaftProximity =
263+ Operations::CreateCenterProximity::Operation::get (l_shaftGeom->getTypeInfo ());
264+ auto projectOnShaft = Operations::Project::Operation::get (l_shaftGeom);
265+ auto findClosestProxOnVol = Operations::FindClosestProximity::Operation::get (l_volGeom);
266+ auto projectOnVol = Operations::Project::Operation::get (l_volGeom);
267+ auto containsPointInVol = Operations::ContainsPointInProximity::Operation::get (l_volGeom);
268+
269+ // Iterate over shaft segments to find which one contains the next candidate CP
270+ for (auto itShaft = l_shaftGeom->begin (); itShaft != l_shaftGeom->end (); itShaft++)
271+ {
272+ BaseProximity::SPtr shaftProx = createShaftProximity (itShaft->element ());
273+ if (!shaftProx) continue ;
274+
275+ const EdgeProximity::SPtr edgeProx = dynamic_pointer_cast<EdgeProximity>(shaftProx);
276+ if (!edgeProx) continue ;
277+
278+ const type::Vec3 p0 = edgeProx->element ()->getP0 ()->getPosition ();
279+ const type::Vec3 p1 = edgeProx->element ()->getP1 ()->getPosition ();
280+ const type::Vec3 shaftEdgeDir = (p1 - p0).normalized ();
281+ const type::Vec3 lastCPToP1 = p1 - lastCP;
282+
283+ // Skip if last CP lies after edge end point
284+ if (dot (shaftEdgeDir, lastCPToP1) < 0_sreal) continue ;
285+
286+ const int numCPs = floor (lastCPToP1.norm () / tipDistThreshold);
287+
288+ for (int idCP = 0 ; idCP < numCPs; idCP++)
289+ {
290+ // Candidate coupling point along shaft segment
291+ const type::Vec3 candidateCP = lastCP + tipDistThreshold * shaftEdgeDir;
292+
293+ // Project candidate CP onto the edge element and compute scalar coordinate
294+ // along segment
295+ const SReal edgeSegmentLength = (p1 - p0).norm ();
296+ const type::Vec3 p0ToCandidateCP = candidateCP - p0;
297+ const SReal projPtOnEdge = dot (p0ToCandidateCP, shaftEdgeDir);
298+
299+ // Skip if candidate CP is outside current edge segment
300+ if (projPtOnEdge < 0_sreal || projPtOnEdge > edgeSegmentLength) break ;
301+
302+ // Project candidate CP onto shaft geometry ...
303+ shaftProx = projectOnShaft (candidateCP, itShaft->element ()).prox ;
304+ if (!shaftProx) continue ;
305+
306+ // ... then find nearest volume proximity
307+ const BaseProximity::SPtr volProx =
308+ findClosestProxOnVol (shaftProx, l_volGeom.get (), projectOnVol, getFilterFunc ());
309+ if (!volProx) continue ;
310+
311+ // Proximity can be detected before the tip enters the tetra (e.g. near a
312+ // boundary face) Only accept proximities if the tip is inside the tetra
313+ // during insertion
314+ if (containsPointInVol (shaftProx->getPosition (), volProx))
315+ {
316+ volProx->normalize ();
317+ m_couplingPts.push_back (volProx);
318+ lastCP = volProx->getPosition ();
319+ }
320+ }
321+ }
322+ }
323+ else // Don't bother with removing the point that was just added
324+ {
325+ // Remove coupling points that are ahead of the tip in the insertion direction
326+ ElementIterator::SPtr itShaft = l_shaftGeom->begin (l_shaftGeom->getSize () - 2 );
327+ auto prunePointsAheadOfTip =
328+ Operations::Needle::PrunePointsAheadOfTip::get (itShaft->getTypeInfo ());
329+ prunePointsAheadOfTip (m_couplingPts, itShaft->element ());
330+ }
331+ }
332+
333+ InsertionAlgorithm::AlgorithmOutput InsertionAlgorithm::reprojectCouplingPoints ()
334+ {
335+ if (m_couplingPts.empty () || !l_shaftGeom) return AlgorithmOutput ();
336+
337+ AlgorithmOutput reprojectionOutput;
338+
339+ // Reprojection on shaft geometry sequence
340+ auto findClosestProxOnShaft = Operations::FindClosestProximity::Operation::get (l_shaftGeom);
341+ auto projectOnShaft = Operations::Project::Operation::get (l_shaftGeom);
342+ for (const auto & cp : m_couplingPts)
343+ {
344+ const BaseProximity::SPtr shaftProx =
345+ findClosestProxOnShaft (cp, l_shaftGeom.get (), projectOnShaft, getFilterFunc ());
346+ if (!shaftProx) continue ;
347+ shaftProx->normalize ();
348+ reprojectionOutput.add (shaftProx, cp);
349+ }
350+ // This is a final-frontier check: If there are coupling points stored, but the
351+ // findClosestProxOnShaf operation yields no proximities on the shaft, it could be
352+ // because the needle has exited abruptly. Thus, we clear the coupling points.
353+ if (reprojectionOutput.size () == 0 ) m_couplingPts.clear ();
354+
355+ return reprojectionOutput;
356+ }
357+
346358} // namespace sofa::collisionalgorithm
0 commit comments