Kaynağa Gözat

Merge branch 'ctkCoordinatesWidget-smart-norm'

* ctkCoordinatesWidget-smart-norm:
  Smarter normalized mode for ctkCoordinatesWidget
  Cleanup ctkCoordinatesWidget
Julien Finet 12 yıl önce
ebeveyn
işleme
5a04ea58cf

+ 92 - 26
Libs/Widgets/ctkCoordinatesWidget.cpp

@@ -19,8 +19,9 @@
 =========================================================================*/
 
 // Qt includes
-#include <QHBoxLayout>
+#include <QDebug>
 #include <QDoubleSpinBox>
+#include <QHBoxLayout>
 
 // CTK includes
 #include "ctkCoordinatesWidget.h"
@@ -36,17 +37,14 @@ ctkCoordinatesWidget::ctkCoordinatesWidget(QWidget* _parent) :QWidget(_parent)
   this->Minimum = -100000.;
   this->Maximum = 100000.;
   this->Normalized = false;
-  this->Dimension = 3;
-  this->Coordinates = new double [this->Dimension];
+  this->Dimension = 0;
+  this->Coordinates = 0;
 
   QHBoxLayout* hboxLayout = new QHBoxLayout(this);
-  this->setLayout(hboxLayout);
-  for (int i = 0; i < this->Dimension; ++i)
-    {
-    this->Coordinates[i] = 0.;
-    this->addSpinBox();
-    }
   hboxLayout->setContentsMargins(0, 0, 0, 0);
+  this->setLayout(hboxLayout);
+
+  this->setDimension(3);
 }
 
 //------------------------------------------------------------------------------
@@ -83,6 +81,7 @@ void ctkCoordinatesWidget::setDimension(int dim)
       {
       newPos[i] = 0.;
       this->addSpinBox();
+      this->LastUserEditedCoordinates.push_back(i);
       }
     }
   else
@@ -94,6 +93,7 @@ void ctkCoordinatesWidget::setDimension(int dim)
       QWidget* widget = item ? item->widget() : 0;
       delete item;
       delete widget;
+      this->LastUserEditedCoordinates.pop_back();
       }
     }
   delete [] this->Coordinates;
@@ -274,7 +274,11 @@ void ctkCoordinatesWidget::setCoordinates(double* coordinates)
       item ? dynamic_cast<QDoubleSpinBox*>(item->widget()) : 0;
     if (spinBox)
       {
+      // we don't want updateCoordinate() to be called.
+      // it could mess with the LastUserEditedCoordinates list.
+      bool spinBoxSignalWasBlocked = spinBox->blockSignals(true);
       spinBox->setValue(this->Coordinates[i]);
+      spinBox->blockSignals(spinBoxSignalWasBlocked);
       }
     }
   this->blockSignals(blocked);
@@ -318,7 +322,6 @@ double const * ctkCoordinatesWidget::coordinates()const
 //------------------------------------------------------------------------------
 void ctkCoordinatesWidget::updateCoordinate(double coordinate)
 {
-  double den = 0.;
   int element = -1;
   for (int i = 0; i < this->Dimension; ++i)
     {
@@ -330,37 +333,82 @@ void ctkCoordinatesWidget::updateCoordinate(double coordinate)
       this->Coordinates[i] = coordinate;
       element = i;
       }
-    else
+    }
+  Q_ASSERT(element != -1);
+  // Update the last edited history by push first the element.
+  for (int i = this->Dimension -1; i > 0; --i)
+    {
+    if (this->LastUserEditedCoordinates[i] == element)
       {
-      den += this->Coordinates[i]*this->Coordinates[i];
+      this->LastUserEditedCoordinates.swap(i,i-1);
       }
     }
