Pārlūkot izejas kodu

Smarter normalized mode for ctkCoordinatesWidget

Say you have a normal (1,0,0).
You want to change the normal to (0.6,0.6,0.529)
If you change x to 0.6, you get: (0.6,0.566,0.566)
then you change y to 0.6, you get: (0.582,0.6,0.539)
-> x got automatically changed, but you didn't really want to.

This patch ensures that the lastly edited value is not touched when
normalizing the new coordinates.
Julien Finet 12 gadi atpakaļ
vecāks
revīzija
f47c767561

+ 65 - 16
Libs/Widgets/ctkCoordinatesWidget.cpp

@@ -81,6 +81,7 @@ void ctkCoordinatesWidget::setDimension(int dim)
       {
       newPos[i] = 0.;
       this->addSpinBox();
+      this->LastUserEditedCoordinates.push_back(i);
       }
     }
   else
@@ -92,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;
@@ -272,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);
@@ -316,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)
     {
@@ -328,38 +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;
         }

+ 1 - 0
Libs/Widgets/ctkCoordinatesWidget.h

@@ -134,6 +134,7 @@ protected:
   bool    Normalized;
   int     Dimension;
   double* Coordinates;
+  QList<int> LastUserEditedCoordinates;
 };
 
 #endif