UsTK : Ultrasound ToolKit  version 2.0.1 under development (2024-05-20)
usNeedleInsertionModelVirtualSprings.cpp
1 /****************************************************************************
2  *
3  * This file is part of the ustk software.
4  * Copyright (C) 2016 - 2017 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * ("GPL") version 2 as published by the Free Software Foundation.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ustk with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * This software was developed at:
17  * Inria Rennes - Bretagne Atlantique
18  * Campus Universitaire de Beaulieu
19  * 35042 Rennes Cedex
20  * France
21  *
22  * If you have questions regarding the use of this file, please contact
23  * Inria at ustk@inria.fr
24  *
25  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27  *
28  * Author:
29  * Jason Chevrie
30  *
31  *****************************************************************************/
32 
33 #include <visp3/ustk_needle_modeling/usNeedleInsertionModelVirtualSprings.h>
34 
35 #include <iomanip>
36 
37 #include <visp3/ustk_core/usGeometryTools.h>
38 
39 #include <visp3/core/vpConfig.h>
40 
41 #ifdef VISP_HAVE_EIGEN3
42 #include <eigen3/Eigen/SparseCore>
43 #include <eigen3/Eigen/SparseLU>
44 #endif
45 
46 #ifdef VISP_HAVE_OPENCV
47 #include <opencv2/core/core.hpp>
48 #include <opencv2/imgproc/imgproc.hpp>
49 #endif
50 
51 #include <visp3/core/vpException.h>
52 #include <visp3/core/vpPoseVector.h>
53 #include <visp3/core/vpRotationMatrix.h>
54 #include <visp3/core/vpTime.h>
55 #include <visp3/core/vpTranslationVector.h>
56 #include <visp3/gui/vpDisplayX.h>
57 
59  : m_needle(),
60 
61  m_tipForce(0), m_tipMoment(0), m_cutAngle(0), m_bevelLength(0.001),
62 
63  m_defaultSpringStiffness(100), m_stiffnessPerUnitLength(25000),
64  m_tissueSurface(vpColVector(3, 0), vpColVector(3, 0)),
65 
66  m_interSpringDistance(0.005), m_interTipSpringDistance(0.0005),
67 
68  m_IsStateConsistent(false),
69 
70  m_LastSegmentLengthComputed(true),
71 
72  m_insertionBehavior(InsertionType::NaturalBehavior), m_IsInserting(false), m_AllowSpringAddition(true),
73  m_AllowSpringRemoval(true), m_AutomaticSpringAddition(true),
74 
75  m_tipSpringsIndex(0), m_nbMinTipSprings(1), m_nbMaxTipSprings(10)
76 
77 {
78  // Computation of tip position
80 }
81 
84  : m_needle(needle.m_needle),
85 
86  m_tipForce(needle.m_tipForce), m_tipMoment(needle.m_tipMoment), m_cutAngle(needle.m_cutAngle),
87  m_bevelLength(needle.m_bevelLength),
88 
89  m_defaultSpringStiffness(needle.m_defaultSpringStiffness),
90  m_stiffnessPerUnitLength(needle.m_stiffnessPerUnitLength),
91 
92  m_springs(needle.m_springs), m_inactiveAutoAddedSprings(needle.m_inactiveAutoAddedSprings),
93  m_inactiveMeasureSprings(needle.m_inactiveMeasureSprings), m_tissueSurface(needle.m_tissueSurface),
94  m_interSpringDistance(needle.m_interSpringDistance), m_interTipSpringDistance(needle.m_interTipSpringDistance),
95 
96  m_IsStateConsistent(needle.m_IsStateConsistent),
97 
98  m_LastSegmentLengthComputed(needle.m_LastSegmentLengthComputed),
99 
100  m_insertionBehavior(needle.m_insertionBehavior), m_IsInserting(needle.m_IsInserting),
101  m_AllowSpringAddition(needle.m_AllowSpringAddition), m_AllowSpringRemoval(needle.m_AllowSpringRemoval),
102  m_AutomaticSpringAddition(needle.m_AutomaticSpringAddition),
103 
104  m_tipSpringsIndex(needle.m_tipSpringsIndex), m_nbMinTipSprings(needle.m_nbMinTipSprings),
105  m_nbMaxTipSprings(needle.m_nbMaxTipSprings)
106 
107 {
108 }
109 
111 
113 {
114  return new usNeedleInsertionModelVirtualSprings(*this);
115 }
116 
119 {
120  m_needle = needle.m_needle;
121  m_tipForce = needle.m_tipForce;
122  m_tipMoment = needle.m_tipMoment;
123  m_cutAngle = needle.m_cutAngle;
124  m_bevelLength = needle.m_bevelLength;
125 
128 
129  m_springs = needle.m_springs;
135 
137 
139 
141  m_IsInserting = needle.m_IsInserting;
145 
149 
150  return *this;
151 }
152 
154 {
155  switch (preset) {
158  this->setInterSpringDistance(0.005);
159  this->setInterTipSpringDistance(0.0005);
160  this->setNbMinTipSprings(10);
161  this->setNbMaxTipSprings(12);
162  this->setStiffnessPerUnitLength(20000);
163  this->setBevelAngle(M_PI / 180 * 24);
164  break;
165  }
168  this->setInterSpringDistance(0.005);
169  this->setInterTipSpringDistance(0.0005);
170  this->setNbMinTipSprings(10);
171  this->setNbMaxTipSprings(12);
172  this->setStiffnessPerUnitLength(20000);
173  this->setBevelAngle(M_PI / 180 * 24);
174  break;
175  }
178  this->setInterSpringDistance(0.005);
179  this->setInterTipSpringDistance(0.0005);
180  this->setNbMinTipSprings(10);
181  this->setNbMaxTipSprings(12);
182  this->setStiffnessPerUnitLength(35500 * 4 * 0.2 / 0.17);
183  break;
184  }
187  this->setInterSpringDistance(0.005);
188  this->setInterTipSpringDistance(0.0005);
189  this->setNbMinTipSprings(10);
190  this->setNbMaxTipSprings(12);
191  this->setStiffnessPerUnitLength(4830);
192  break;
193  }
196  this->setInterSpringDistance(0.005);
197  this->setInterTipSpringDistance(0.0005);
198  this->setNbMinTipSprings(10);
199  this->setNbMaxTipSprings(12);
200  this->setStiffnessPerUnitLength(150000);
201  break;
202  }
205  this->setInterSpringDistance(0.005);
206  this->setInterTipSpringDistance(0.0005);
207  this->setNbMinTipSprings(10);
208  this->setNbMaxTipSprings(10);
209  this->setStiffnessPerUnitLength(500);
210  break;
211  }
214  this->setInterSpringDistance(0.005);
215  this->setInterTipSpringDistance(0.0005);
216  this->setNbMinTipSprings(10);
217  this->setNbMaxTipSprings(12);
218  this->setStiffnessPerUnitLength(20000);
219  this->setBevelAngle(M_PI / 180 * 30);
220  break;
221  }
224  this->setInterSpringDistance(0.005);
225  this->setInterTipSpringDistance(0.0005);
226  this->setNbMinTipSprings(10);
227  this->setNbMaxTipSprings(12);
228  this->setStiffnessPerUnitLength(20000);
229  this->setBevelAngle(M_PI / 180 * 26);
230  break;
231  }
232  }
233 }
234 
236 {
237  m_tipForce = tipForce;
238 }
239 
241 {
242  return m_tipForce;
243 }
244 
246 {
247  m_bevelLength = m_needle.getOuterDiameter() / tan(angle);
248 }
249 
251 {
252  return atan2(m_needle.getOuterDiameter(), m_bevelLength);
253 }
254 
256 {
257  if (K > 0)
259 }
260 
262 {
264 }
265 
267 {
268  if (K > 0) {
270  updateTipForce();
271  } else
273 }
274 
276 {
278 }
279 
281 {
282  return m_springs.size();
283 }
284 
286 {
287  int n = 0;
288  for (unsigned int i = 0; i < m_springs.size(); i++)
289  if (!m_springs.at(i).IsPositionUpdateAllowed())
290  n++;
291  return n;
292 }
294 {
295  return m_needle;
296 }
297 
299 {
300  return m_needle;
301 }
302 
304 {
306 }
307 
309 {
310  return m_needle.accessSegment(0).getEndPoint();
311 }
312 
314 {
315  return m_springs.front().getPosition();
316 }
317 
319 {
321 }
322 
324 {
325  if (m_needle.getNbSegments() > 1)
327  else
328  return 0;
329 }
330 
332 {
333  return m_tissueSurface;
334 }
335 
337 {
338  return m_tissueSurface;
339 }
340 
342 {
343  return m_springs.at(i);
344 }
345 
347 {
348  m_interSpringDistance = interSpringDistance;
349 }
350 
352 {
353  return m_interSpringDistance;
354 }
355 
357 {
358  if (interTipSpringDistance <= m_interSpringDistance)
359  m_interTipSpringDistance = interTipSpringDistance;
360  else
362 }
363 
365 {
367 }
368 
370 {
371  if (nb > 0) {
372  if (nb <= m_nbMaxTipSprings)
373  m_nbMinTipSprings = nb;
374  else
376  } else
377  m_nbMinTipSprings = 1;
378 }
379 
381 {
382  return m_nbMinTipSprings;
383 }
384 
386 {
387  if (nb > 0) {
388  if (nb >= m_nbMinTipSprings)
389  m_nbMaxTipSprings = nb;
390  else
392  } else
393  m_nbMaxTipSprings = 1;
394 }
395 
397 {
398  return m_nbMaxTipSprings;
399 }
400 
402 {
403  m_AllowSpringAddition = flag;
404 }
405 
407 {
408  m_AllowSpringRemoval = flag;
409 }
410 
412 {
413  m_insertionBehavior = type;
414 }
415 
417 {
418  return m_insertionBehavior;
419 }
420 
422 {
424 }
425 
427 {
429 }
430 
432 {
433  if (P.size() != 3)
434  throw vpException(vpException::dimensionError,
435  "usNeedleInsertionModelVirtualSprings::getPathDistanceFromPoint: invalid point dimension");
436 
437  double min = std::numeric_limits<double>::infinity();
438 
439  for (unsigned int i = 0; i < m_springs.size(); i++) {
440  vpColVector p = usGeometryTools::projectPointOnPlane(P, m_springs.at(i));
441  double d = (m_springs.at(i).getPosition() - p).frobeniusNorm();
442  if (d < min)
443  min = d;
444  }
445 
446  return min;
447 }
448 
450 {
451  double E = 0;
452 
453  for (unsigned int n = 0; n < m_springs.size(); n++) {
454  double K = m_springs.at(n).getStiffness();
455  double l = (m_needle.accessSegment(n).getEndPoint() - m_springs.at(n).getPosition()).frobeniusNorm();
456 
457  E += 0.5 * K * l * l;
458  }
459 
460  return E;
461 }
462 
464 {
465  if (!this->IsNeedleInserted() || (m_springs.size() < 1))
466  return 0;
467 
468  return (this->getTissueInsertionPoint() - this->getNeedleInsertionPoint()).frobeniusNorm();
469 }
470 
472 {
473  if (!this->IsNeedleInserted()) {
474  if (lmax != nullptr)
475  *lmax = 0;
476  return 0;
477  }
478 
479  double max = 0;
480  double maxL = 0;
481  double totalLength = 0;
482 
483  for (unsigned int i = 0; i < m_springs.size(); i++) {
484  double d = (m_springs.at(i).getPosition() - m_needle.accessSegment(i).getEndPoint()).frobeniusNorm();
485  totalLength += m_needle.accessSegment(i).getParametricLength();
486 
487  if (d > max) {
488  max = d;
489  maxL = totalLength;
490  }
491  }
492 
493  if (lmax != nullptr)
494  *lmax = maxL;
495 
496  return max;
497 }
498 
500 {
501  double mean = 0;
502  double L = this->getInsertionDepth();
503 
504  for (unsigned int i = 0; i < m_springs.size(); i++) {
505  double d = (m_springs.at(i).getPosition() - m_needle.accessSegment(i).getEndPoint()).frobeniusNorm();
506  double l1 = m_needle.accessSegment(i).getParametricLength();
507  if (i == 0)
508  l1 = 0;
509  double l2 = m_needle.accessSegment(i + 1).getParametricLength();
510  mean += d * (l1 + l2) / 2;
511  }
512  if (L > std::numeric_limits<double>::epsilon())
513  mean /= L;
514 
515  return mean;
516 }
517 
519 {
520  // Set base position in worldframe if the base doesn't go under the tissue
521 
522  if (m_springs.size() > 0) {
523  vpColVector position(pose.getTranslationVector());
525  return false;
526  }
527 
528  m_needle.setBasePose(pose);
529 
530  this->updateState();
531 
532  return true;
533 }
534 
536 
537 bool usNeedleInsertionModelVirtualSprings::setSpringPosition(int index, const vpColVector &P, bool update)
538 {
539  if (index < 0 || index > (int)m_springs.size() - 1)
540  throw vpException(vpException::badValue,
541  "usNeedleInsertionModelVirtualSprings::setSpringPosition: invlaid spring index");
542 
543  if (P.size() != 3)
544  throw vpException(vpException::dimensionError,
545  "usNeedleInsertionModelVirtualSprings::setSpringPosition: bad vector dimension");
546 
547  m_springs.at(index).setPosition(P);
548  if (update)
549  this->updateState();
550 
551  return true;
552 }
553 
554 bool usNeedleInsertionModelVirtualSprings::setSpringDirection(int index, const vpColVector &D, bool update)
555 {
556  if (index < 0 || index > (int)m_springs.size() - 1)
557  throw vpException(vpException::badValue,
558  "usNeedleInsertionModelVirtualSprings::setSpringDirection: invalid spring index");
559  if (D.size() != 3)
560  throw vpException(vpException::dimensionError,
561  "usNeedleInsertionModelVirtualSprings::setSpringDirection: invalid vector dimension");
562 
563  m_springs.at(index).setDirection(D);
564  if (update)
565  this->updateState();
566 
567  return true;
568 }
569 
570 void usNeedleInsertionModelVirtualSprings::setSpringStiffness(int index, double K, bool update)
571 {
572  if (index < 0 || index > (int)m_springs.size() - 1)
573  throw vpException(vpException::badValue,
574  "usNeedleInsertionModelVirtualSprings::setSpringStiffness: bad spring index");
575  if (K <= 0)
576  throw vpException(vpException::badValue,
577  "usNeedleInsertionModelVirtualSprings::setSpringStiffness: negative stiffness");
578 
579  m_springs.at(index).setStiffness(K);
580  if (update)
581  this->updateState();
582 }
583 
584 bool usNeedleInsertionModelVirtualSprings::moveSpringPosition(int index, const vpColVector &dP, bool update)
585 {
586  return this->setSpringPosition(index, m_springs.at(index).getPosition() + dP, update);
587 }
588 
589 bool usNeedleInsertionModelVirtualSprings::moveSpringDirection(int index, const vpThetaUVector &thetaU, bool update)
590 {
591  vpRotationMatrix R(thetaU);
592  return this->setSpringDirection(index, R * m_springs.at(index).getDirection(), update);
593 }
594 
595 void usNeedleInsertionModelVirtualSprings::addSpringStiffness(int index, double dK, bool update)
596 {
597  return this->setSpringStiffness(index, m_springs.at(index).getStiffness() + dK, update);
598 }
599 
601 {
602  vpColVector p = m_needle.accessLastSegment().getEndPoint();
603  vpColVector d = m_needle.accessLastSegment().getEndTangent();
604  p += m_bevelLength * d +
605  m_needle.getOuterDiameter() / 2 * vpColVector(vpHomogeneousMatrix(m_needle.getTipPose()).getCol(1), 0, 3);
608 }
609 
611 {
612  if (segment < 0 || segment >= m_needle.getNbSegments())
613  throw vpException(vpException::badValue,
614  "usNeedleInsertionModelVirtualSprings::addInsertionPointOnSegment: invalid segment index");
615  if ((s <= 0) ||
616  ((segment != m_needle.getNbSegments() - 1) && (s > m_needle.accessSegment(segment).getParametricLength())))
617  throw vpException(
618  vpException::badValue,
619  "usNeedleInsertionModelVirtualSprings::addInsertionPointOnSegment: invalid segment length parameter");
620 
621  // if(segment == m_needle.getNbSegments()-1) && !m_LastSegmentLengthComputed) return;
622 
623  // Keep segment coef for the first subsegment and switch origine for the second
624 
627 
629 
630  m_needle.insertSegment(segment, seg);
631 
633 
634  if (segment == m_needle.getNbSegments() - 2) {
635  vpColVector bevelDirection(vpHomogeneousMatrix(m_needle.getTipPose()).getCol(1), 0, 3);
636  double l = (m_needle.getOuterDiameter() / 2 -
638  if (l < 0)
639  l = 0;
640  usVirtualSpring spg(m_needle.accessLastSegment().getStartPoint() + l * bevelDirection,
642  m_springs.insert(m_springs.begin() + segment, spg);
643  } else {
646  m_springs.insert(m_springs.begin() + segment, spg);
647  }
648 }
649 
651 {
654 }
655 
657 {
658  if (segment < 0 || segment >= m_needle.getNbSegments())
659  throw vpException(vpException::badValue,
660  "usNeedleInsertionModelVirtualSprings::addInsertionPointOnSegment: bad segment index");
661  if ((s <= 0) ||
662  ((segment != m_needle.getNbSegments() - 1) && (s > m_needle.accessSegment(segment).getParametricLength())))
663  throw vpException(vpException::badValue,
664  "usNeedleInsertionModelVirtualSprings::addInsertionPointOnSegment: bad segment length parameter");
665 
666  double Lseg = m_needle.accessSegment(segment).getParametricLength();
667  double l2 = Lseg - s;
668 
669  if (segment == m_needle.getNbSegments() - 1) // spring added on last segment
670  {
671  double K = 0;
672  if (segment == 0) // if first spring added
673  {
674  K = m_stiffnessPerUnitLength * l2;
675  } else {
676  double L1 =
677  m_needle.accessSegment(segment - 1).getParametricLength(); // length of tissue before current last spring
679  m_bevelLength; // length of tissue after current last spring
680  if (segment == 1)
681  L1 = 0;
682  K = m_springs.at(segment - 1).getStiffness() * (L - s / 2) / (L1 / 2 + L);
683  m_springs.at(segment - 1).addStiffness(-K);
684  }
685 
686  this->addInsertionPointOnSegmentHard(segment, s);
687  m_springs.back().setStiffness(K);
688  } else // spring added on intermediate segment => interpolation
689  {
690  double c = s / Lseg;
691  vpColVector x0 = m_springs.at(segment - 1).getPosition();
692  vpColVector x1 = m_springs.at(segment).getPosition();
693  vpColVector d0 = m_springs.at(segment - 1).getDirection();
694  vpColVector d1 = m_springs.at(segment).getDirection();
695 
696  vpColVector P = c * x1 + (1 - c) * x0;
697  vpColVector D = c * d1 + (1 - c) * d0;
698 
699  double L1 = m_needle.accessSegment(segment - 1).getParametricLength();
700  if (segment - 1 == 0)
701  L1 = 0;
702  double L = m_needle.accessSegment(segment).getParametricLength();
703  double L2 = m_needle.accessSegment(segment + 1).getParametricLength();
704  if (segment + 1 == m_needle.getNbSegments() - 1)
706  double K1 = m_springs.at(segment - 1).getStiffness() * l2 / (L1 + L);
707  double K2 = m_springs.at(segment).getStiffness() * s / (L + L2);
708  m_springs.at(segment - 1).addStiffness(-K1);
709  m_springs.at(segment).addStiffness(-K2);
710 
711  this->addInsertionPointOnSegmentHard(segment, s);
712  m_springs.at(segment).setPosition(P);
713  m_springs.at(segment).setDirection(D);
714  m_springs.at(segment).setStiffness(K1 + K2);
715  }
716 }
717 
719 {
720  int segment = 0;
721 
722  while (segment < m_needle.getNbSegments() &&
724  segment++;
725 
726  if (segment == 0 && m_springs.size() != 0)
727  throw vpException(vpException::badValue,
728  "usNeedleInsertionModelVirtualSprings::addInsertionPoint: cannot add spring on first segment");
729  if (segment == m_needle.getNbSegments())
730  throw vpException(vpException::badValue,
731  "usNeedleInsertionModelVirtualSprings::addInsertionPoint: spring does not cross the needle");
732 
733  double Lseg = m_needle.accessSegment(segment).getParametricLength();
734  double s = -1;
737  } else
738  s = Lseg / 2;
739 
740  double l2 = Lseg - s;
741 
742  if (segment == m_needle.getNbSegments() - 1) // spring added on last segment
743  {
744  double K = 0;
745  if (segment == 0) // if first spring added
746  {
747  K = 2 * m_stiffnessPerUnitLength * (l2 + m_bevelLength);
748  } else {
749  double L1 =
750  m_needle.accessSegment(segment - 1).getParametricLength(); // length of tissue before current last spring
752  m_bevelLength; // length of tissue after current last spring
753  if (segment == 1)
754  L1 = 0;
755  K = m_springs.at(segment - 1).getStiffness() * (L - s / 2) / (L1 / 2 + L);
756  m_springs.at(segment - 1).addStiffness(-K);
757  }
758  spg.setStiffness(K);
759  } else // spring added on intermediate segment => interpolation
760  {
761  double L1 = m_needle.accessSegment(segment - 1).getParametricLength();
762  if (segment - 1 == 0)
763  L1 = 0;
764  double L = m_needle.accessSegment(segment).getParametricLength();
765  double L2 = m_needle.accessSegment(segment + 1).getParametricLength();
766  if (segment + 1 == m_needle.getNbSegments() - 1)
768  double K1 = m_springs.at(segment - 1).getStiffness() * l2 / (L1 + L);
769  double K2 = m_springs.at(segment).getStiffness() * s / (L + L2);
770  m_springs.at(segment - 1).addStiffness(-K1);
771  m_springs.at(segment).addStiffness(-K2);
772  spg.setStiffness(K1 + K2);
773  }
774 
775  this->addInsertionPointOnSegmentHard(segment, s);
776  m_springs.at(segment) = spg;
777 
778  return segment;
779 }
780 
781 int usNeedleInsertionModelVirtualSprings::addInsertionPoint(const vpColVector &p, const vpColVector &d)
782 {
783  if (p.size() != 3 || d.size() != 3)
784  throw vpException(vpException::dimensionError,
785  "usNeedleInsertionModelVirtualSprings::addInsertionPoint: invalid vector dimension");
786 
787  unsigned int index = 0;
788  while (index < m_springs.size() && usGeometryTools::IsPointInFrontOfPlane(p, m_springs.at(index)))
789  index++;
790 
791  if (index == 0 && m_springs.size() != 0) {
792  std::cout << "Warning in usNeedleInsertionModelVirtualSprings::addInsertionPoint: add spring on first segment"
793  << std::endl;
794  }
795 
796  usVirtualSpring spg(p, d, 0);
797 
798  double Lseg = m_needle.accessSegment(index).getParametricLength();
799  double s = 0;
800 
803  spg.setDirection(-spg.getDirection());
804  s = Lseg / 2;
808 
809  vpColVector P = m_needle.accessSegment(index).getPoint(s);
810  spg.setDirection(vpColVector::crossProd(P - p, vpColVector::crossProd(d, P - p)).normalize());
813  s = Lseg - m_interTipSpringDistance;
814 
815  vpColVector P = m_needle.accessSegment(index).getPoint(s);
816  spg.setDirection(vpColVector::crossProd(P - p, vpColVector::crossProd(d, P - p)).normalize());
817  } else {
818  s = Lseg / 2;
819  }
820 
821  double l2 = Lseg - s;
822 
823  int segment = index;
824 
825  if (segment == m_needle.getNbSegments() - 1) // spring added on last segment
826  {
827  double K = 0;
828  if (segment == 0) // if first spring added
829  {
830  K = 2 * m_stiffnessPerUnitLength * (l2 + m_bevelLength);
831  } else {
832  double L1 =
833  m_needle.accessSegment(segment - 1).getParametricLength(); // length of tissue before current last spring
835  m_bevelLength; // length of tissue after current last spring
836  if (segment == 1)
837  L1 = 0;
838  double K = m_springs.at(segment - 1).getStiffness() * (L - s / 2) / (L1 / 2 + L);
839  m_springs.at(segment - 1).addStiffness(-K);
840  }
841  spg.setStiffness(K);
842  } else // spring added on intermediate segment => interpolation
843  {
844  double L1 = m_needle.accessSegment(segment - 1).getParametricLength();
845  if (segment - 1 == 0)
846  L1 = 0;
847  double L = m_needle.accessSegment(segment).getParametricLength();
848  double L2 = m_needle.accessSegment(segment + 1).getParametricLength();
849  if (segment + 1 == m_needle.getNbSegments() - 1)
851  double K1 = m_springs.at(segment - 1).getStiffness() * l2 / (L1 + L);
852  double K2 = m_springs.at(segment).getStiffness() * s / (L + L2);
853  m_springs.at(segment - 1).addStiffness(-K1);
854  m_springs.at(segment).addStiffness(-K2);
855  spg.setStiffness(K1 + K2);
856  }
857 
858  this->addInsertionPointOnSegmentHard(segment, s);
859  m_springs.at(segment) = spg;
860 
861  return segment;
862 }
863 
865 {
866  if (m_needle.getNbSegments() == 1)
868  else
871 }
872 
874 {
875  if (last == -1)
876  last = first; // remove only one point
877 
878  if (first < 0 || (last < first) || last >= (int)m_springs.size())
879  throw vpException(
880  vpException::badValue,
881  "usNeedleInsertionModelVirtualSprings::removeInsertionPointHard: bad index, cannot remove spring");
882 
883  // Remark: erase doesn't erase the last element => add 1 to second argument
884  m_springs.erase(m_springs.begin() + first, m_springs.begin() + last + 1);
885 
886  double l = m_needle.accessSegment(first).getParametricLength();
887  for (int i = first + 1; i <= last + 1; i++) {
889  }
891  m_needle.removeSegments(first + 1, last + 1);
892 }
893 
895 {
896  this->removeInsertionPointsHard(m_springs.size() - 1);
897 }
898 
900 {
901  if (last == -1)
902  last = first; // remove only one point
903 
904  if (first < 0 || (last < first) || last >= (int)m_springs.size())
905  throw vpException(vpException::badValue,
906  "usNeedleInsertionModelVirtualSprings::removeInsertionPoint: bad index, cannot remove spring");
907  if ((first == 0) && (last != ((int)m_springs.size() - 1)))
908  throw vpException(vpException::badValue, "usNeedleInsertionModelVirtualSprings::removeInsertionPoint: cannot "
909  "remove first insertion point without removing all insertion points");
910 
911  // Remark: erase doesn't erase the last element => add 1 to second argument
912 
913  // if the last spring is not removed we can simply fusion the springs
914  if (last < (int)m_springs.size() - 1) {
915  this->fusionSprings(first - 1, last + 1);
916  }
917  // else if all springs must be removed
918  else if (first == 0) {
919  for (int i = first; i <= last; i++) {
920  if (!m_springs.at(i).IsPositionUpdateAllowed())
921  m_inactiveMeasureSprings.push_back(m_springs.at(i));
922  }
923  this->removeInsertionPointsHard(first, last);
924  }
925  // else we can fusion springs and remove the last
926  else {
927  if (last > first)
928  this->fusionSprings(first - 1, last);
929 
930  double L1 = m_needle.accessSegment(m_needle.getNbSegments() - 3)
931  .getParametricLength(); // length of tissue before last spring that will remain
932  double L = m_needle.accessSegment(m_needle.getNbSegments() - 2)
933  .getParametricLength(); // length of tissue before current last spring
935  m_bevelLength; // length of tissue after current last spring
936  if (m_springs.size() == 2)
937  L1 = 0;
938  m_springs.at(m_springs.size() - 2)
939  .setStiffness((m_springs.at(m_springs.size() - 2).getStiffness() + m_springs.back().getStiffness()) *
940  (L1 / 2 + L2) / (L1 / 2 + L + L2));
941 
942  if (!m_springs.back().IsPositionUpdateAllowed())
943  m_inactiveMeasureSprings.push_back(m_springs.back());
945  }
946 }
947 
949 {
950  this->removeInsertionPoints(m_springs.size() - 1);
951 }
952 
954 {
955  for (int spg = m_springs.size() - 1; spg > 0; spg--) {
956  if (m_springs.at(spg).IsPositionUpdateAllowed())
957  this->removeInsertionPoints(spg);
958  }
959  m_tipSpringsIndex = m_springs.size() - 1;
960 }
961 
962 void usNeedleInsertionModelVirtualSprings::fusionSprings(int firstSpring, int lastSpring)
963 {
964  int nbSprings = lastSpring - firstSpring + 1;
965  if (firstSpring < 0 || nbSprings < 3 || lastSpring >= (int)m_springs.size())
966  throw vpException(vpException::badValue,
967  "usNeedleInsertionModelVirtualSprings::fusionSprings: bad springs indexes");
968 
969  if (nbSprings == 3) {
970  double L1 = m_needle.accessSegment(firstSpring).getParametricLength();
971  if (firstSpring == 0)
972  L1 = 0;
973  double l = m_needle.accessSegment(firstSpring + 1).getParametricLength();
974  double l2 = m_needle.accessSegment(lastSpring).getParametricLength();
975  double L2 = m_needle.accessSegment(lastSpring + 1).getParametricLength();
976 
977  if (lastSpring == (int)m_springs.size() - 1)
979 
980  double K0 = m_springs.at(firstSpring).getStiffness();
981  double K1 = m_springs.at(firstSpring + 1).getStiffness();
982  double K2 = m_springs.at(lastSpring).getStiffness();
983  double w0 = K0 * l2 / (L1 + l);
984  double w2 = K2 * l / (l2 + L2);
985 
986  m_springs.at(firstSpring).addStiffness(K1 * w0 / (w0 + w2));
987  m_springs.at(lastSpring).addStiffness(K1 * w2 / (w0 + w2));
988 
989  if (!m_springs.at(firstSpring + 1).IsPositionUpdateAllowed())
990  m_inactiveMeasureSprings.push_back(m_springs.at(firstSpring + 1));
991  this->removeInsertionPointsHard(firstSpring + 1);
992  } else {
993  int middle = (firstSpring + lastSpring) / 2;
994  this->fusionSprings(middle, lastSpring);
995  this->fusionSprings(firstSpring, middle + 1);
996  }
997 }
998 
1000 {
1001  for (unsigned int i = 0; i < m_springs.size(); i++) {
1002  double L1 = m_needle.accessSegment(i).getParametricLength() / 2;
1003  double L2 = m_needle.accessSegment(i + 1).getParametricLength() / 2;
1004 
1005  if (i == 0)
1006  L1 = 0;
1007  if ((int)i + 1 == (int)m_needle.getNbSegments() - 1)
1008  L2 = m_needle.accessLastSegment().getParametricLength(); //+m_bevelLength;
1009 
1010  m_springs.at(i).setStiffness(m_stiffnessPerUnitLength * (L1 + L2));
1011  }
1012 }
1013 
1015 {
1016  if (m_springs.size() > 0) {
1017  vpColVector z = m_springs.back().getDirection();
1018  if (m_springs.size() > 1)
1019  z = (m_springs.back().getPosition() - m_springs.at(m_springs.size() - 2).getPosition()).normalize();
1020 
1021  vpHomogeneousMatrix worldMtip(m_needle.getWorldMtip());
1022  vpColVector ztip(worldMtip.getCol(2), 0, 3);
1023  vpColVector xtip(worldMtip.getCol(0), 0, 3);
1024 
1025  ztip = (ztip - vpColVector::dotProd(ztip, xtip) * xtip).normalize();
1026  z = (z - vpColVector::dotProd(z, xtip) * xtip).normalize();
1027 
1028  double vect = vpColVector::crossProd(z, ztip).frobeniusNorm();
1029  double dot = vpColVector::dotProd(z, ztip);
1030  m_cutAngle = 180 / M_PI * atan2(vect, dot);
1031  } else {
1032  m_cutAngle = 0;
1033  }
1034  // m_cutAngle = 0;
1035 }
1036 
1038 {
1039  double bevel_rad = atan2(m_needle.getOuterDiameter(), m_bevelLength);
1040  double cut_rad = M_PI / 180 * m_cutAngle;
1041 
1042  double a = m_needle.getOuterDiameter() / tan(bevel_rad);
1043  double b = m_needle.getOuterDiameter() / sin(bevel_rad);
1044 
1046  (a * a * tan(cut_rad) / 2 - b * b * cos(bevel_rad) * tan(bevel_rad - cut_rad) / 2);
1047 
1049  (-a * a * a * tan(cut_rad) / 6 +
1050  b * b * tan(bevel_rad - cut_rad) / 2 *
1051  (a * cos(bevel_rad) / 3 - m_needle.getOuterDiameter() * sin(bevel_rad) / 6));
1052 }
1053 
1055 {
1056  for (int i = 0; i < m_tipSpringsIndex; i++) {
1057  if (!m_springs.at(i).IsDirectionUpdateAllowed())
1058  continue;
1059  m_springs.at(i).setDirection(m_needle.accessSegment(i).getEndTangent());
1060  }
1061 
1062  for (unsigned int i = m_tipSpringsIndex; i < m_springs.size(); i++) {
1063  if (!m_springs.at(i).IsDirectionUpdateAllowed())
1064  continue;
1065  m_springs.at(i).setDirection(m_needle.accessSegment(i).getEndTangent());
1066  }
1067 }
1068 
1070 {
1071 #ifdef VISP_HAVE_EIGEN3
1072 
1073  int nbSeg = m_needle.getNbSegments();
1074 
1075  // Set system to solve :
1076  //
1077  // M * a = b
1078  //
1079  // a : 12 nbSeg parameters
1080  // b : 12 nbSeg constraints
1081  // M : 12 nbSeg x 12 nbSeg matrix
1082  //
1083 
1084  typedef Eigen::Triplet<double> T;
1085  std::vector<T> L;
1086  L.reserve(144 / 10 * nbSeg * nbSeg);
1087 
1088  Eigen::VectorXd a = Eigen::VectorXd::Zero(12 * nbSeg);
1089  Eigen::VectorXd b = Eigen::VectorXd::Zero(12 * nbSeg);
1090 
1091  int line = 0;
1092  int col = 0;
1093 
1094  double EI = m_needle.getEI();
1095  vpPoseVector basePose(m_needle.getBasePose());
1096  vpHomogeneousMatrix worldMbase(m_needle.getWorldMbase());
1097  vpHomogeneousMatrix worldMtip(m_needle.getWorldMtip());
1098 
1099  // Continuity
1100  for (int n = 0; n < nbSeg - 1; n++) {
1101  double s = m_needle.accessSegment(n).getParametricLength();
1102 
1103  for (int dim = 0; dim < 3; dim++) {
1104  // Order 0 (continuity) : 3 * (nbSeg-1) constraints
1105  col = 12 * n + 4 * dim;
1106  L.push_back(T(line, col, 1));
1107  L.push_back(T(line, col + 1, s));
1108  L.push_back(T(line, col + 2, s * s));
1109  L.push_back(T(line, col + 3, s * s * s));
1110 
1111  col = 12 * (n + 1) + 4 * dim;
1112  L.push_back(T(line, col, -1));
1113 
1114  line++;
1115 
1116  // Order 1 (continuity of first derivative) : 3 * (nbSeg-1) constraints
1117  col = 12 * n + 4 * dim;
1118 
1119  L.push_back(T(line, col + 1, 1));
1120  L.push_back(T(line, col + 2, 2 * s));
1121  L.push_back(T(line, col + 3, 3 * s * s));
1122 
1123  col = 12 * (n + 1) + 4 * dim;
1124  L.push_back(T(line, col + 1, -1));
1125 
1126  line++;
1127 
1128  // Order 2 (continuity of second derivative) : 3 * (nbSeg-1) constraints
1129  col = 12 * n + 4 * dim;
1130 
1131  L.push_back(T(line, col + 2, 2));
1132  L.push_back(T(line, col + 3, 6 * s));
1133 
1134  col = 12 * (n + 1) + 4 * dim;
1135  L.push_back(T(line, col + 2, -2));
1136 
1137  line++;
1138  }
1139  }
1140 
1141  vpColVector tipForceVector(3, 0);
1142  vpColVector tipMomentVector(3, 0);
1143 
1144  if (m_needle.getNbSegments() > 1 && m_IsInserting) {
1145  vpColVector xtip(3);
1146  xtip[0] = worldMtip[0][0];
1147  xtip[1] = worldMtip[1][0];
1148  xtip[2] = worldMtip[2][0];
1149 
1150  vpColVector z = m_springs.back().getDirection();
1151  vpColVector y = vpColVector::crossProd(z, xtip);
1152  y.normalize();
1153  tipForceVector = -y;
1154  tipMomentVector = -y;
1155 
1156  tipForceVector *= m_tipForce;
1157  tipMomentVector *= m_tipMoment;
1158  }
1159 
1160  for (int dim = 0; dim < 3; dim++) {
1161  // Base conditions : 6 constraints
1162  col = 4 * dim;
1163 
1164  L.push_back(T(line, col, 1));
1165  b(line) = basePose[dim];
1166  line++;
1167 
1168  L.push_back(T(line, col + 1, 1));
1169  b(line) = worldMbase[dim][2];
1170  line++;
1171 
1172  // Tip conditions : 6 constraints
1173  col = 12 * (nbSeg - 1) + 4 * dim;
1174 
1175  L.push_back(T(line, col + 3, 6 * EI));
1176  b(line) = -tipForceVector[dim];
1177 
1178  line++;
1179 
1180  L.push_back(T(line, col + 3, 6 * m_needle.accessLastSegment().getParametricLength() * EI));
1181  L.push_back(T(line, col + 2, 2 * EI));
1182  b(line) = -tipMomentVector[dim];
1183  line++;
1184  }
1185 
1186  // Springs : 3 * (nbSeg-1) constraints
1187  for (int n = 0; n < nbSeg - 1; n++) {
1188  // Force calculation
1189  double px_n = m_springs.at(n).getPosition()[0];
1190  double py_n = m_springs.at(n).getPosition()[1];
1191  double pz_n = m_springs.at(n).getPosition()[2];
1192  double dx_n = m_springs.at(n).getDirection()[0];
1193  double dy_n = m_springs.at(n).getDirection()[1];
1194  double dz_n = m_springs.at(n).getDirection()[2];
1195 
1196  // Build an orthonormal base for the plan normal to the spring direction
1197  vpColVector n1m_n(3);
1198  if ((fabs(dx_n) > std::numeric_limits<double>::epsilon()) ||
1199  (fabs(dy_n) > std::numeric_limits<double>::epsilon())) {
1200  vpColVector z(3);
1201  z[0] = 0;
1202  z[1] = 0;
1203  z[2] = 1;
1204  n1m_n = vpColVector::crossProd(m_springs.at(n).getDirection(), z);
1205  } else {
1206  vpColVector y(3);
1207  y[0] = 0;
1208  y[1] = 1;
1209  y[2] = 0;
1210  n1m_n = vpColVector::crossProd(m_springs.at(n).getDirection(), y);
1211  }
1212  n1m_n.normalize();
1213 
1214  double n1x_n = n1m_n[0];
1215  double n1y_n = n1m_n[1];
1216  double n1z_n = n1m_n[2];
1217 
1218  vpColVector n2m_n = vpColVector::cross(m_springs.at(n).getDirection(), n1m_n);
1219  n2m_n.normalize();
1220 
1221  double n2x_n = n2m_n[0];
1222  double n2y_n = n2m_n[1];
1223  double n2z_n = n2m_n[2];
1224 
1225  col = 12 * n;
1226 
1227  L.push_back(T(line, col + 3, 6 * EI * n1x_n));
1228  L.push_back(T(line, col + 3 + 4, 6 * EI * n1y_n));
1229  L.push_back(T(line, col + 3 + 8, 6 * EI * n1z_n));
1230  L.push_back(T(line + 1, col + 3, 6 * EI * n2x_n));
1231  L.push_back(T(line + 1, col + 3 + 4, 6 * EI * n2y_n));
1232  L.push_back(T(line + 1, col + 3 + 8, 6 * EI * n2z_n));
1233  for (int i = 0; i < nbSeg - 1 - n; i++) {
1234  double K = m_springs.at(n + i).getStiffness();
1235  vpColVector p = m_springs.at(n + i).getPosition();
1236  double dot1 = vpColVector::dotProd(p, n1m_n);
1237  L.push_back(T(line, col + 12 * (i + 1), -K * n1x_n));
1238  L.push_back(T(line, col + 12 * (i + 1) + 4, -K * n1y_n));
1239  L.push_back(T(line, col + 12 * (i + 1) + 8, -K * n1z_n));
1240  b(line) += -K * dot1;
1241 
1242  double dot2 = vpColVector::dotProd(p, n2m_n);
1243  L.push_back(T(line + 1, col + 12 * (i + 1), -K * n2x_n));
1244  L.push_back(T(line + 1, col + 12 * (i + 1) + 4, -K * n2y_n));
1245  L.push_back(T(line + 1, col + 12 * (i + 1) + 8, -K * n2z_n));
1246  b(line + 1) += -K * dot2;
1247  }
1248  b(line) += -vpColVector::dotProd(tipForceVector, n1m_n);
1249  b(line + 1) += -vpColVector::dotProd(tipForceVector, n2m_n);
1250 
1251  line += 2;
1252 
1253  // Points stay in the normal plan of the spring
1254  col = 12 * (n + 1);
1255  double dotProd = dx_n * px_n + dy_n * py_n + dz_n * pz_n;
1256 
1257  L.push_back(T(line, col, dx_n));
1258  L.push_back(T(line, col + 4, dy_n));
1259  L.push_back(T(line, col + 8, dz_n));
1260  b(line) = dotProd;
1261  line++;
1262  }
1263 
1264  Eigen::SparseMatrix<double> M(12 * nbSeg, 12 * nbSeg);
1265  M.setFromTriplets(L.begin(), L.end());
1266 
1267  Eigen::SparseLU<Eigen::SparseMatrix<double> > solver;
1268  solver.compute(M);
1269 
1270  a = solver.solve(b);
1271 
1272  // Update needle parameters
1273  vpMatrix coef(3, 4);
1274  for (int n = 0; n < nbSeg; n++) {
1275 
1276  for (int dim = 0; dim < 3; dim++) {
1277  int seg = 12 * n + 4 * dim;
1278  coef[dim][0] = a(seg);
1279  coef[dim][1] = a(seg + 1);
1280  coef[dim][2] = a(seg + 2);
1281  coef[dim][3] = a(seg + 3);
1282 
1284  }
1285  }
1286 
1287  // Compute new tip pose
1288  vpRotationMatrix worldRbase(worldMbase);
1289 
1290  vpRotationMatrix worldRtip(worldRbase);
1291 
1292  for (int i = 0; i < m_needle.getNbSegments(); i++) {
1293  vpColVector vect =
1294  vpColVector::crossProd(m_needle.accessSegment(i).getStartTangent(), m_needle.accessSegment(i).getEndTangent());
1295  double dot =
1296  vpColVector::dotProd(m_needle.accessSegment(i).getStartTangent(), m_needle.accessSegment(i).getEndTangent());
1297  double theta = atan2(vect.frobeniusNorm(), dot);
1298  vect.normalize();
1299  vect = theta * vect;
1300  vpThetaUVector thetaU(vect[0], vect[1], vect[2]);
1301  vpRotationMatrix Ri(thetaU);
1302  worldRtip = Ri * worldRtip;
1303  }
1304 
1305  vpColVector t = m_needle.accessLastSegment().getEndPoint();
1306  vpTranslationVector translation(t[0], t[1], t[2]);
1307 
1308  vpPoseVector tipPose(translation, worldRtip);
1309  m_needle.setTipPose(tipPose);
1310 
1311 #else
1312  throw vpException(
1313  vpException::functionNotImplementedError,
1314  "usNeedleInsertionModelVirtualSprings::solveSegmentsParametersSparseEigen: not implemented without Eigen3");
1315 #endif
1316 }
1317 
1319 {
1320 #ifdef VISP_HAVE_OPENCV
1321 
1322  int nbSeg = m_needle.getNbSegments();
1323 
1324  // Set system to solve :
1325  //
1326  // M * a = b
1327  //
1328  // a : 12 nbSeg parameters
1329  // b : 12 nbSeg constraints
1330  // M : 12 nbSeg x 12 nbSeg matrix
1331  //
1332 
1333  cv::Mat M = cv::Mat::zeros(12 * nbSeg, 12 * nbSeg, CV_64F);
1334  cv::Mat b = cv::Mat::zeros(12 * nbSeg, 1, CV_64F);
1335  cv::Mat a = cv::Mat::zeros(12 * nbSeg, 1, CV_64F);
1336 
1337  int line = 0;
1338  int col = 0;
1339 
1340  double EI = m_needle.getEI();
1341  vpPoseVector basePose(m_needle.getBasePose());
1342  vpHomogeneousMatrix worldMbase(m_needle.getWorldMbase());
1343  vpHomogeneousMatrix worldMtip(m_needle.getWorldMtip());
1344 
1345  // Continuity
1346  for (int n = 0; n < nbSeg - 1; n++) {
1347  double s = m_needle.accessSegment(n).getParametricLength();
1348 
1349  for (int dim = 0; dim < 3; dim++) {
1350  // Order 0 (continuity) : 3 * (nbSeg-1) constraints
1351  col = 12 * n + 4 * dim;
1352  M.at<double>(line, col) = 1;
1353  M.at<double>(line, col + 1) = s;
1354  M.at<double>(line, col + 2) = s * s;
1355  M.at<double>(line, col + 3) = s * s * s;
1356 
1357  col = 12 * (n + 1) + 4 * dim;
1358  M.at<double>(line, col) = -1;
1359  M.at<double>(line, col + 1) = 0;
1360  M.at<double>(line, col + 2) = 0;
1361  M.at<double>(line, col + 3) = 0;
1362 
1363  line++;
1364 
1365  // Order 1 (continuity of first derivative) : 3 * (nbSeg-1) constraints
1366  col = 12 * n + 4 * dim;
1367 
1368  M.at<double>(line, col) = 0;
1369  M.at<double>(line, col + 1) = 1;
1370  M.at<double>(line, col + 2) = 2 * s;
1371  M.at<double>(line, col + 3) = 3 * s * s;
1372 
1373  col = 12 * (n + 1) + 4 * dim;
1374  M.at<double>(line, col) = 0;
1375  M.at<double>(line, col + 1) = -1;
1376  M.at<double>(line, col + 2) = 0;
1377  M.at<double>(line, col + 3) = 0;
1378 
1379  line++;
1380 
1381  // Order 2 (continuity of second derivative) : 3 * (nbSeg-1) constraints
1382  col = 12 * n + 4 * dim;
1383 
1384  M.at<double>(line, col) = 0;
1385  M.at<double>(line, col + 1) = 0;
1386  M.at<double>(line, col + 2) = 2;
1387  M.at<double>(line, col + 3) = 6 * s;
1388 
1389  col = 12 * (n + 1) + 4 * dim;
1390  M.at<double>(line, col) = 0;
1391  M.at<double>(line, col + 1) = 0;
1392  M.at<double>(line, col + 2) = -2;
1393  M.at<double>(line, col + 3) = 0;
1394 
1395  line++;
1396  }
1397  }
1398 
1399  vpColVector tipForceVector(3, 0);
1400  vpColVector tipMomentVector(3, 0);
1401 
1402  if (m_needle.getNbSegments() > 1 && m_IsInserting) {
1403  vpColVector xtip(3);
1404  xtip[0] = worldMtip[0][0];
1405  xtip[1] = worldMtip[1][0];
1406  xtip[2] = worldMtip[2][0];
1407 
1408  vpColVector z = m_springs.back().getDirection();
1409  vpColVector y = vpColVector::crossProd(z, xtip);
1410  y.normalize();
1411  tipForceVector = -y;
1412  tipMomentVector = -y;
1413 
1414  tipForceVector *= m_tipForce;
1415  tipMomentVector *= m_tipMoment;
1416  }
1417 
1418  for (int dim = 0; dim < 3; dim++) {
1419  // Base conditions : 6 constraints
1420  col = 4 * dim;
1421 
1422  M.at<double>(line, col) = 1;
1423  b.at<double>(line, 0) = basePose[dim];
1424  line++;
1425 
1426  M.at<double>(line, col + 1) = 1;
1427  b.at<double>(line, 0) = worldMbase[dim][2];
1428  line++;
1429 
1430  // Tip conditions : 6 constraints
1431  col = 12 * (nbSeg - 1) + 4 * dim;
1432 
1433  M.at<double>(line, col + 3) = 6 * EI;
1434  b.at<double>(line, 0) = -tipForceVector[dim];
1435 
1436  line++;
1437 
1438  M.at<double>(line, col + 3) = 6 * m_needle.accessLastSegment().getParametricLength();
1439  M.at<double>(line, col + 2) = 2;
1440  b.at<double>(line) = -tipMomentVector[dim];
1441  line++;
1442  }
1443 
1444  // Springs : 3 * (nbSeg-1) constraints
1445  for (int n = 0; n < nbSeg - 1; n++) {
1446  // Force calculation
1447  double px_n = m_springs.at(n).getPosition()[0];
1448  double py_n = m_springs.at(n).getPosition()[1];
1449  double pz_n = m_springs.at(n).getPosition()[2];
1450  double dx_n = m_springs.at(n).getDirection()[0];
1451  double dy_n = m_springs.at(n).getDirection()[1];
1452  double dz_n = m_springs.at(n).getDirection()[2];
1453 
1454  // Build an orthonormal base for the plan normal to the spring direction
1455  vpColVector n1m_n(3);
1456  if ((fabs(dx_n) > std::numeric_limits<double>::epsilon()) ||
1457  (fabs(dy_n) > std::numeric_limits<double>::epsilon())) {
1458  vpColVector z(3);
1459  z[0] = 0;
1460  z[1] = 0;
1461  z[2] = 1;
1462  n1m_n = vpColVector::crossProd(m_springs.at(n).getDirection(), z);
1463  } else {
1464  vpColVector y(3);
1465  y[0] = 0;
1466  y[1] = 1;
1467  y[2] = 0;
1468  n1m_n = vpColVector::crossProd(m_springs.at(n).getDirection(), y);
1469  }
1470  n1m_n.normalize();
1471 
1472  double n1x_n = n1m_n[0];
1473  double n1y_n = n1m_n[1];
1474  double n1z_n = n1m_n[2];
1475 
1476  vpColVector n2m_n = vpColVector::cross(m_springs.at(n).getDirection(), n1m_n);
1477  n2m_n.normalize();
1478 
1479  double n2x_n = n2m_n[0];
1480  double n2y_n = n2m_n[1];
1481  double n2z_n = n2m_n[2];
1482 
1483  col = 12 * n;
1484 
1485  M.at<double>(line, col + 3) = 6 * EI * n1x_n;
1486  M.at<double>(line, col + 3 + 4) = 6 * EI * n1y_n;
1487  M.at<double>(line, col + 3 + 8) = 6 * EI * n1z_n;
1488  M.at<double>(line + 1, col + 3) = 6 * EI * n2x_n;
1489  M.at<double>(line + 1, col + 3 + 4) = 6 * EI * n2y_n;
1490  M.at<double>(line + 1, col + 3 + 8) = 6 * EI * n2z_n;
1491  for (int i = 0; i < nbSeg - 1 - n; i++) {
1492  double K = m_springs.at(n + i).getStiffness();
1493  vpColVector p = m_springs.at(n + i).getPosition();
1494 
1495  M.at<double>(line, col + 12 * (i + 1)) = -K * n1x_n;
1496  M.at<double>(line, col + 12 * (i + 1) + 4) = -K * n1y_n;
1497  M.at<double>(line, col + 12 * (i + 1) + 8) = -K * n1z_n;
1498  b.at<double>(line, 0) += -K * (p[0] * n1x_n + p[1] * n1y_n + p[2] * n1z_n);
1499 
1500  M.at<double>(line + 1, col + 12 * (i + 1)) = -K * n2x_n;
1501  M.at<double>(line + 1, col + 12 * (i + 1) + 4) = -K * n2y_n;
1502  M.at<double>(line + 1, col + 12 * (i + 1) + 8) = -K * n2z_n;
1503  b.at<double>(line + 1, 0) += -K * (p[0] * n2x_n + p[1] * n2y_n + p[2] * n2z_n);
1504  }
1505  b.at<double>(line, 0) += -vpColVector::dotProd(tipForceVector, n1m_n);
1506  b.at<double>(line + 1, 0) += -vpColVector::dotProd(tipForceVector, n2m_n);
1507  line += 2;
1508 
1509  // Points stay in the normal plan of the spring
1510  col = 12 * (n + 1);
1511  double dotProd = dx_n * px_n + dy_n * py_n + dz_n * pz_n;
1512 
1513  M.at<double>(line, col) = dx_n;
1514  M.at<double>(line, col + 4) = dy_n;
1515  M.at<double>(line, col + 8) = dz_n;
1516  b.at<double>(line, 0) = dotProd;
1517  line++;
1518  }
1519 
1520  // Solve system
1521 
1522  cv::solve(M, b, a);
1523 
1524  // Update needle parameters
1525  vpMatrix coef(3, 4);
1526  for (int n = 0; n < nbSeg; n++) {
1527 
1528  for (int dim = 0; dim < 3; dim++) {
1529  int seg = 12 * n + 4 * dim;
1530  coef[dim][0] = a.at<double>(seg);
1531  coef[dim][1] = a.at<double>(seg + 1);
1532  coef[dim][2] = a.at<double>(seg + 2);
1533  coef[dim][3] = a.at<double>(seg + 3);
1534 
1536  }
1537  }
1538 
1539  // Compute new tip pose
1540  vpRotationMatrix worldRbase(worldMbase);
1541 
1542  vpRotationMatrix worldRtip(worldRbase);
1543 
1544  for (int i = 0; i < m_needle.getNbSegments(); i++) {
1545  vpColVector vect =
1546  vpColVector::crossProd(m_needle.accessSegment(i).getStartTangent(), m_needle.accessSegment(i).getEndTangent());
1547  double dot =
1548  vpColVector::dotProd(m_needle.accessSegment(i).getStartTangent(), m_needle.accessSegment(i).getEndTangent());
1549  double theta = atan2(vect.frobeniusNorm(), dot);
1550  vect.normalize();
1551  vect = theta * vect;
1552  vpThetaUVector thetaU(vect[0], vect[1], vect[2]);
1553  vpRotationMatrix Ri(thetaU);
1554  worldRtip = Ri * worldRtip;
1555  }
1556 
1557  vpColVector t = m_needle.accessLastSegment().getEndPoint();
1558  vpTranslationVector translation(t[0], t[1], t[2]);
1559 
1560  vpPoseVector tipPose(translation, worldRtip);
1561  m_needle.setTipPose(tipPose);
1562 
1563 #else
1564  throw vpException(
1565  vpException::functionNotImplementedError,
1566  "usNeedleInsertionModelVirtualSprings::solveSegmentsParametersOpenCV: not implemented without OpenCV");
1567 #endif
1568 }
1569 
1571 {
1572  int nbSeg = m_needle.getNbSegments();
1573 
1574  // Set system to solve :
1575  //
1576  // M * a = b
1577  //
1578  // a : 12 nbSeg parameters
1579  // b : 12 nbSeg constraints
1580  // M : 12 nbSeg x 12 nbSeg matrix
1581  //
1582 
1583  vpMatrix M(12 * nbSeg, 12 * nbSeg, 0);
1584  vpColVector a(12 * nbSeg, 0);
1585  vpColVector b(12 * nbSeg, 0);
1586 
1587  int line = 0;
1588  int col = 0;
1589 
1590  double EI = m_needle.getEI();
1591  vpPoseVector basePose(m_needle.getBasePose());
1592  vpHomogeneousMatrix worldMbase(m_needle.getWorldMbase());
1593  vpHomogeneousMatrix worldMtip(m_needle.getWorldMtip());
1594 
1595  // Continuity
1596  for (int n = 0; n < nbSeg - 1; n++) {
1597  double s = m_needle.accessSegment(n).getParametricLength();
1598 
1599  for (int dim = 0; dim < 3; dim++) {
1600  // Order 0 (continuity) : 3 * (nbSeg-1) constraints
1601  col = 12 * n + 4 * dim;
1602  M[line][col] = 1;
1603  M[line][col + 1] = s;
1604  M[line][col + 2] = s * s;
1605  M[line][col + 3] = s * s * s;
1606 
1607  col = 12 * (n + 1) + 4 * dim;
1608  M[line][col] = -1;
1609  M[line][col + 1] = 0;
1610  M[line][col + 2] = 0;
1611  M[line][col + 3] = 0;
1612 
1613  line++;
1614 
1615  // Order 1 (continuity of first derivative) : 3 * (nbSeg-1) constraints
1616  col = 12 * n + 4 * dim;
1617 
1618  M[line][col] = 0;
1619  M[line][col + 1] = 1;
1620  M[line][col + 2] = 2 * s;
1621  M[line][col + 3] = 3 * s * s;
1622 
1623  col = 12 * (n + 1) + 4 * dim;
1624  M[line][col] = 0;
1625  M[line][col + 1] = -1;
1626  M[line][col + 2] = 0;
1627  M[line][col + 3] = 0;
1628 
1629  line++;
1630 
1631  // Order 2 (continuity of second derivative) : 3 * (nbSeg-1) constraints
1632  col = 12 * n + 4 * dim;
1633 
1634  M[line][col] = 0;
1635  M[line][col + 1] = 0;
1636  M[line][col + 2] = 2;
1637  M[line][col + 3] = 6 * s;
1638 
1639  col = 12 * (n + 1) + 4 * dim;
1640  M[line][col] = 0;
1641  M[line][col + 1] = 0;
1642  M[line][col + 2] = -2;
1643  M[line][col + 3] = 0;
1644 
1645  line++;
1646  }
1647  }
1648 
1649  vpColVector tipForceVector(3, 0);
1650  vpColVector tipMomentVector(3, 0);
1651 
1652  if (m_needle.getNbSegments() > 1 && m_IsInserting) {
1653  vpColVector xtip(3);
1654  xtip[0] = worldMtip[0][0];
1655  xtip[1] = worldMtip[1][0];
1656  xtip[2] = worldMtip[2][0];
1657 
1658  vpColVector z = m_springs.back().getDirection();
1659  vpColVector y = vpColVector::crossProd(z, xtip);
1660  y.normalize();
1661  tipForceVector = -y;
1662  tipMomentVector = -y;
1663 
1664  tipForceVector *= m_tipForce;
1665  tipMomentVector *= m_tipMoment;
1666  }
1667 
1668  for (int dim = 0; dim < 3; dim++) {
1669  // Base conditions : 6 constraints
1670  col = 4 * dim;
1671 
1672  M[line][col] = 1;
1673  b[line] = basePose[dim];
1674  line++;
1675 
1676  M[line][col + 1] = 1;
1677  b[line] = worldMbase[dim][2];
1678  line++;
1679 
1680  // Tip conditions : 6 constraints
1681  col = 12 * (nbSeg - 1) + 4 * dim;
1682 
1683  M[line][col + 3] = 6 * EI;
1684  b[line] = -tipForceVector[dim];
1685 
1686  line++;
1687 
1688  M[line][col + 3] = 6 * m_needle.accessLastSegment().getParametricLength() * EI;
1689  M[line][col + 2] = 2 * EI;
1690  b[line] = -tipMomentVector[dim];
1691  line++;
1692  }
1693 
1694  // Springs : 3 * (nbSeg-1) constraints
1695  for (int n = 0; n < nbSeg - 1; n++) {
1696  // Force calculation
1697  double px_n = m_springs.at(n).getPosition()[0];
1698  double py_n = m_springs.at(n).getPosition()[1];
1699  double pz_n = m_springs.at(n).getPosition()[2];
1700  double dx_n = m_springs.at(n).getDirection()[0];
1701  double dy_n = m_springs.at(n).getDirection()[1];
1702  double dz_n = m_springs.at(n).getDirection()[2];
1703 
1704  // Build an orthonormal base for the plan normal to the spring direction
1705  vpColVector n1m_n(3);
1706  if ((fabs(dx_n) > std::numeric_limits<double>::epsilon()) ||
1707  (fabs(dy_n) > std::numeric_limits<double>::epsilon())) {
1708  vpColVector z(3);
1709  z[0] = 0;
1710  z[1] = 0;
1711  z[2] = 1;
1712  n1m_n = vpColVector::crossProd(m_springs.at(n).getDirection(), z);
1713  } else {
1714  vpColVector y(3);
1715  y[0] = 0;
1716  y[1] = 1;
1717  y[2] = 0;
1718  n1m_n = vpColVector::crossProd(m_springs.at(n).getDirection(), y);
1719  }
1720  n1m_n.normalize();
1721 
1722  double n1x_n = n1m_n[0];
1723  double n1y_n = n1m_n[1];
1724  double n1z_n = n1m_n[2];
1725 
1726  vpColVector n2m_n = vpColVector::cross(m_springs.at(n).getDirection(), n1m_n);
1727  n2m_n.normalize();
1728 
1729  double n2x_n = n2m_n[0];
1730  double n2y_n = n2m_n[1];
1731  double n2z_n = n2m_n[2];
1732 
1733  col = 12 * n;
1734 
1735  M[line][col + 3] = 6 * EI * n1x_n;
1736  M[line][col + 3 + 4] = 6 * EI * n1y_n;
1737  M[line][col + 3 + 8] = 6 * EI * n1z_n;
1738  M[line + 1][col + 3] = 6 * EI * n2x_n;
1739  M[line + 1][col + 3 + 4] = 6 * EI * n2y_n;
1740  M[line + 1][col + 3 + 8] = 6 * EI * n2z_n;
1741  for (int i = 0; i < nbSeg - 1 - n; i++) {
1742  double K = m_springs.at(n + i).getStiffness();
1743  vpColVector p = m_springs.at(n + i).getPosition();
1744  double dot1 = vpColVector::dotProd(p, n1m_n);
1745  M[line][col + 12 * (i + 1)] = -K * n1x_n;
1746  M[line][col + 12 * (i + 1) + 4] = -K * n1y_n;
1747  M[line][col + 12 * (i + 1) + 8] = -K * n1z_n;
1748  b[line] += -K * dot1;
1749 
1750  double dot2 = vpColVector::dotProd(p, n2m_n);
1751  M[line + 1][col + 12 * (i + 1)] = -K * n2x_n;
1752  M[line + 1][col + 12 * (i + 1) + 4] = -K * n2y_n;
1753  M[line + 1][col + 12 * (i + 1) + 8] = -K * n2z_n;
1754  b[line + 1] += -K * dot2;
1755  }
1756  b[line] += -vpColVector::dotProd(tipForceVector, n1m_n);
1757  b[line + 1] += -vpColVector::dotProd(tipForceVector, n2m_n);
1758 
1759  line += 2;
1760 
1761  // Points stay in the normal plan of the spring
1762  col = 12 * (n + 1);
1763  double dotProd = dx_n * px_n + dy_n * py_n + dz_n * pz_n;
1764 
1765  M[line][col] = dx_n;
1766  M[line][col + 4] = dy_n;
1767  M[line][col + 8] = dz_n;
1768  b[line] = dotProd;
1769  line++;
1770  }
1771 
1772  // Solve system
1773 
1774  vpMatrix Minv = M.inverseByLU();
1775  a = Minv * b;
1776 
1777  // Update needle parameters
1778  vpMatrix coef(3, 4);
1779  for (int n = 0; n < nbSeg; n++) {
1780 
1781  for (int dim = 0; dim < 3; dim++) {
1782  int seg = 12 * n + 4 * dim;
1783  coef[dim][0] = a[seg];
1784  coef[dim][1] = a[seg + 1];
1785  coef[dim][2] = a[seg + 2];
1786  coef[dim][3] = a[seg + 3];
1787 
1789  }
1790  }
1791 
1792  // Compute new tip pose
1793  vpRotationMatrix worldRbase(worldMbase);
1794 
1795  vpRotationMatrix worldRtip(worldRbase);
1796 
1797  for (int i = 0; i < m_needle.getNbSegments(); i++) {
1798  vpColVector vect =
1799  vpColVector::crossProd(m_needle.accessSegment(i).getStartTangent(), m_needle.accessSegment(i).getEndTangent());
1800  double dot =
1801  vpColVector::dotProd(m_needle.accessSegment(i).getStartTangent(), m_needle.accessSegment(i).getEndTangent());
1802  double theta = atan2(vect.frobeniusNorm(), dot);
1803  vect.normalize();
1804  vect = theta * vect;
1805  vpThetaUVector thetaU(vect[0], vect[1], vect[2]);
1806  vpRotationMatrix Ri(thetaU);
1807  worldRtip = Ri * worldRtip;
1808  }
1809 
1810  vpColVector t = m_needle.accessLastSegment().getEndPoint();
1811  vpTranslationVector translation(t[0], t[1], t[2]);
1812 
1813  vpPoseVector tipPose(translation, worldRtip);
1814  m_needle.setTipPose(tipPose);
1815 }
1816 
1818 {
1819 #if defined(VISP_HAVE_EIGEN3)
1821 #elif defined(VISP_HAVE_OPENCV)
1823 #else
1825 #endif
1826 }
1827 
1829 {
1830  // Go along each segment and update the value of the segment length between successive springs
1831  // Except for the last segment (need to use AddRemoveSprings)
1832  // If no insertion points, the length of the needle is known
1833  if (m_springs.size() == 0) {
1836  return;
1837  }
1838 
1839  for (int i = 0; i < m_needle.getNbSegments() - 1; i++) {
1840  double l = m_needle.accessSegment(i).getLength();
1842  }
1843 }
1844 
1846 {
1847  // If the tip is reached before the last spring, springs need to be removed
1848  // If the last segment length is higher than the specified distance between springs, a spring is added
1849 
1850  // If needle is not yet inserted
1851  if (m_springs.size() == 0) {
1852  // If the tissue surface has been defined
1853  if (m_tissueSurface.getDirection().frobeniusNorm() != 0) {
1854  double s = m_needle.getFullLength();
1857 
1858  if ((s >= 0) && (s < m_needle.getFullLength() - m_interTipSpringDistance)) {
1861  return true;
1862  } else {
1865  return false;
1866  }
1867  } else {
1870  return false;
1871  }
1872  }
1873 
1874  double totalLength = m_needle.accessSegment(0).getParametricLength();
1875  int nbTipSprings = 0;
1876  int segment = 1;
1877 
1878  while ((totalLength < m_needle.getFullLength()) && (nbTipSprings <= m_nbMaxTipSprings) &&
1879  (segment < (m_needle.getNbSegments() - 1))) {
1880  totalLength += m_needle.accessSegment(segment).getParametricLength();
1881  if (segment >= m_tipSpringsIndex)
1882  nbTipSprings++;
1883  segment++;
1884  }
1885 
1886  // if we stopped because total length has been reached
1887  // it means that the needle was retracted and we need to remove the last springs
1888  if (totalLength >= m_needle.getFullLength()) {
1889  // if the needle is still inserted
1890  if (segment - 1 > 0) {
1891  double lastSegmentLength =
1892  m_needle.getFullLength() - (totalLength - m_needle.accessSegment(segment - 1).getParametricLength());
1893  int lastValidSpringIndex = segment - 2;
1894 
1896  // Add spring at needle tip if it is not on the current last segment
1897  if ((lastValidSpringIndex < m_tipSpringsIndex) && (lastSegmentLength > m_interTipSpringDistance)) {
1898  this->addInsertionPointOnSegment(lastValidSpringIndex + 1, lastSegmentLength - m_interTipSpringDistance);
1899  lastValidSpringIndex++;
1902  }
1903 
1904  // Add springs to reach the minimum number of tip springs (to avoid large discontinuity when removing last
1905  // springs)
1906  if (m_tipSpringsIndex > lastValidSpringIndex)
1907  m_tipSpringsIndex = lastValidSpringIndex;
1908  while ((lastValidSpringIndex - m_tipSpringsIndex + 1 < m_nbMinTipSprings) && (m_tipSpringsIndex > 0)) {
1910  if (l > 1.5 * m_interTipSpringDistance) {
1912  lastValidSpringIndex++;
1913  } else
1915  }
1916  } else
1917  m_tipSpringsIndex = lastValidSpringIndex;
1918 
1919  // Remove all springs where the needle is not anymore
1920  this->removeInsertionPoints(lastValidSpringIndex + 1, m_springs.size() - 1);
1921  }
1922  // else the needle has been fully removed
1923  else {
1924  this->removeInsertionPoints(0, m_springs.size() - 1);
1926  m_tipSpringsIndex = 0;
1927  }
1928 
1930  return true;
1931  }
1932  // if we stopped because tip length is higher than the threshold
1933  // it means that we can remove a spring that is far from the tip
1934  else if (nbTipSprings > m_nbMaxTipSprings) {
1935  // Keep spring if it leads to a segment that is too long or if it is a measure spring and increase tip spring index
1936  // instead
1940  (!m_springs.at(m_tipSpringsIndex).IsPositionUpdateAllowed())) {
1941  // this->fusionSprings(m_tipSpringsIndex, m_tipSpringsIndex+2);
1943  }
1944  // Remove beginning of the tip
1945  else
1947  return true;
1948  }
1949  // if we reached the last segment we can deduce its length
1950  else {
1951  double lastSegmentLength = m_needle.getFullLength() - totalLength;
1952 
1953  // If the last segment is too long
1954  if (m_AutomaticSpringAddition && (lastSegmentLength > m_interTipSpringDistance)) {
1955  // If its length corresponds to the curvilinear parameter we can add a spring (last segment has been measured and
1956  // the system recomputed)
1958  m_needle.accessLastSegment().setParametricLength(lastSegmentLength);
1960  return true;
1961  }
1962  // Else we update its length and the system must be recomputed
1963  else {
1964  m_needle.accessLastSegment().setParametricLength(lastSegmentLength);
1966  return true;
1967  }
1968  }
1969  // Else we can update its length
1970  else {
1971  m_needle.accessLastSegment().setParametricLength(lastSegmentLength);
1973  return false;
1974  }
1975  }
1976 }
1977 
1979 {
1980  std::vector<int> activated;
1981 
1982  for (unsigned int spg = 0; spg < m_inactiveMeasureSprings.size(); spg++) {
1983  for (int seg = 0; seg < m_needle.getNbSegments(); seg++) {
1985  activated.push_back(spg);
1986  break;
1987  }
1988  }
1989  }
1990  for (int i = activated.size() - 1; i >= 0; i--) {
1991  int j = this->addInsertionPoint(m_inactiveMeasureSprings.at(activated.at(i)));
1992  if (j <= m_tipSpringsIndex)
1994  m_inactiveMeasureSprings.erase(m_inactiveMeasureSprings.begin() + activated.at(i),
1995  m_inactiveMeasureSprings.begin() + activated.at(i) + 1);
1996  }
1997  return (activated.size() != 0);
1998 }
1999 
2000 void usNeedleInsertionModelVirtualSprings::addMeasureSpring(const vpColVector &p, const vpColVector &d)
2001 {
2002  if (p.size() != 3 || d.size() != 3)
2003  throw vpException(vpException::dimensionError,
2004  "usNeedleInsertionModelVirtualSprings::addMeasureSpring: invalid vector dimension");
2005 
2006  if (m_tissueSurface.getDirection().frobeniusNorm() != 0 &&
2008  std::cerr
2009  << "Warning in usNeedleInsertionModelVirtualSprings::addMeasureSpring: cannot add spring outside of tissue"
2010  << std::endl;
2011  return;
2012  }
2013 
2014  usVirtualSpring spg(p, d, 0);
2015  spg.AllowPositionUpdate(false);
2016  spg.AllowDirectionUpdate(true);
2017  spg.AllowStiffnessUpdate(true);
2018 
2019  m_inactiveMeasureSprings.push_back(spg);
2020 }
2021 
2023 {
2024  // Get needle length before motion
2025  double l = m_needle.accessSegment(0).getParametricLength();
2026 
2027  // Solve system assuming same insertion direction as previous one
2028  bool needRecomputation = false;
2029 
2030  this->updateCutAngle();
2031  this->updateInsertionDirections();
2032  this->updateTipForce();
2033  this->updateSpringsStiffness();
2034  this->solveSegmentsParameters(); // solve the system
2035  m_LastSegmentLengthComputed = false; // set flag to specify that the curvilinear parameter of the last segment does
2036  // certainly not correspond to the length
2037  this->computeSegmentsLengths(); // measure the length of each segment except the last one
2038  needRecomputation = this->addRemoveSprings();
2039 
2040  // Check insertion direction (insertion and removal)
2042  m_IsInserting = true;
2044  m_IsInserting = false;
2045  else if (m_springs.size() > 0) {
2046  double insertionStep = l - m_needle.accessSegment(0).getParametricLength();
2047 
2048  // Insertion
2049  if (insertionStep > 1e-5 && (!m_IsInserting))
2050  m_IsInserting = true;
2051  // Removal
2052  else if (insertionStep < -1e-5 && m_IsInserting)
2053  m_IsInserting = false;
2054  } else
2055  m_IsInserting = false;
2056 
2057  int i = 0;
2058  while (i < 10 && needRecomputation) {
2059  this->updateCutAngle();
2060  this->updateInsertionDirections();
2061  this->updateTipForce();
2062  this->updateSpringsStiffness();
2063  this->solveSegmentsParameters(); // this->displayDebug();
2064  this->computeSegmentsLengths();
2065  needRecomputation = this->addRemoveSprings();
2066 
2067  i++;
2068  }
2069 
2070  this->updateCutAngle();
2071  this->updateInsertionDirections();
2072  this->updateTipForce();
2073  this->updateSpringsStiffness();
2074  this->solveSegmentsParameters();
2075 
2076  needRecomputation = this->checkInactiveMeasureSprings();
2077  if (needRecomputation) {
2079  this->removeAutoAddedSprings();
2080 
2081  while (i < 300 && needRecomputation) {
2082  this->updateCutAngle();
2083  this->updateInsertionDirections();
2084  this->updateTipForce();
2085  this->updateSpringsStiffness();
2086  this->solveSegmentsParameters(); // this->displayDebug();
2087  this->computeSegmentsLengths();
2088  needRecomputation = this->addRemoveSprings();
2089 
2090  i++;
2091  }
2092 
2093  this->updateCutAngle();
2094  this->updateInsertionDirections();
2095  this->updateTipForce();
2096  this->updateSpringsStiffness();
2097  this->solveSegmentsParameters();
2098  }
2099 
2100  return true;
2101 }
2102 
2104 {
2105  std::cout << "Needle " << this << ": " << m_springs.size() << " InsertionPoints: \n";
2106  for (unsigned int i = 0; i < m_springs.size(); i++) {
2107  std::cout << "\t Direction " << i << ":\n";
2108  vpColVector d = m_springs.at(i).getPosition();
2109  for (int j = 0; j < 3; j++) {
2110  std::cout << "\t\t " << d[j] << "\n";
2111  }
2112  }
2113  std::cout << std::endl;
2114 }
2115 
2117 {
2118  std::cout << "Needle " << this << ": " << m_springs.size() << " InsertionDirections: \n";
2119  for (unsigned int i = 0; i < m_springs.size(); i++) {
2120  std::cout << "\t Direction " << i << ":\n";
2121  vpColVector d = m_springs.at(i).getDirection();
2122  for (int j = 0; j < 3; j++) {
2123  std::cout << "\t\t " << d[j] << "\n";
2124  }
2125  }
2126  std::cout << std::endl;
2127 }
2128 
2130 {
2131  std::cout << "Needle " << this << ": " << m_springs.size() << " Springs: \n";
2132  for (unsigned int i = 0; i < m_springs.size(); i++) {
2133  std::cout << "\t Stiffness " << i << ": " << m_springs.at(i).getStiffness() << "\n";
2134  }
2135  std::cout << std::endl;
2136 }
2137 
2138 std::ostream &operator<<(std::ostream &s, const usNeedleInsertionModelVirtualSprings &needle)
2139 {
2140  s << "usNeedleInsertionModelVirtualSprings\n";
2141 
2142  s << needle.m_needle;
2143 
2144  s << needle.m_tipForce << '\n';
2145  s << needle.m_tipMoment << '\n';
2146  s << needle.m_cutAngle << '\n';
2147 
2148  s << needle.m_defaultSpringStiffness << '\n';
2149  s << needle.m_stiffnessPerUnitLength << '\n';
2150 
2151  int n = needle.m_springs.size();
2152  s << n << '\n';
2153  for (int i = 0; i < n; i++)
2154  s << needle.m_springs.at(i);
2155  n = needle.m_inactiveAutoAddedSprings.size();
2156  s << n << '\n';
2157  for (int i = 0; i < n; i++)
2158  s << needle.m_inactiveAutoAddedSprings.at(i);
2159  n = needle.m_inactiveMeasureSprings.size();
2160  s << n << '\n';
2161  for (int i = 0; i < n; i++)
2162  s << needle.m_inactiveMeasureSprings.at(i);
2163  s << needle.m_tissueSurface;
2164 
2165  s << needle.m_interSpringDistance << '\n';
2166  s << needle.m_interTipSpringDistance << '\n';
2167 
2168  s << needle.m_IsStateConsistent << '\n';
2169 
2170  s << needle.m_LastSegmentLengthComputed << '\n';
2171 
2172  s << (int)needle.m_insertionBehavior << '\n';
2173  s << needle.m_IsInserting << '\n';
2174  s << needle.m_AllowSpringAddition << '\n';
2175  s << needle.m_AllowSpringRemoval << '\n';
2176  s << needle.m_AutomaticSpringAddition << '\n';
2177 
2178  s << needle.m_tipSpringsIndex << '\n';
2179  s << needle.m_nbMinTipSprings << '\n';
2180  s << needle.m_nbMaxTipSprings << '\n';
2181 
2182  s.flush();
2183  return s;
2184 }
2185 
2186 std::istream &operator>>(std::istream &s, usNeedleInsertionModelVirtualSprings &needle)
2187 {
2188  std::string c;
2189  s >> c;
2190  if (c != "usNeedleInsertionModelVirtualSprings") {
2191  vpException e(vpException::ioError, "operator>>=(std::istream&, usNeedleInsertionModelVirtualSprings&): Stream "
2192  "does not contain usNeedleInsertionModelVirtualSprings data");
2193  throw e;
2194  }
2195 
2196  s >> needle.m_needle;
2197  s >> needle.m_tipForce;
2198  s >> needle.m_tipMoment;
2199  s >> needle.m_cutAngle;
2200 
2201  s >> needle.m_defaultSpringStiffness;
2202  s >> needle.m_stiffnessPerUnitLength;
2203 
2204  int n = 0;
2205  s >> n;
2206  needle.m_springs.clear();
2207  needle.m_springs.resize(n);
2208  for (int i = 0; i < n; i++)
2209  s >> needle.m_springs.at(i);
2210  s >> n;
2211  needle.m_inactiveAutoAddedSprings.clear();
2212  needle.m_inactiveAutoAddedSprings.resize(n);
2213  for (int i = 0; i < n; i++)
2214  s >> needle.m_inactiveAutoAddedSprings.at(i);
2215  s >> n;
2216  needle.m_inactiveMeasureSprings.clear();
2217  needle.m_inactiveMeasureSprings.resize(n);
2218  for (int i = 0; i < n; i++)
2219  s >> needle.m_inactiveMeasureSprings.at(i);
2220  s >> needle.m_tissueSurface;
2221 
2222  s >> needle.m_interSpringDistance;
2223  s >> needle.m_interTipSpringDistance;
2224 
2225  s >> needle.m_IsStateConsistent;
2226 
2227  s >> needle.m_LastSegmentLengthComputed;
2228 
2229  int b = 0;
2230  s >> b;
2232  s >> needle.m_IsInserting;
2233  s >> needle.m_AllowSpringAddition;
2234  s >> needle.m_AllowSpringRemoval;
2235  s >> needle.m_AutomaticSpringAddition;
2236 
2237  s >> needle.m_tipSpringsIndex;
2238  s >> needle.m_nbMinTipSprings;
2239  s >> needle.m_nbMaxTipSprings;
2240  s.get();
2241  return s;
2242 }
2243 
2244 std::ostream &operator<<=(std::ostream &s, const usNeedleInsertionModelVirtualSprings &needle)
2245 {
2246  s.write("usNeedleInsertionModelVirtualSprings", 37);
2247  s <<= needle.m_needle;
2248  s.write((char *)&(needle.m_tipForce), sizeof(double));
2249  s.write((char *)&(needle.m_tipMoment), sizeof(double));
2250  s.write((char *)&(needle.m_cutAngle), sizeof(double));
2251 
2252  s.write((char *)&(needle.m_defaultSpringStiffness), sizeof(double));
2253  s.write((char *)&(needle.m_stiffnessPerUnitLength), sizeof(double));
2254 
2255  int n = needle.m_springs.size();
2256  s.write((char *)&n, sizeof(int));
2257  for (int i = 0; i < n; i++)
2258  s <<= needle.m_springs.at(i);
2259  n = needle.m_inactiveAutoAddedSprings.size();
2260  s.write((char *)&n, sizeof(int));
2261  for (int i = 0; i < n; i++)
2262  s <<= needle.m_inactiveAutoAddedSprings.at(i);
2263  n = needle.m_inactiveMeasureSprings.size();
2264  s.write((char *)&n, sizeof(int));
2265  for (int i = 0; i < n; i++)
2266  s <<= needle.m_inactiveMeasureSprings.at(i);
2267  s <<= needle.m_tissueSurface;
2268 
2269  s.write((char *)&(needle.m_interSpringDistance), sizeof(double));
2270  s.write((char *)&(needle.m_interTipSpringDistance), sizeof(double));
2271 
2272  s.write((char *)&(needle.m_IsStateConsistent), sizeof(bool));
2273 
2274  s.write((char *)&(needle.m_LastSegmentLengthComputed), sizeof(double));
2275 
2276  s.write((char *)&(needle.m_insertionBehavior), sizeof(int));
2277  s.write((char *)&(needle.m_IsInserting), sizeof(bool));
2278  s.write((char *)&(needle.m_AllowSpringAddition), sizeof(bool));
2279  s.write((char *)&(needle.m_AllowSpringRemoval), sizeof(bool));
2280  s.write((char *)&(needle.m_AutomaticSpringAddition), sizeof(bool));
2281 
2282  s.write((char *)&(needle.m_tipSpringsIndex), sizeof(int));
2283  s.write((char *)&(needle.m_nbMinTipSprings), sizeof(int));
2284  s.write((char *)&(needle.m_nbMaxTipSprings), sizeof(int));
2285 
2286  s.flush();
2287  return s;
2288 }
2289 
2290 std::istream &operator>>=(std::istream &s, usNeedleInsertionModelVirtualSprings &needle)
2291 {
2292  char c[37];
2293  s.read(c, 37);
2294  if (strcmp(c, "usNeedleInsertionModelVirtualSprings")) {
2295  vpException e(vpException::ioError, "operator>>=(std::istream&, usNeedleInsertionModelVirtualSprings&): Stream "
2296  "does not contain usNeedleInsertionModelVirtualSprings data");
2297  throw e;
2298  }
2299  s >>= needle.m_needle;
2300  s.read((char *)&(needle.m_tipForce), sizeof(double));
2301  s.read((char *)&(needle.m_tipMoment), sizeof(double));
2302  s.read((char *)&(needle.m_cutAngle), sizeof(double));
2303 
2304  s.read((char *)&(needle.m_defaultSpringStiffness), sizeof(double));
2305  s.read((char *)&(needle.m_stiffnessPerUnitLength), sizeof(double));
2306 
2307  int n = 0;
2308  s.read((char *)&n, sizeof(int));
2309  needle.m_springs.clear();
2310  needle.m_springs.resize(n);
2311  for (int i = 0; i < n; i++)
2312  s >>= needle.m_springs.at(i);
2313  s.read((char *)&n, sizeof(int));
2314  needle.m_inactiveAutoAddedSprings.clear();
2315  needle.m_inactiveAutoAddedSprings.resize(n);
2316  for (int i = 0; i < n; i++)
2317  s >>= needle.m_inactiveAutoAddedSprings.at(i);
2318  s.read((char *)&n, sizeof(int));
2319  needle.m_inactiveMeasureSprings.clear();
2320  needle.m_inactiveMeasureSprings.resize(n);
2321  for (int i = 0; i < n; i++)
2322  s >>= needle.m_inactiveMeasureSprings.at(i);
2323  s >>= needle.m_tissueSurface;
2324 
2325  s.read((char *)&(needle.m_interSpringDistance), sizeof(double));
2326  s.read((char *)&(needle.m_interTipSpringDistance), sizeof(double));
2327 
2328  s.read((char *)&(needle.m_IsStateConsistent), sizeof(bool));
2329 
2330  s.read((char *)&(needle.m_LastSegmentLengthComputed), sizeof(double));
2331 
2332  int b = 0;
2333  s.read((char *)&b, sizeof(int));
2335 
2336  s.read((char *)&(needle.m_IsInserting), sizeof(bool));
2337  s.read((char *)&(needle.m_AllowSpringAddition), sizeof(bool));
2338  s.read((char *)&(needle.m_AllowSpringRemoval), sizeof(bool));
2339  s.read((char *)&(needle.m_AutomaticSpringAddition), sizeof(bool));
2340 
2341  s.read((char *)&(needle.m_tipSpringsIndex), sizeof(int));
2342  s.read((char *)&(needle.m_nbMinTipSprings), sizeof(int));
2343  s.read((char *)&(needle.m_nbMaxTipSprings), sizeof(int));
2344  return s;
2345 }
void insertSegment(int i, const usPolynomialCurve3D &seg)
Definition: usBSpline3D.cpp:85
void removeSegments(int i, int j)
Definition: usBSpline3D.cpp:96
const usPolynomialCurve3D & accessSegment(int i) const
int getNbSegments() const
Parameters setters and getters.
Definition: usBSpline3D.cpp:59
const usPolynomialCurve3D & accessLastSegment() const
void setSpringStiffness(int index, double K, bool update=false)
virtual bool setBasePose(const vpPoseVector &pose)=0
Control of the needle.
bool moveSpringDirection(int index, const vpThetaUVector &thetaU, bool update=false)
virtual usNeedleInsertionModelVirtualSprings * clone() const
int addInsertionPoint(usVirtualSpring spg)
Internal model command.
void addMeasureSpring(const vpColVector &p, const vpColVector &d)
Control of the tissue.
bool setSpringPosition(int index, const vpColVector &P, bool update=false)
bool m_LastSegmentLengthComputed
Segments lengths measurement.
double getTissueDeformationEnergy() const
Tissue deformation energy.
const usOrientedPlane3D & accessSurface() const
Tissue.
double getPathDistanceFromPoint(const vpColVector &P) const
Measure model information.
void loadPreset(const ModelPreset preset)
Parameters saving and loading.
void addSpringStiffness(int index, double dK, bool update=false)
const usNeedleModelSpline & accessNeedle() const
Model parameters.
bool setSpringDirection(int index, const vpColVector &D, bool update=false)
void setTipForce(double tipForce)
Parameters setters and getters.
const usNeedleInsertionModelVirtualSprings & operator=(const usNeedleInsertionModelVirtualSprings &needle)
std::vector< usVirtualSpring > m_springs
Model Parameters.
usNeedleModelSpline m_needle
Needle parameters.
bool moveSpringPosition(int index, const vpColVector &dP, bool update=false)
void setInterTipSpringDistance(double interTipSpringDistance)
vpPoseVector getBasePose() const
Parameters setters and getters.
void setBasePose(double tx, double ty, double tz, double thetax, double thetay, double thetaz)
Command of the needle.
vpColVector getTipPosition() const
vpHomogeneousMatrix getWorldMtip() const
vpHomogeneousMatrix getWorldMbase() const
vpPoseVector getTipPose() const
void setTipPose(double tx, double ty, double tz, double thetax, double thetay, double thetaz)
double getOuterDiameter() const
void loadPreset(const NeedlePreset preset)
Parameters saving and loading.
vpColVector getDirection() const
void setDirection(const vpColVector &D)
void setPosition(const vpColVector &P)
vpColVector getStartTangent() const
usPolynomialCurve3D getSubPolynomialCurve(double startParameter, double endParameter) const
double getLength(int nbCountSeg=50) const
vpColVector getPoint(double parameter) const
void setParametricLength(double length)
void setPolynomialCoefficients(const vpMatrix &polynomialCoefficients)
vpColVector getEndPoint() const
vpColVector getEndTangent() const
double getParametricLength() const
void changeCoefficientsToFitBoundaries(double startParameter, double endParameter)
vpColVector getStartPoint() const
void AllowDirectionUpdate(bool flag)
void AllowStiffnessUpdate(bool flag)
void AllowPositionUpdate(bool flag)
void setStiffness(double K)
Parameters setters and getters.
VISP_EXPORT bool IsPointInFrontOfPlane(const vpColVector &point, const usOrientedPlane3D &plane)
VISP_EXPORT vpColVector getPlaneCurveCrossingPoint(const usPolynomialCurve3D &poly, const usOrientedPlane3D &plane, double threshold, double *t=nullptr)
VISP_EXPORT vpColVector projectPointOnPlane(const vpColVector &point, const usOrientedPlane3D &plane, const vpColVector &direction=vpColVector())
VISP_EXPORT bool DoesSegmentCrossPlaneDirect(const usPolynomialCurve3D &poly, const usOrientedPlane3D &plane)
VISP_EXPORT bool DoesSegmentCrossPlane(const usPolynomialCurve3D &poly, const usOrientedPlane3D &plane)