-  Q_ASSERT(element != -1);
+  // What is the oldest coordinate to be edited
+  int oldestElement = this->LastUserEditedCoordinates.last();
+
   if (this->isNormalized())
     {
+    // We have to ensure the coordinates are normalized.
+    double den = 0.;
+    double squaredNorm = this->squaredNorm();
     // Old Values xx + yy + zz = 1
     // New values: x'x' + y'y' + z'z' = 1
     // Say we are changing y into y':
     // x'x' + z'z' = 1 - y'y'
-    // Let's pose a the coef to multiply x into x' that keeps the norm to 1:
-    // axax + azaz = 1 - y'y'
-    // aa(xx + zz) = 1 - y'y'
-    // a = sqrt( (1 - y'y') / (xx + zz) )
-    bool mult = true;
-    if (den != 0.0)
+    if (oldestElement != -1 &&
+        this->Coordinates[oldestElement] != 0.0 &&
+        squaredNorm != 0.0)
       {
-      mult = true;
-      den = sqrt( (1. - coordinate * coordinate) / den);
+      // 1) Normalize only with the oldest user edited value
+      // The oldest element is z, that means we try to have
+      // x = x' (so that the user doesn't loose the edit he just made on the
+      // element (x) he edited before this one (y).
+      // Let's pose a the coef to multiply z into z' that keeps the norm to 1
+      // xx + z'z' = 1 - y'y' (because x = x')
+      // xx + azaz = 1 - y'y' (because z' = az)
+      // aa*zz = 1 - y'y' - xx
+      // a = sqrt( (1 - y'y' - xx) / zz )
+      den = (1. - (squaredNorm -
+             this->Coordinates[oldestElement] *
+             this->Coordinates[oldestElement])) /
+              (this->Coordinates[oldestElement] *
+               this->Coordinates[oldestElement]);
+      if (den > 0.)
+        {
+        den = sqrt(den);
+        }
       }
-    else if (this->Dimension > 1)
+    // Maybe 1) failed, then give 2) a chance.
+    if (den <= 0)
       {
-      mult = false;
-      den = sqrt((1. - coordinate*coordinate) / (this->Dimension - 1));
+      oldestElement = -1;
       }
+    bool mult = true;
+    if (oldestElement == -1)
+      {
+      // 2) Normalize with all the coordinates
+      // Let's pose a the coef to multiply x into x' and z into z' that keeps
+      // the norm to 1:
+      // axax + azaz = 1 - y'y'
+      // aa(xx + zz) = 1 - y'y'
+      // a = sqrt( (1 - y'y') / (xx + zz) )
+      squaredNorm -= coordinate * coordinate;
+      if (squaredNorm != 0.0)
+        {
+        den = sqrt( (1. - coordinate * coordinate) / squaredNorm);
+        }
+      else if (this->Dimension > 1)
+        {
+        mult = false;
+        den = sqrt((1. - coordinate*coordinate) / (this->Dimension - 1));
+        }
+      }
+    // Normalize coordinates
     double* normalizedCoordinates = new double[this->Dimension];
     for (int i = 0; i < this->Dimension; ++i)
       {
-      if (i != element)
+      if ((i != element && oldestElement == -1) ||
+          (i == oldestElement && oldestElement != -1))
         {
         normalizedCoordinates[i] = mult ? this->Coordinates[i] * den : den;
         }
@@ -420,12 +468,30 @@ double ctkCoordinatesWidget::normalize(double* coordinates, int dimension)
 }
 
 //------------------------------------------------------------------------------
+double ctkCoordinatesWidget::norm()const
+{
+  return ctkCoordinatesWidget::norm(this->Coordinates, this->Dimension);
+}
+
+//------------------------------------------------------------------------------
 double ctkCoordinatesWidget::norm(double* coordinates, int dimension)
 {
+  return sqrt(ctkCoordinatesWidget::squaredNorm(coordinates, dimension));
+}
+
+//------------------------------------------------------------------------------
+double ctkCoordinatesWidget::squaredNorm()const
+{
+  return ctkCoordinatesWidget::squaredNorm(this->Coordinates, this->Dimension);
+}
+
+//------------------------------------------------------------------------------
+double ctkCoordinatesWidget::squaredNorm(double* coordinates, int dimension)
+{
   double sum = 0.;
   for (int i = 0; i < dimension; ++i)
     {
     sum += coordinates[i] * coordinates[i];
     }
-  return sqrt(sum);
+  return sum;
 }

+ 4 - 0
Libs/Widgets/ctkCoordinatesWidget.h

@@ -87,6 +87,8 @@ public:
 
   /// Return the norm of the coordinates.
   double norm()const;
+  /// Return the squared norm of the coordinates.
+  double squaredNorm()const;
 
   /// Set/Get the coordinates. Use commas to separate elements, spaces are
   /// allowed: e.g. "0,0.0, 0."
@@ -123,6 +125,7 @@ protected:
 
   /// Compute the norm of a coordinates \a dimension vector
   static double norm(double* coordinates, int dimension);
+  static double squaredNorm(double* coordinates, int dimension);
 
   int     Decimals;
   double  SingleStep;
@@ -131,6 +134,7 @@ protected:
   bool    Normalized;
   int     Dimension;
   double* Coordinates;
+  QList<int> LastUserEditedCoordinates;
 };
 
 #endif