SMILX  1.01
milxQtDiffusionTensorModel.cpp
1 /*=========================================================================
2  The Software is copyright (c) Commonwealth Scientific and Industrial Research Organisation (CSIRO)
3  ABN 41 687 119 230.
4  All rights reserved.
5 
6  Licensed under the CSIRO BSD 3-Clause License
7  You may not use this file except in compliance with the License.
8  You may obtain a copy of the License in the file LICENSE.md or at
9 
10  https://stash.csiro.au/projects/SMILI/repos/smili/browse/license.txt
11 
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  See the License for the specific language governing permissions and
16  limitations under the License.
17 =========================================================================*/
18 #include "milxQtDiffusionTensorModel.h"
19 
20 #include <time.h>
21 
22 #include <vtkMath.h>
23 #include <vtkLine.h>
24 #include <vtkSphereSource.h>
25 
26 #include <boost/math/special_functions/spherical_harmonic.hpp>
27 
28 milxQtDiffusionTensorModel::milxQtDiffusionTensorModel(QWidget *theParent) : milxQtModel(theParent)
29 {
30  milxQtWindow::prefix = "DTI: ";
31 
32  createActions();
33 
34  createConnections();
35 }
36 
37 milxQtDiffusionTensorModel::~milxQtDiffusionTensorModel()
38 {
39  //dtor
40 }
41 
43 {
44  vtkSmartPointer<vtkPolyData> currentMesh = model.Result();
45 
46  typedef double projectionType;
47 
49  vtkSmartPointer<vtkUnsignedCharArray> scalars = vtkSmartPointer<vtkUnsignedCharArray>::New();
50  scalars->SetNumberOfComponents(3);
51  scalars->SetNumberOfTuples(currentMesh->GetNumberOfPoints());
52  scalars->SetName("Fibre Colours");
53  scalars->FillComponent(0, 0);
54  scalars->FillComponent(1, 0);
55  scalars->FillComponent(2, 0);
56  vtkSmartPointer<vtkFloatArray> projections = vtkSmartPointer<vtkFloatArray>::New();
57  projections->SetNumberOfComponents(3);
58  projections->SetNumberOfTuples(currentMesh->GetNumberOfPoints());
59  projections->SetName("Fibre Projections");
60  projections->FillComponent(0, 0.0);
61  projections->FillComponent(1, 0.0);
62  projections->FillComponent(2, 0.0);
63 
64  emit working(-1);
65  if(currentMesh->GetNumberOfLines() == 0)
66  {
67  printInfo("No lines in model. Computing colours for mesh.");
68 
70  for(size_t j = 0; j < 3; j ++)
71  {
72  projectionType axis[3] = {0.0, 0.0, 0.0};
73  axis[j] = 1.0;
74  cout << "Computing toward axis " << j << endl;
75 
77  for(vtkIdType k = 0; k < currentMesh->GetNumberOfPoints(); k ++)
78  {
79  coordinate currentProjection(projections->GetTuple3(k)), position(currentMesh->GetPoint(k));
80 
81  projectionType projection = vtkMath::Dot(axis, position.data_block()); //project to axis being done,
82  currentProjection[j] = projection; //projection in each direction
83 
84  projections->SetTuple3(k, currentProjection[0], currentProjection[1], currentProjection[2]);
85  }
86  }
87 
89  for(vtkIdType k = 0; k < currentMesh->GetNumberOfPoints(); k ++)
90  {
91  coordinate currentProjection(projections->GetTuple3(k));
92  coordinate currentProjSquared = element_product(currentProjection, currentProjection);
93  projectionType maxProjection = currentProjSquared.max_value();
94  currentProjSquared /= maxProjection;
95 
96  unsigned char colourOfPoint[3] = {0, 0, 0};
97  colourOfPoint[0] = static_cast<unsigned char>( currentProjSquared[0]*255.0 );
98  colourOfPoint[1] = static_cast<unsigned char>( currentProjSquared[1]*255.0 );
99  colourOfPoint[2] = static_cast<unsigned char>( currentProjSquared[2]*255.0 );
100 
101  scalars->SetTupleValue(k, colourOfPoint);
102  }
103 
104  currentMesh->GetPointData()->SetVectors(projections);
105  currentMesh->GetPointData()->SetScalars(scalars);
106  }
107  else
108  {
109  printInfo("Re-colouring lines by axes directions");
110  //Ensure lines done properly so can colour appropriately
111  currentMesh->GetLines()->InitTraversal();
112  vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
113  vtkSmartPointer<vtkIdList> idList = vtkSmartPointer<vtkIdList>::New();
114  std::vector<vtkIdType> trackLengths;
115  for(vtkIdType j = 0; j < currentMesh->GetNumberOfLines(); j ++)
116  {
117  currentMesh->GetLines()->GetNextCell(idList);
118  trackLengths.push_back(idList->GetNumberOfIds());
119  for(vtkIdType pointId = 0; pointId < idList->GetNumberOfIds(); pointId ++)
120  {
121  // std::cout << idList->GetId(pointId) << " ";
122 
123  double position[3];
124  currentMesh->GetPoint(idList->GetId(pointId), position);
125  points->InsertNextPoint(position);
126  }
127  }
128 
129  //Re-stitch lines together
130  vtkIdType step = 0;
131  vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New();
132  for(size_t j = 0; j < trackLengths.size(); j ++)
133  {
134  for(vtkIdType k = step; k < trackLengths[j]-1; k ++)
135  {
136  vtkSmartPointer<vtkLine> line = vtkSmartPointer<vtkLine>::New();
137  line->GetPointIds()->SetId(0, k);
138  line->GetPointIds()->SetId(1, k+1);
139  lines->InsertNextCell(line);
140  }
141  qDebug() << trackLengths[j] << endl;
142  step += trackLengths[j];
143  }
144 
145  vtkSmartPointer<vtkPolyData> linesPolyData = vtkSmartPointer<vtkPolyData>::New();
146  //Add the points to the dataset
147  linesPolyData->SetPoints(points);
148  //Add the lines to the dataset
149  linesPolyData->SetLines(lines);
150  linesPolyData->Modified();
151  model.SetInput(linesPolyData);
152  }
153 
154  emit done(-1);
155  generateModel();
156 }
157 
158 void milxQtDiffusionTensorModel::harmonics(QString ordersString)
159 {
160  bool ok = false;
161  if(ordersString.isEmpty())
162  {
163  ordersString = QInputDialog::getText(this, tr("Enter order magnitudes of harmonics separated by spaces"),
164  tr("Orders: "), QLineEdit::Normal,
165  "1 0 0 0 1 1 1 1 1", &ok);
166  }
167  if (!ok || ordersString.isEmpty())
168  return;
169 
170  QStringList orders = ordersString.split(" ");
171  std::vector<double> sHarmonicCoefficients;
172  for(int i = 0; i < orders.size(); i++)
173  sHarmonicCoefficients.push_back(orders[i].toDouble());
174 
175  int n_coefs = sHarmonicCoefficients.size();
176  int l_max = milxQtDiffusionTensorModel::LforN( n_coefs );
177 
178  vtkSmartPointer<vtkSphereSource> sphereSource = vtkSmartPointer<vtkSphereSource>::New();
179  sphereSource->SetThetaResolution( 64 );
180  sphereSource->SetPhiResolution( 64 );
181  sphereSource->SetRadius( 1.0 );
182  sphereSource->Update();
183  vtkSmartPointer<vtkPolyData> glyph = sphereSource->GetOutput();
184 
186  double m_x = 0, m_y = 0, m_z = 0;
187  for(int i = 0; i < glyph->GetNumberOfPoints(); i++)
188  {
189  double point_sphere[3];
190  glyph->GetPoint(i, point_sphere);
191  double x_s = point_sphere[0];
192  double y_s = point_sphere[1];
193  double z_s = point_sphere[2];
194 
195  double x_g, y_g, z_g;
196 
198  double amplitude = computeAmplitude( sHarmonicCoefficients, x_s, y_s, z_s, l_max );
199 
200  if(amplitude < 0)
201  amplitude = 0;
202 
204  x_g = x_s * amplitude + m_x;
205  y_g = y_s * amplitude + m_y;
206  z_g = z_s * amplitude + m_z;
207 
208  glyph->GetPoints()->SetPoint(i, x_g, y_g, z_g);
209  }
210 
211  model.SetInput(glyph);
212  model.GenerateNormals(true);
213 
214  generateModel();
216 }
217 
218 double milxQtDiffusionTensorModel::computeAmplitude(std::vector<double> SH, double x, double y, double z, int lmax)
219 {
220  double az = atan2(y, x);
221  double el = acos(z);
222  double val = 0.0;
223  for (int l = 0; l <= lmax; l+=2)
224  {
225  // val += SH[milxQtDiffusionTensorModel::index(l,0)] * boost::math::legendre_p<double>(l, 0, z);
226  val += SH[milxQtDiffusionTensorModel::index(l,0)] * boost::math::spherical_harmonic_r<double>(l, 0, el,az);
227  }
228 
229  for (int m = 1; m <= lmax; m++)
230  {
231 // float caz = cos(m*az);
232 // float saz = sin(m*az);
233  for (int l = 2*((m+1)/2); l <= lmax; l+=2)
234  {
235  // float buf = boost::math::legendre_p<double>(l, 0, z);
236  // val += SH[index(l,m)]*buf*caz;
237  // val += SH[index(l,-m)]*buf*saz;
238  val += SH[milxQtDiffusionTensorModel::index(l,m)]*boost::math::spherical_harmonic_r(l,m,el,az);
239  val += SH[milxQtDiffusionTensorModel::index(l,-m)]*boost::math::spherical_harmonic_i(l,m,el,az);
240  }
241  }
242 
243  return val;
244 }
245 
246 void milxQtDiffusionTensorModel::createActions()
247 {
248  colourDirectionAct = new QAction(this);
249  colourDirectionAct->setText(QApplication::translate("Model", "Colour By Direction", 0, QApplication::UnicodeUTF8));
250  colourDirectionAct->setShortcut(tr("Alt+c"));
251  harmonicsAct = new QAction(this);
252  harmonicsAct->setText(QApplication::translate("Model", "Show Spherical Harmonic ...", 0, QApplication::UnicodeUTF8));
253  harmonicsAct->setShortcut(tr("Alt+s"));
254 }
255 
256 void milxQtDiffusionTensorModel::createConnections()
257 {
258  //Operations
259  connect(colourDirectionAct, SIGNAL(triggered()), this, SLOT(colourByDirection()));
260  connect(harmonicsAct, SIGNAL(triggered()), this, SLOT(harmonics()));
261 
262  //milxQtModel::createConnections();
263 }
264 
265 void milxQtDiffusionTensorModel::contextMenuEvent(QContextMenuEvent *currentEvent)
266 {
268 
269  contextMenu->addSeparator()->setText(tr("DiffusionTensor"));
270  contextMenu->addAction(colourDirectionAct);
271  contextMenu->addAction(harmonicsAct);
272  contextMenu->addSeparator()->setText(tr("Extensions"));
273  foreach(QAction *currAct, extActionsToAdd)
274  {
275  contextMenu->addAction(currAct);
276  }
277  contextMenu->addSeparator();
282 
283  contextMenu->exec(currentEvent->globalPos());
284 }
QAction * scaleAct
Action for the scale bar of the display.
QList< QAction * > extActionsToAdd
Extension actions to add.
Definition: milxQtWindow.h:253
void harmonics(QString ordersString="")
QMenu * contextMenu
Context Menu.
Definition: milxQtWindow.h:250
QAction * axesAct
Action for axes of the display.
QString prefix
Prefix of the data.
Definition: milxQtWindow.h:242
This class represents the MILX Qt Model/Mesh Display object using VTK.
Definition: milxQtModel.h:115
QMenu * basicContextMenu()
Return the basic context menu with the milxQtModel class ordering. This is for the benefit of derived...
QAction * refreshAct
Action for refreshing the display.
void reset()
Reset the rendering, camera and windowing.
void done(int value)
Send signal that computation is done. Value carries the progress,.
void printInfo(QString msg)
Info message wrapper for console.
void working(int value)
Send signal that computation is in progress. Value carries the progress,.