Vous avez reçu un message "Your GitLab account has been locked ..." ? Pas d'inquiétude : lisez cet article https://docs.gricad-pages.univ-grenoble-alpes.fr/help/unlock/

Slice.cpp 23.9 KB
Newer Older
1
/*****************************************************************************
2
3
4
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
5
 * (c) 2001-2018 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO)
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/
25

26
27
28
// C++ stuff
#include <algorithm>

29
30
31
32
33
// -- Core stuff
#include "Slice.h"

// -- vtk stuff
#include <vtkProperty.h>
34
#include <vtkViewport.h>
saubatn's avatar
saubatn committed
35
#include <vtkImageMapper3D.h>
36
37
#include <vtkQuad.h>
#include <vtkDataSetMapper.h>
Emmanuel Promayon's avatar
Emmanuel Promayon committed
38
#include <vtkImageMapToColors.h>
39

40
41
using namespace std;

42

43
namespace camitk {
44
// -------------------- constructor --------------------
45
Slice::Slice(vtkSmartPointer<vtkImageData> volume, SliceOrientation orientation, vtkSmartPointer<vtkWindowLevelLookupTable> lookupTable) {
46
47
    sliceOrientation = orientation;
    lut = lookupTable;
48
49
50
51
    init();
    setOriginalVolume(volume);
    setSlice(getNumberOfSlices() / 2);
}
52

53

54
// -------------------- Destructor --------------------
55
Slice::~Slice() {
56
    // Let's unreference vtkSmartPointers
57
    init();
58
}
59

60
// -------------------- init --------------------
61
62
void Slice::init() {
    currentSliceIndex = 0;
63

64
    for (int i=0; i<3; i++) {
65
        originalSpacing[i] = 1.0;
66
67
68
69
70
71
72
73
    }

    originalVolume = nullptr;
    image3DActor = nullptr;
    image2DActor  = nullptr;
    pickPlaneActor = nullptr;
    pickPlaneActorPointSet = nullptr;
    pixelActor = nullptr;
74
    pixelActorPointSet = nullptr;
75

76
77
}

78
79
80
81
82
// -------------------- getImageData --------------------
vtkSmartPointer<vtkImageData> Slice::getImageData() const {
    return originalVolume;
}

83
// -------------------- setOriginalVolume --------------------
84
void Slice::setOriginalVolume(vtkSmartPointer<vtkImageData> volume) {
85
86
87
    // If there were already a referenced volume,
    //  de-reference the smart pointer.
    originalVolume = volume;
88

Emmanuel Promayon's avatar
Emmanuel Promayon committed
89
90
    // Get the original volume information
    // Original volume dimensions in number of voxels (x, y and z) 
91
    int originalDimensions[3];    
92
93
    originalVolume->GetDimensions(originalDimensions);
    originalVolume->GetSpacing(originalSpacing);
94

95
96
97
98
99
    for (int i=0; i<3; i++) {
        // compute original size (nb of slice * spacing)
        originalSize[i] = originalDimensions[i] * originalSpacing[i];
        
        // As originalSpacing[i] will be used to get the slice number
100
        // for 2D images, lets replace 0.0 by 1.0 to avoid division by 0
101
        if (originalSpacing[i] == 0.0) {
102
            originalSpacing[i] = 1.0;
103
        }
104
    }
105
    
106
    // Prepare all the visualization pipeline
107
    initActors();
108
109
}

110
// -------------------- setImageWorldTransform --------------------
111
void Slice::setImageWorldTransform(vtkSmartPointer<vtkTransform> transform) {
112
    image3DActor->SetUserTransform(transform);
113
114
}

115
// -------------------- pixelPicked --------------------
116
117
void Slice::pixelPicked(double x, double y, double z) {
    return;
118
119
}

120
// -------------------- reslicedToVolumeCoords --------------------
121
void Slice::reslicedToVolumeCoords(const double* ijk, double* xyz) {
122
123
124
    xyz[0] = ijk[0] * originalSpacing[0];
    xyz[1] = ijk[1] * originalSpacing[1];
    xyz[2] = ijk[2] * originalSpacing[2];
125
126
}

127
// -------------------- volumeToReslicedCoords --------------------
128
void Slice::volumeToReslicedCoords(const double* xyz, double* ijk) {
129
130
131
    ijk[0] = xyz[0] / originalSpacing[0];
    ijk[1] = xyz[1] / originalSpacing[1];
    ijk[2] = xyz[2] / originalSpacing[2];
132
133
}

134
// -------------------- getNumberOfSlices --------------------
135
int Slice::getNumberOfSlices() const {
136
137
138
139
140
141
    // Array containing first and last indices of the image in each direction
    // 0 & 1 -> x; 2 and 3 -> y; 4 & 5 -> z
    int extent[6];
    originalVolume->GetExtent(extent);
    
    int nbSlices;
142
    switch (sliceOrientation) {
143
        case AXIAL:
144
        case AXIAL_NEURO:
145
146
            nbSlices = extent[5] - extent[4] + 1;
            break;
147
        case CORONAL:
148
149
            nbSlices = extent[3] - extent[2] + 1;
            break;
150
        case SAGITTAL:
151
152
            nbSlices = extent[1] - extent[0] + 1;
            break;
153
154
155
        default:
            nbSlices = 0;
            break;
156
157
158
    }

    return nbSlices;
159
160
}

161
// -------------------- getSlice --------------------
162
163
int Slice::getSlice() const {
    return currentSliceIndex;
164
165
}

166
// -------------------- setSlice --------------------
167
void Slice::setSlice(int s) {
168
    // Check if s is inside the volume bounds
169
    if (s < 0) {
170
        currentSliceIndex = 0.0;
171
    }
172
    else {
173
174
175
176
177
178
        if (s >= getNumberOfSlices()) {
            currentSliceIndex = getNumberOfSlices() - 1;
        }
        else {
            currentSliceIndex = s;
        }
179
    }
180

181
182
183
184
185
    // Array containing first and last indices of the image in each direction
    // 0 & 1 -> x; 2 and 3 -> y; 4 & 5 -> z
    int extent[6];
    originalVolume->GetExtent(extent);

186
    switch (sliceOrientation) {
187
        case AXIAL:
188
189
190
        case AXIAL_NEURO:
            image3DActor->SetDisplayExtent(extent[0], extent[1], extent[2], extent[3], s, s);
            image2DActor->SetDisplayExtent(extent[0], extent[1], extent[2], extent[3], s, s);
191
192
            break;
        case CORONAL:
193
194
            image3DActor->SetDisplayExtent(extent[0], extent[1], s, s, extent[4], extent[5]);
            image2DActor->SetDisplayExtent(extent[0], extent[1], s, s, extent[4], extent[5]);
195
196
            break;
        case SAGITTAL:
197
198
199
200
            image3DActor->SetDisplayExtent(s, s, extent[2], extent[3], extent[4], extent[5]);
            image2DActor->SetDisplayExtent(s, s, extent[2], extent[3], extent[4], extent[5]);
            break;
        default:
201
            break;
202
    }
203

204
    updatePickPlane();
205

206
    // hide pixel actor for now (it should be visible only when user Ctrl-Click on the image
207
    pixelActor->VisibilityOff();
208
209
}

210

211
// -------------------- setSlice --------------------
212
void Slice::setSlice(double x, double y, double z) {
213
    // At this point, coordinates are expressed in the image coordinate system
214
215
    double ijk[3] = {0.0, 0.0, 0.0};
    double xyz[3] = {x, y, z};
216

217
    // Tranform real coordinates to index coordinates
218
    volumeToReslicedCoords(xyz, ijk);
219

220
    switch (sliceOrientation) {
221
222
223
224
225
226
227
228
229
230
231
        default:
            break;
        case AXIAL:
            setSlice(rint(ijk[2]));
            break;
        case CORONAL:
            setSlice(rint(ijk[1]));
            break;
        case SAGITTAL:
            setSlice(rint(ijk[0]));
            break;
232
    }
233
234
235

    // Set pixel position in current slice.
    setPixelRealPosition(x, y, z);
236
237
238
    
    // show the pixel actor
    pixelActor->VisibilityOn();    
239
240
}

241
// -------------------- getNumberOfColors --------------------
242
243
int Slice::getNumberOfColors() const {
    return originalVolume->GetScalarTypeMax() - originalVolume->GetScalarTypeMin();
244
245
}

246
// -------------------- setPixelRealPosition --------------------
247
void Slice::setPixelRealPosition(double x, double y, double z) {
248
    updatePixelActor(x, y, z);
249
250
}

251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
// -------------------- get2DImageActor --------------------
vtkSmartPointer<vtkImageActor> Slice::get2DImageActor() const {
    return image2DActor;
}

// -------------------- get3DImageActor --------------------
vtkSmartPointer<vtkImageActor> Slice::get3DImageActor() const {
    return image3DActor;
}

// -------------------- getPixelActor --------------------
vtkSmartPointer<vtkActor> Slice::getPixelActor() {
    return pixelActor;
}

// -------------------- getPickPlaneActor --------------------
vtkSmartPointer<vtkActor> Slice::getPickPlaneActor() const {
    return pickPlaneActor;
}

// -------------------- initActors --------------------
void Slice::initActors() {
273
    // Image mapper
274
    vtkSmartPointer<vtkImageMapToColors> imgToMapFilter = vtkSmartPointer<vtkImageMapToColors>::New();
275
276
    imgToMapFilter->SetInputData(originalVolume);

277
    // set the lookupTable
278
    imgToMapFilter->SetLookupTable(lut);
279

280
281
    // the 2D and 3D image actors are directly plugged to the output of imgToMapFilter
    image3DActor = vtkSmartPointer<vtkImageActor>::New();
282
283
284
    image3DActor->GetMapper()->SetInputConnection(imgToMapFilter->GetOutputPort());
    image3DActor->InterpolateOn();

285
    image2DActor = vtkSmartPointer<vtkImageActor>::New();
286
287
288
289
    image2DActor->GetMapper()->SetInputConnection(imgToMapFilter->GetOutputPort());
    image2DActor->InterpolateOn();

    // Pick plane
290
    initPickPlaneActor();
291
292
    updatePickPlane();

293
294
295
296
297
298
299
    // pixel
    initPixelActor();
    updatePixelActor();
}

// -------------------- initPickPlaneActor --------------------
void Slice::initPickPlaneActor() {
300
    // create the picked plane actor 3D geometry
301
302
303
304
305
306
307
    vtkSmartPointer<vtkPoints> planePoints = vtkSmartPointer<vtkPoints>::New();
    planePoints->SetNumberOfPoints(8);

    // create zero-positionned points
    for (int i = 0; i < planePoints->GetNumberOfPoints(); i++) {
        planePoints->InsertPoint(i, 0.0, 0.0, 0.0);
    }
308

309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
    // vtkQuad is defined by the four points (0,1,2,3) in counterclockwise order.
    vtkSmartPointer<vtkQuad> leftQuad = vtkSmartPointer<vtkQuad>::New();
    leftQuad->GetPointIds()->SetId(0, 0);
    leftQuad->GetPointIds()->SetId(1, 1);
    leftQuad->GetPointIds()->SetId(2, 2);
    leftQuad->GetPointIds()->SetId(3, 3);

    vtkSmartPointer<vtkQuad> topQuad = vtkSmartPointer<vtkQuad>::New();
    topQuad->GetPointIds()->SetId(0, 4);
    topQuad->GetPointIds()->SetId(1, 5);
    topQuad->GetPointIds()->SetId(2, 1);
    topQuad->GetPointIds()->SetId(3, 0);
    
    vtkSmartPointer<vtkQuad> rightQuad = vtkSmartPointer<vtkQuad>::New();
    rightQuad->GetPointIds()->SetId(0, 5);
    rightQuad->GetPointIds()->SetId(1, 4);
    rightQuad->GetPointIds()->SetId(2, 7);
    rightQuad->GetPointIds()->SetId(3, 6);

    vtkSmartPointer<vtkQuad> bottomQuad = vtkSmartPointer<vtkQuad>::New();
    bottomQuad->GetPointIds()->SetId(0, 3);
    bottomQuad->GetPointIds()->SetId(1, 2);
    bottomQuad->GetPointIds()->SetId(2, 6);
    bottomQuad->GetPointIds()->SetId(3, 7);
    
    // Create the unstructured grid that includes the two quads
    pickPlaneActorPointSet = vtkSmartPointer<vtkUnstructuredGrid>::New();
    pickPlaneActorPointSet->Allocate(4);
    pickPlaneActorPointSet->InsertNextCell(leftQuad->GetCellType(), leftQuad->GetPointIds());
    pickPlaneActorPointSet->InsertNextCell(topQuad->GetCellType(), topQuad->GetPointIds());
    pickPlaneActorPointSet->InsertNextCell(rightQuad->GetCellType(), rightQuad->GetPointIds());
    pickPlaneActorPointSet->InsertNextCell(bottomQuad->GetCellType(), bottomQuad->GetPointIds());
    pickPlaneActorPointSet->SetPoints(planePoints);

    // Create the corresponding mapper
    vtkSmartPointer<vtkDataSetMapper> pickPlaneMapper = vtkSmartPointer<vtkDataSetMapper>::New();
    pickPlaneMapper->SetInputData(pickPlaneActorPointSet);
346

347
    // instantiate the actor
348
    pickPlaneActor = vtkSmartPointer<vtkActor>::New();
349
    pickPlaneActor->SetMapper(pickPlaneMapper);
350
351
    pickPlaneActor->GetProperty()->SetAmbient(1.0);
    pickPlaneActor->GetProperty()->SetDiffuse(1.0);
352

353
    // Update pixel actor properties
354
    switch (sliceOrientation) {
355
356
        case AXIAL_NEURO:
            pickPlaneActor->GetProperty()->SetColor(1.0, 1.0, 0.0);
357
358
359
360
361
362
363
364
365
            break;
        case AXIAL:
            pickPlaneActor->GetProperty()->SetColor(0.0, 0.0, 1.0);
            break;
        case CORONAL:
            pickPlaneActor->GetProperty()->SetColor(0.0, 1.0, 0.0);
            break;
        case SAGITTAL:
            pickPlaneActor->GetProperty()->SetColor(1.0, 0.0, 0.0);
366
367
368
            break;
        default:
            pickPlaneActor->GetProperty()->SetColor(1.0, 1.0, 1.0);
369
370
            break;
    }
371
372
    pickPlaneActor->GetProperty()->SetLineWidth(1.0);
    pickPlaneActor->GetProperty()->SetRepresentationToWireframe();
373

374
375
376
377
378
    //-- pickPlaneActor can not be picked
    pickPlaneActor->PickableOff();
    
    // by default, the plane actor is always visible
    pickPlaneActor->VisibilityOn();
379
380
}

381
// -------------------- initPixelActor --------------------
382
void Slice::initPixelActor() {
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
    // create the pixel actor 3D geometry
    vtkSmartPointer<vtkPoints> pixelPoints = vtkSmartPointer<vtkPoints>::New();
    pixelPoints->SetNumberOfPoints(8);

    // create zero-positionned points
    for (int i = 0; i < pixelPoints->GetNumberOfPoints(); i++) {
        pixelPoints->InsertPoint(i, 0.0, 0.0, 0.0);
    }

    // vtkQuad is defined by the four points (0,1,2,3) in counterclockwise order.
    vtkSmartPointer<vtkQuad> verticalQuad = vtkSmartPointer<vtkQuad>::New();
    verticalQuad->GetPointIds()->SetId(0, 0);
    verticalQuad->GetPointIds()->SetId(1, 1);
    verticalQuad->GetPointIds()->SetId(2, 2);
    verticalQuad->GetPointIds()->SetId(3, 3);

    vtkSmartPointer<vtkQuad> horizontalQuad = vtkSmartPointer<vtkQuad>::New();
    horizontalQuad->GetPointIds()->SetId(0, 4);
    horizontalQuad->GetPointIds()->SetId(1, 5);
    horizontalQuad->GetPointIds()->SetId(2, 6);
    horizontalQuad->GetPointIds()->SetId(3, 7);

    // Create the unstructured grid that includes the two quads
    pixelActorPointSet = vtkSmartPointer<vtkUnstructuredGrid>::New();
407
    pixelActorPointSet->Allocate(2);
408
409
410
411
412
    pixelActorPointSet->InsertNextCell(horizontalQuad->GetCellType(), horizontalQuad->GetPointIds());
    pixelActorPointSet->InsertNextCell(verticalQuad->GetCellType(), verticalQuad->GetPointIds());
    pixelActorPointSet->SetPoints(pixelPoints);

    // Create the corresponding mapper
413
414
    vtkSmartPointer<vtkDataSetMapper> pixelMapper = vtkSmartPointer<vtkDataSetMapper>::New();
    pixelMapper->SetInputData(pixelActorPointSet);
415

416
417
    // instantiate the actor
    pixelActor = vtkSmartPointer<vtkActor>::New();
418
    pixelActor->SetMapper(pixelMapper);
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
    pixelActor->GetProperty()->SetAmbient(1.0);
    pixelActor->GetProperty()->SetDiffuse(1.0);

    // Update pixel actor properties
    switch (sliceOrientation) {
        case AXIAL_NEURO:
            pixelActor->GetProperty()->SetColor(1.0, 1.0, 0.0);
            break;
        case AXIAL:
            pixelActor->GetProperty()->SetColor(0.0, 0.0, 1.0);
            break;
        case CORONAL:
            pixelActor->GetProperty()->SetColor(0.0, 1.0, 0.0);
            break;
        case SAGITTAL:
            pixelActor->GetProperty()->SetColor(1.0, 0.0, 0.0);
            break;
        default:
            pixelActor->GetProperty()->SetColor(1.0, 1.0, 1.0);
            break;
    }
    pixelActor->GetProperty()->SetLineWidth(1.0);
    pixelActor->GetProperty()->SetRepresentationToWireframe();

    //-- pixelActor can not be picked
    pixelActor->PickableOff();
445
446

    // by default, the pixel actor is not visible (it should be only visible when user Ctrl-Click on the image)
447
    pixelActor->VisibilityOff();
448
449
450
451
452
453
}

// -------------------- updatePickPlane --------------------
void Slice::updatePickPlane() {
    // Be careful, the center of the first voxel (0,0,0) is displayed at coordinates (0.0, 0.0, 0.0).
    // Pixels within borders are represented (in 2D) only by their half, quarter or eigth depending on their coordinates.
454
455
456
457
    // see also bug #65 "Unexpected image behaviors in Viewers"

    // update the point positions
    double sliceBackPlane, sliceFrontPlane;
458
    switch (sliceOrientation) {
459
        case AXIAL_NEURO:
460
        case AXIAL:
461
462
463
464
465
466
467
468
469
470
            sliceBackPlane = currentSliceIndex*originalSpacing[2] - originalSpacing[2] / 2.0;
            sliceFrontPlane = currentSliceIndex*originalSpacing[2] + originalSpacing[2] / 2.0;
            pickPlaneActorPointSet->GetPoints()->SetPoint(0, 0.0, 0.0, sliceBackPlane);
            pickPlaneActorPointSet->GetPoints()->SetPoint(1, 0.0, 0.0, sliceFrontPlane);
            pickPlaneActorPointSet->GetPoints()->SetPoint(2, 0.0, originalSize[1], sliceFrontPlane);
            pickPlaneActorPointSet->GetPoints()->SetPoint(3, 0.0, originalSize[1], sliceBackPlane);
            pickPlaneActorPointSet->GetPoints()->SetPoint(4, originalSize[0], 0.0, sliceBackPlane);
            pickPlaneActorPointSet->GetPoints()->SetPoint(5, originalSize[0], 0.0, sliceFrontPlane);
            pickPlaneActorPointSet->GetPoints()->SetPoint(6, originalSize[0], originalSize[1], sliceFrontPlane);
            pickPlaneActorPointSet->GetPoints()->SetPoint(7, originalSize[0], originalSize[1], sliceBackPlane);
471
472
473
            break;

        case CORONAL:
474
475
476
477
478
479
480
481
482
483
            sliceBackPlane = currentSliceIndex*originalSpacing[1] - originalSpacing[1] / 2.0;
            sliceFrontPlane = currentSliceIndex*originalSpacing[1] + originalSpacing[1] / 2.0;
            pickPlaneActorPointSet->GetPoints()->SetPoint(0, 0.0, sliceBackPlane, 0.0);
            pickPlaneActorPointSet->GetPoints()->SetPoint(1, 0.0, sliceFrontPlane, 0.0);
            pickPlaneActorPointSet->GetPoints()->SetPoint(2, 0.0, sliceFrontPlane, originalSize[2]);
            pickPlaneActorPointSet->GetPoints()->SetPoint(3, 0.0, sliceBackPlane, originalSize[2]);
            pickPlaneActorPointSet->GetPoints()->SetPoint(4, originalSize[0], sliceBackPlane, 0.0);
            pickPlaneActorPointSet->GetPoints()->SetPoint(5, originalSize[0], sliceFrontPlane, 0.0);
            pickPlaneActorPointSet->GetPoints()->SetPoint(6, originalSize[0], sliceFrontPlane, originalSize[2]);
            pickPlaneActorPointSet->GetPoints()->SetPoint(7, originalSize[0], sliceBackPlane, originalSize[2]);
484
485
486
            break;

        case SAGITTAL:
487
488
489
490
491
492
493
494
495
496
            sliceBackPlane = currentSliceIndex*originalSpacing[0] - originalSpacing[0] / 2.0;
            sliceFrontPlane = currentSliceIndex*originalSpacing[0] + originalSpacing[0] / 2.0;
            pickPlaneActorPointSet->GetPoints()->SetPoint(0, sliceBackPlane, 0.0, 0.0);
            pickPlaneActorPointSet->GetPoints()->SetPoint(1, sliceFrontPlane, 0.0, 0.0);
            pickPlaneActorPointSet->GetPoints()->SetPoint(2, sliceFrontPlane, 0.0, originalSize[2]);
            pickPlaneActorPointSet->GetPoints()->SetPoint(3, sliceBackPlane, 0.0, originalSize[2]);
            pickPlaneActorPointSet->GetPoints()->SetPoint(4, sliceBackPlane, originalSize[1], 0.0);
            pickPlaneActorPointSet->GetPoints()->SetPoint(5, sliceFrontPlane, originalSize[1], 0.0);
            pickPlaneActorPointSet->GetPoints()->SetPoint(6, sliceFrontPlane, originalSize[1], originalSize[2]);
            pickPlaneActorPointSet->GetPoints()->SetPoint(7, sliceBackPlane, originalSize[1], originalSize[2]);
497
            break;
498
499
        default:
            break;
500
    }
501
502
    // Needed to notify the vtk pipeline of the change in the geometry (and therefore update the actor)
    pickPlaneActorPointSet->Modified();
503
504
505
}

// ----------------- updatePixelActorPosition -----------------
506
void Slice::updatePixelActor() {
507
508
509
510
511
512
    double xMin = 0.0;
    double xMax = originalSize[0];
    double yMin = 0.0;
    double yMax = originalSize[1];
    double zMin = 0.0;
    double zMax = originalSize[2];
513
    // Draw the bounding box around the center of the slice
514
    updatePixelActor(xMin + (xMax - xMin) / 2,  yMin + (yMax - yMin) / 2, zMin + (zMax - zMin) / 2);
515
}
516

517
518
519
void Slice::updatePixelActor(double x, double y, double z) {
    // update the point positions
    double sliceHalfThickness;
520
    switch (sliceOrientation) {
521
522
523
524
525
526
527
528
529
530
531
532
533
        case AXIAL_NEURO:
        case AXIAL:
            sliceHalfThickness = originalSpacing[2] / 2.0;
            // vertical quad
            pixelActorPointSet->GetPoints()->SetPoint(0, x, 0.0, z - sliceHalfThickness);
            pixelActorPointSet->GetPoints()->SetPoint(1, x, 0.0, z + sliceHalfThickness);
            pixelActorPointSet->GetPoints()->SetPoint(2, x, originalSize[1], z + sliceHalfThickness);
            pixelActorPointSet->GetPoints()->SetPoint(3, x, originalSize[1], z - sliceHalfThickness);
            // horizontal quad
            pixelActorPointSet->GetPoints()->SetPoint(4, 0.0, y, z - sliceHalfThickness);
            pixelActorPointSet->GetPoints()->SetPoint(5, 0.0, y, z + sliceHalfThickness);
            pixelActorPointSet->GetPoints()->SetPoint(6, originalSize[0], y, z + sliceHalfThickness);
            pixelActorPointSet->GetPoints()->SetPoint(7, originalSize[0], y, z - sliceHalfThickness);
534
            break;
535
536
537
538
539
540
541
542
543
544
545
546
547

        case CORONAL:
            sliceHalfThickness = originalSpacing[1] / 2.0;
            // vertical quad
            pixelActorPointSet->GetPoints()->SetPoint(0, x, y - sliceHalfThickness, originalSize[2]);
            pixelActorPointSet->GetPoints()->SetPoint(1, x, y + sliceHalfThickness, originalSize[2]);
            pixelActorPointSet->GetPoints()->SetPoint(2, x, y + sliceHalfThickness, 0.0);
            pixelActorPointSet->GetPoints()->SetPoint(3, x, y - sliceHalfThickness, 0.0);
            // horizontal quad
            pixelActorPointSet->GetPoints()->SetPoint(4, 0.0, y - sliceHalfThickness, z);
            pixelActorPointSet->GetPoints()->SetPoint(5, 0.0, y + sliceHalfThickness, z);
            pixelActorPointSet->GetPoints()->SetPoint(6, originalSize[0], y + sliceHalfThickness, z);
            pixelActorPointSet->GetPoints()->SetPoint(7, originalSize[0], y - sliceHalfThickness, z);
548
            break;
549
550
551
552
553
554
555
556
557
558
559
560
561

        case SAGITTAL:
            sliceHalfThickness = originalSpacing[0] / 2.0;
            // vertical quad
            pixelActorPointSet->GetPoints()->SetPoint(0, x - sliceHalfThickness, y, originalSize[2]);
            pixelActorPointSet->GetPoints()->SetPoint(1, x + sliceHalfThickness, y, originalSize[2]);
            pixelActorPointSet->GetPoints()->SetPoint(2, x + sliceHalfThickness, y, 0.0);
            pixelActorPointSet->GetPoints()->SetPoint(3, x - sliceHalfThickness, y, 0.0);
            // horizontal quad
            pixelActorPointSet->GetPoints()->SetPoint(4, x - sliceHalfThickness, 0.0, z);
            pixelActorPointSet->GetPoints()->SetPoint(5, x + sliceHalfThickness, 0.0, z);
            pixelActorPointSet->GetPoints()->SetPoint(6, x + sliceHalfThickness, originalSize[1], z);
            pixelActorPointSet->GetPoints()->SetPoint(7, x - sliceHalfThickness, originalSize[1], z);
562
563
564
            break;
        default:
            break;
565
    }
566
567
    // Needed to notify the vtk pipeline of the change in the geometry (and therefore update the actor)
    pixelActorPointSet->Modified();
568
569
}

570

571
572


573
//------------------------------- addProp ----------------------------------------
574
bool Slice::addProp(const QString& name, vtkSmartPointer< vtkProp > prop) {
575
576
577
    if (!extraProp.contains(name)) {
        extraProp.insert(name, prop);
        return true;
578
    }
579
    else {
580
        return false;
581
    }
582
583
584
585
}

//------------------------------- getProp ----------------------------------------
vtkSmartPointer< vtkProp > Slice::getProp(const QString& name) {
586
    if (extraProp.contains(name)) {
587
        return extraProp.value(name);
588
589
    }
    else {
590
        return nullptr;
591
    }
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
}

//------------------------------- getNumberOfProp ----------------------------------------
unsigned int Slice::getNumberOfProp() const {
    return extraProp.values().size();
}

//------------------------------- getProp ----------------------------------------
vtkSmartPointer< vtkProp > Slice::getProp(unsigned int index) {
    return extraProp.values().at(index);
}

//------------------------------- removeProp ----------------------------------------
bool Slice::removeProp(const QString& name) {
    if (extraProp.contains(name)) {
        // remove the prop from any renderer/consummers
        vtkSmartPointer<vtkProp> prop = extraProp.value(name);
        prop->VisibilityOff();
610
611

        for (int i = 0; i < prop->GetNumberOfConsumers(); i++) {
612
            vtkViewport* viewer = vtkViewport::SafeDownCast(prop->GetConsumer(i));
613

614
            if (viewer) {
615
                viewer->RemoveViewProp(prop);
616
            }
617
        }
618

619
620
621
        // remove it from the maps
        extraProp.remove(name);
        return true;
622
    }
623
    else {
624
        return false;
625
    }
626
627
628
629
630
}




631
632
}