comparison Applications/Samples/SingleFrameEditorApplication.h @ 408:6834c236b36d

reorganization
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 12 Nov 2018 14:52:10 +0100
parents 842a3c7cfdc0
children 6decc0ba9da5
comparison
equal deleted inserted replaced
407:842a3c7cfdc0 408:6834c236b36d
21 21
22 #pragma once 22 #pragma once
23 23
24 #include "SampleApplicationBase.h" 24 #include "SampleApplicationBase.h"
25 25
26 #include "../../Framework/Toolbox/ImageGeometry.h" 26 #include "../../Framework/Radiography/RadiographyScene.h"
27 #include "../../Framework/Toolbox/OrthancApiClient.h" 27
28 #include "../../Framework/Toolbox/DicomFrameConverter.h" 28 #include "../../Framework/Toolbox/UndoRedoStack.h"
29 29
30 #include <Core/Images/FontRegistry.h> 30 #include <Core/Images/FontRegistry.h>
31 #include <Core/Images/Image.h> 31 #include <Core/Images/Image.h>
32 #include <Core/Images/ImageProcessing.h> 32 #include <Core/Images/ImageProcessing.h>
33 #include <Core/Images/PamReader.h> 33 #include <Core/Images/PamReader.h>
49 #include <boost/math/special_functions/round.hpp> 49 #include <boost/math/special_functions/round.hpp>
50 50
51 51
52 namespace OrthancStone 52 namespace OrthancStone
53 { 53 {
54 class RadiologyScene : 54 class RadiographyLayerCommand : public UndoRedoStack::ICommand
55 public IObserver,
56 public IObservable
57 {
58 public:
59 typedef OriginMessage<MessageType_Widget_GeometryChanged, RadiologyScene> GeometryChangedMessage;
60 typedef OriginMessage<MessageType_Widget_ContentChanged, RadiologyScene> ContentChangedMessage;
61
62
63 enum Corner
64 {
65 Corner_TopLeft,
66 Corner_TopRight,
67 Corner_BottomLeft,
68 Corner_BottomRight
69 };
70
71
72
73 class Layer : public boost::noncopyable
74 {
75 friend class RadiologyScene;
76
77 private:
78 size_t index_;
79 bool hasSize_;
80 unsigned int width_;
81 unsigned int height_;
82 bool hasCrop_;
83 unsigned int cropX_;
84 unsigned int cropY_;
85 unsigned int cropWidth_;
86 unsigned int cropHeight_;
87 Matrix transform_;
88 Matrix transformInverse_;
89 double pixelSpacingX_;
90 double pixelSpacingY_;
91 double panX_;
92 double panY_;
93 double angle_;
94 bool resizeable_;
95
96
97 protected:
98 const Matrix& GetTransform() const
99 {
100 return transform_;
101 }
102
103
104 private:
105 static void ApplyTransform(double& x /* inout */,
106 double& y /* inout */,
107 const Matrix& transform)
108 {
109 Vector p;
110 LinearAlgebra::AssignVector(p, x, y, 1);
111
112 Vector q = LinearAlgebra::Product(transform, p);
113
114 if (!LinearAlgebra::IsNear(q[2], 1.0))
115 {
116 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
117 }
118 else
119 {
120 x = q[0];
121 y = q[1];
122 }
123 }
124
125
126 void UpdateTransform()
127 {
128 transform_ = CreateScalingMatrix(pixelSpacingX_, pixelSpacingY_);
129
130 double centerX, centerY;
131 GetCenter(centerX, centerY);
132
133 transform_ = LinearAlgebra::Product(
134 CreateOffsetMatrix(panX_ + centerX, panY_ + centerY),
135 CreateRotationMatrix(angle_),
136 CreateOffsetMatrix(-centerX, -centerY),
137 transform_);
138
139 LinearAlgebra::InvertMatrix(transformInverse_, transform_);
140 }
141
142
143 void AddToExtent(Extent2D& extent,
144 double x,
145 double y) const
146 {
147 ApplyTransform(x, y, transform_);
148 extent.AddPoint(x, y);
149 }
150
151
152 void GetCornerInternal(double& x,
153 double& y,
154 Corner corner,
155 unsigned int cropX,
156 unsigned int cropY,
157 unsigned int cropWidth,
158 unsigned int cropHeight) const
159 {
160 double dx = static_cast<double>(cropX);
161 double dy = static_cast<double>(cropY);
162 double dwidth = static_cast<double>(cropWidth);
163 double dheight = static_cast<double>(cropHeight);
164
165 switch (corner)
166 {
167 case Corner_TopLeft:
168 x = dx;
169 y = dy;
170 break;
171
172 case Corner_TopRight:
173 x = dx + dwidth;
174 y = dy;
175 break;
176
177 case Corner_BottomLeft:
178 x = dx;
179 y = dy + dheight;
180 break;
181
182 case Corner_BottomRight:
183 x = dx + dwidth;
184 y = dy + dheight;
185 break;
186
187 default:
188 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
189 }
190
191 ApplyTransform(x, y, transform_);
192 }
193
194
195 void SetIndex(size_t index)
196 {
197 index_ = index;
198 }
199
200
201 bool Contains(double x,
202 double y) const
203 {
204 ApplyTransform(x, y, transformInverse_);
205
206 unsigned int cropX, cropY, cropWidth, cropHeight;
207 GetCrop(cropX, cropY, cropWidth, cropHeight);
208
209 return (x >= cropX && x <= cropX + cropWidth &&
210 y >= cropY && y <= cropY + cropHeight);
211 }
212
213
214 void DrawBorders(CairoContext& context,
215 double zoom)
216 {
217 unsigned int cx, cy, width, height;
218 GetCrop(cx, cy, width, height);
219
220 double dx = static_cast<double>(cx);
221 double dy = static_cast<double>(cy);
222 double dwidth = static_cast<double>(width);
223 double dheight = static_cast<double>(height);
224
225 cairo_t* cr = context.GetObject();
226 cairo_set_line_width(cr, 2.0 / zoom);
227
228 double x, y;
229 x = dx;
230 y = dy;
231 ApplyTransform(x, y, transform_);
232 cairo_move_to(cr, x, y);
233
234 x = dx + dwidth;
235 y = dy;
236 ApplyTransform(x, y, transform_);
237 cairo_line_to(cr, x, y);
238
239 x = dx + dwidth;
240 y = dy + dheight;
241 ApplyTransform(x, y, transform_);
242 cairo_line_to(cr, x, y);
243
244 x = dx;
245 y = dy + dheight;
246 ApplyTransform(x, y, transform_);
247 cairo_line_to(cr, x, y);
248
249 x = dx;
250 y = dy;
251 ApplyTransform(x, y, transform_);
252 cairo_line_to(cr, x, y);
253
254 cairo_stroke(cr);
255 }
256
257
258 static double Square(double x)
259 {
260 return x * x;
261 }
262
263
264 public:
265 Layer() :
266 index_(0),
267 hasSize_(false),
268 width_(0),
269 height_(0),
270 hasCrop_(false),
271 pixelSpacingX_(1),
272 pixelSpacingY_(1),
273 panX_(0),
274 panY_(0),
275 angle_(0),
276 resizeable_(false)
277 {
278 UpdateTransform();
279 }
280
281 virtual ~Layer()
282 {
283 }
284
285 size_t GetIndex() const
286 {
287 return index_;
288 }
289
290 void ResetCrop()
291 {
292 hasCrop_ = false;
293 }
294
295 void SetCrop(unsigned int x,
296 unsigned int y,
297 unsigned int width,
298 unsigned int height)
299 {
300 if (!hasSize_)
301 {
302 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
303 }
304
305 if (x + width > width_ ||
306 y + height > height_)
307 {
308 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
309 }
310
311 hasCrop_ = true;
312 cropX_ = x;
313 cropY_ = y;
314 cropWidth_ = width;
315 cropHeight_ = height;
316
317 UpdateTransform();
318 }
319
320 void GetCrop(unsigned int& x,
321 unsigned int& y,
322 unsigned int& width,
323 unsigned int& height) const
324 {
325 if (hasCrop_)
326 {
327 x = cropX_;
328 y = cropY_;
329 width = cropWidth_;
330 height = cropHeight_;
331 }
332 else
333 {
334 x = 0;
335 y = 0;
336 width = width_;
337 height = height_;
338 }
339 }
340
341 void SetAngle(double angle)
342 {
343 angle_ = angle;
344 UpdateTransform();
345 }
346
347 double GetAngle() const
348 {
349 return angle_;
350 }
351
352 void SetSize(unsigned int width,
353 unsigned int height)
354 {
355 if (hasSize_ &&
356 (width != width_ ||
357 height != height_))
358 {
359 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
360 }
361
362 hasSize_ = true;
363 width_ = width;
364 height_ = height;
365
366 UpdateTransform();
367 }
368
369
370 unsigned int GetWidth() const
371 {
372 return width_;
373 }
374
375
376 unsigned int GetHeight() const
377 {
378 return height_;
379 }
380
381
382 Extent2D GetExtent() const
383 {
384 Extent2D extent;
385
386 unsigned int x, y, width, height;
387 GetCrop(x, y, width, height);
388
389 double dx = static_cast<double>(x);
390 double dy = static_cast<double>(y);
391 double dwidth = static_cast<double>(width);
392 double dheight = static_cast<double>(height);
393
394 AddToExtent(extent, dx, dy);
395 AddToExtent(extent, dx + dwidth, dy);
396 AddToExtent(extent, dx, dy + dheight);
397 AddToExtent(extent, dx + dwidth, dy + dheight);
398
399 return extent;
400 }
401
402
403 bool GetPixel(unsigned int& imageX,
404 unsigned int& imageY,
405 double sceneX,
406 double sceneY) const
407 {
408 if (width_ == 0 ||
409 height_ == 0)
410 {
411 return false;
412 }
413 else
414 {
415 ApplyTransform(sceneX, sceneY, transformInverse_);
416
417 int x = static_cast<int>(std::floor(sceneX));
418 int y = static_cast<int>(std::floor(sceneY));
419
420 if (x < 0)
421 {
422 imageX = 0;
423 }
424 else if (x >= static_cast<int>(width_))
425 {
426 imageX = width_;
427 }
428 else
429 {
430 imageX = static_cast<unsigned int>(x);
431 }
432
433 if (y < 0)
434 {
435 imageY = 0;
436 }
437 else if (y >= static_cast<int>(height_))
438 {
439 imageY = height_;
440 }
441 else
442 {
443 imageY = static_cast<unsigned int>(y);
444 }
445
446 return true;
447 }
448 }
449
450
451 void SetPan(double x,
452 double y)
453 {
454 panX_ = x;
455 panY_ = y;
456 UpdateTransform();
457 }
458
459
460 void SetPixelSpacing(double x,
461 double y)
462 {
463 pixelSpacingX_ = x;
464 pixelSpacingY_ = y;
465 UpdateTransform();
466 }
467
468 double GetPixelSpacingX() const
469 {
470 return pixelSpacingX_;
471 }
472
473 double GetPixelSpacingY() const
474 {
475 return pixelSpacingY_;
476 }
477
478 double GetPanX() const
479 {
480 return panX_;
481 }
482
483 double GetPanY() const
484 {
485 return panY_;
486 }
487
488 void GetCenter(double& centerX,
489 double& centerY) const
490 {
491 centerX = static_cast<double>(width_) / 2.0;
492 centerY = static_cast<double>(height_) / 2.0;
493 ApplyTransform(centerX, centerY, transform_);
494 }
495
496
497 void GetCorner(double& x /* out */,
498 double& y /* out */,
499 Corner corner) const
500 {
501 unsigned int cropX, cropY, cropWidth, cropHeight;
502 GetCrop(cropX, cropY, cropWidth, cropHeight);
503 GetCornerInternal(x, y, corner, cropX, cropY, cropWidth, cropHeight);
504 }
505
506
507 bool LookupCorner(Corner& corner /* out */,
508 double x,
509 double y,
510 double zoom,
511 double viewportDistance) const
512 {
513 static const Corner CORNERS[] = {
514 Corner_TopLeft,
515 Corner_TopRight,
516 Corner_BottomLeft,
517 Corner_BottomRight
518 };
519
520 unsigned int cropX, cropY, cropWidth, cropHeight;
521 GetCrop(cropX, cropY, cropWidth, cropHeight);
522
523 double threshold = Square(viewportDistance / zoom);
524
525 for (size_t i = 0; i < 4; i++)
526 {
527 double cx, cy;
528 GetCornerInternal(cx, cy, CORNERS[i], cropX, cropY, cropWidth, cropHeight);
529
530 double d = Square(cx - x) + Square(cy - y);
531
532 if (d <= threshold)
533 {
534 corner = CORNERS[i];
535 return true;
536 }
537 }
538
539 return false;
540 }
541
542 bool IsResizeable() const
543 {
544 return resizeable_;
545 }
546
547 void SetResizeable(bool resizeable)
548 {
549 resizeable_ = resizeable;
550 }
551
552 virtual bool GetDefaultWindowing(float& center,
553 float& width) const = 0;
554
555 virtual void Render(Orthanc::ImageAccessor& buffer,
556 const Matrix& viewTransform,
557 ImageInterpolation interpolation) const = 0;
558
559 virtual bool GetRange(float& minValue,
560 float& maxValue) const = 0;
561 };
562
563
564 class LayerAccessor : public boost::noncopyable
565 {
566 private:
567 RadiologyScene& scene_;
568 size_t index_;
569 Layer* layer_;
570
571 public:
572 LayerAccessor(RadiologyScene& scene,
573 size_t index) :
574 scene_(scene),
575 index_(index)
576 {
577 Layers::iterator layer = scene.layers_.find(index);
578 if (layer == scene.layers_.end())
579 {
580 layer_ = NULL;
581 }
582 else
583 {
584 assert(layer->second != NULL);
585 layer_ = layer->second;
586 }
587 }
588
589 LayerAccessor(RadiologyScene& scene,
590 double x,
591 double y) :
592 scene_(scene),
593 index_(0) // Dummy initialization
594 {
595 if (scene.LookupLayer(index_, x, y))
596 {
597 Layers::iterator layer = scene.layers_.find(index_);
598
599 if (layer == scene.layers_.end())
600 {
601 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
602 }
603 else
604 {
605 assert(layer->second != NULL);
606 layer_ = layer->second;
607 }
608 }
609 else
610 {
611 layer_ = NULL;
612 }
613 }
614
615 void Invalidate()
616 {
617 layer_ = NULL;
618 }
619
620 bool IsValid() const
621 {
622 return layer_ != NULL;
623 }
624
625 RadiologyScene& GetScene() const
626 {
627 if (IsValid())
628 {
629 return scene_;
630 }
631 else
632 {
633 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
634 }
635 }
636
637 size_t GetIndex() const
638 {
639 if (IsValid())
640 {
641 return index_;
642 }
643 else
644 {
645 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
646 }
647 }
648
649 Layer& GetLayer() const
650 {
651 if (IsValid())
652 {
653 return *layer_;
654 }
655 else
656 {
657 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
658 }
659 }
660 };
661
662
663 private:
664 class AlphaLayer : public Layer
665 {
666 private:
667 const RadiologyScene& scene_;
668 std::auto_ptr<Orthanc::ImageAccessor> alpha_; // Grayscale8
669 bool useWindowing_;
670 float foreground_;
671
672 public:
673 AlphaLayer(const RadiologyScene& scene) :
674 scene_(scene),
675 useWindowing_(true),
676 foreground_(0)
677 {
678 }
679
680
681 void SetForegroundValue(float foreground)
682 {
683 useWindowing_ = false;
684 foreground_ = foreground;
685 }
686
687
688 void SetAlpha(Orthanc::ImageAccessor* image)
689 {
690 std::auto_ptr<Orthanc::ImageAccessor> raii(image);
691
692 if (image == NULL)
693 {
694 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
695 }
696
697 if (image->GetFormat() != Orthanc::PixelFormat_Grayscale8)
698 {
699 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
700 }
701
702 SetSize(image->GetWidth(), image->GetHeight());
703 alpha_ = raii;
704 }
705
706
707 void LoadText(const Orthanc::Font& font,
708 const std::string& utf8)
709 {
710 SetAlpha(font.RenderAlpha(utf8));
711 }
712
713
714 virtual bool GetDefaultWindowing(float& center,
715 float& width) const
716 {
717 return false;
718 }
719
720
721 virtual void Render(Orthanc::ImageAccessor& buffer,
722 const Matrix& viewTransform,
723 ImageInterpolation interpolation) const
724 {
725 if (alpha_.get() == NULL)
726 {
727 return;
728 }
729
730 if (buffer.GetFormat() != Orthanc::PixelFormat_Float32)
731 {
732 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
733 }
734
735 unsigned int cropX, cropY, cropWidth, cropHeight;
736 GetCrop(cropX, cropY, cropWidth, cropHeight);
737
738 Matrix m = LinearAlgebra::Product(viewTransform,
739 GetTransform(),
740 CreateOffsetMatrix(cropX, cropY));
741
742 Orthanc::ImageAccessor cropped;
743 alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
744
745 Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false);
746 ApplyProjectiveTransform(tmp, cropped, m, interpolation, true /* clear */);
747
748 // Blit
749 const unsigned int width = buffer.GetWidth();
750 const unsigned int height = buffer.GetHeight();
751
752 float value = foreground_;
753
754 if (useWindowing_)
755 {
756 float center, width;
757 if (scene_.GetWindowing(center, width))
758 {
759 value = center + width / 2.0f;
760 }
761 }
762
763 for (unsigned int y = 0; y < height; y++)
764 {
765 float *q = reinterpret_cast<float*>(buffer.GetRow(y));
766 const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y));
767
768 for (unsigned int x = 0; x < width; x++, p++, q++)
769 {
770 float a = static_cast<float>(*p) / 255.0f;
771
772 *q = (a * value + (1.0f - a) * (*q));
773 }
774 }
775 }
776
777
778 virtual bool GetRange(float& minValue,
779 float& maxValue) const
780 {
781 if (useWindowing_)
782 {
783 return false;
784 }
785 else
786 {
787 minValue = 0;
788 maxValue = 0;
789
790 if (foreground_ < 0)
791 {
792 minValue = foreground_;
793 }
794
795 if (foreground_ > 0)
796 {
797 maxValue = foreground_;
798 }
799
800 return true;
801 }
802 }
803 };
804
805
806
807 private:
808 static Matrix CreateOffsetMatrix(double dx,
809 double dy)
810 {
811 Matrix m = LinearAlgebra::IdentityMatrix(3);
812 m(0, 2) = dx;
813 m(1, 2) = dy;
814 return m;
815 }
816
817
818 static Matrix CreateScalingMatrix(double sx,
819 double sy)
820 {
821 Matrix m = LinearAlgebra::IdentityMatrix(3);
822 m(0, 0) = sx;
823 m(1, 1) = sy;
824 return m;
825 }
826
827
828 static Matrix CreateRotationMatrix(double angle)
829 {
830 Matrix m;
831 const double v[] = { cos(angle), -sin(angle), 0,
832 sin(angle), cos(angle), 0,
833 0, 0, 1 };
834 LinearAlgebra::FillMatrix(m, 3, 3, v);
835 return m;
836 }
837
838
839 class DicomLayer : public Layer
840 {
841 private:
842 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData
843 std::auto_ptr<DicomFrameConverter> converter_;
844 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32
845
846 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag)
847 {
848 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement());
849 }
850
851
852 void ApplyConverter()
853 {
854 if (source_.get() != NULL &&
855 converter_.get() != NULL)
856 {
857 converted_.reset(converter_->ConvertFrame(*source_));
858 }
859 }
860
861 public:
862 void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset)
863 {
864 converter_.reset(new DicomFrameConverter);
865 converter_->ReadParameters(dataset);
866 ApplyConverter();
867
868 std::string tmp;
869 Vector pixelSpacing;
870
871 if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) &&
872 LinearAlgebra::ParseVector(pixelSpacing, tmp) &&
873 pixelSpacing.size() == 2)
874 {
875 SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]);
876 }
877
878 //SetPan(-0.5 * GetPixelSpacingX(), -0.5 * GetPixelSpacingY());
879
880 OrthancPlugins::DicomDatasetReader reader(dataset);
881
882 unsigned int width, height;
883 if (!reader.GetUnsignedIntegerValue(width, ConvertTag(Orthanc::DICOM_TAG_COLUMNS)) ||
884 !reader.GetUnsignedIntegerValue(height, ConvertTag(Orthanc::DICOM_TAG_ROWS)))
885 {
886 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
887 }
888 else
889 {
890 SetSize(width, height);
891 }
892 }
893
894
895 void SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership
896 {
897 std::auto_ptr<Orthanc::ImageAccessor> raii(image);
898
899 if (image == NULL)
900 {
901 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
902 }
903
904 SetSize(image->GetWidth(), image->GetHeight());
905
906 source_ = raii;
907 ApplyConverter();
908 }
909
910
911 virtual void Render(Orthanc::ImageAccessor& buffer,
912 const Matrix& viewTransform,
913 ImageInterpolation interpolation) const
914 {
915 if (converted_.get() != NULL)
916 {
917 if (converted_->GetFormat() != Orthanc::PixelFormat_Float32)
918 {
919 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
920 }
921
922 unsigned int cropX, cropY, cropWidth, cropHeight;
923 GetCrop(cropX, cropY, cropWidth, cropHeight);
924
925 Matrix m = LinearAlgebra::Product(viewTransform,
926 GetTransform(),
927 CreateOffsetMatrix(cropX, cropY));
928
929 Orthanc::ImageAccessor cropped;
930 converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
931
932 ApplyProjectiveTransform(buffer, cropped, m, interpolation, false);
933 }
934 }
935
936
937 virtual bool GetDefaultWindowing(float& center,
938 float& width) const
939 {
940 if (converter_.get() != NULL &&
941 converter_->HasDefaultWindow())
942 {
943 center = static_cast<float>(converter_->GetDefaultWindowCenter());
944 width = static_cast<float>(converter_->GetDefaultWindowWidth());
945 return true;
946 }
947 else
948 {
949 return false;
950 }
951 }
952
953
954 virtual bool GetRange(float& minValue,
955 float& maxValue) const
956 {
957 if (converted_.get() != NULL)
958 {
959 if (converted_->GetFormat() != Orthanc::PixelFormat_Float32)
960 {
961 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
962 }
963
964 Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, *converted_);
965 return true;
966 }
967 else
968 {
969 return false;
970 }
971 }
972 };
973
974
975
976
977 typedef std::map<size_t, Layer*> Layers;
978
979 OrthancApiClient& orthanc_;
980 size_t countLayers_;
981 bool hasWindowing_;
982 float windowingCenter_;
983 float windowingWidth_;
984 Layers layers_;
985
986
987 Layer& RegisterLayer(Layer* layer)
988 {
989 if (layer == NULL)
990 {
991 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
992 }
993
994 std::auto_ptr<Layer> raii(layer);
995
996 size_t index = countLayers_++;
997 raii->SetIndex(index);
998 layers_[index] = raii.release();
999
1000 EmitMessage(GeometryChangedMessage(*this));
1001 EmitMessage(ContentChangedMessage(*this));
1002
1003 return *layer;
1004 }
1005
1006
1007 public:
1008 RadiologyScene(MessageBroker& broker,
1009 OrthancApiClient& orthanc) :
1010 IObserver(broker),
1011 IObservable(broker),
1012 orthanc_(orthanc),
1013 countLayers_(0),
1014 hasWindowing_(false),
1015 windowingCenter_(0), // Dummy initialization
1016 windowingWidth_(0) // Dummy initialization
1017 {
1018 }
1019
1020
1021 virtual ~RadiologyScene()
1022 {
1023 for (Layers::iterator it = layers_.begin(); it != layers_.end(); it++)
1024 {
1025 assert(it->second != NULL);
1026 delete it->second;
1027 }
1028 }
1029
1030
1031 bool GetWindowing(float& center,
1032 float& width) const
1033 {
1034 if (hasWindowing_)
1035 {
1036 center = windowingCenter_;
1037 width = windowingWidth_;
1038 return true;
1039 }
1040 else
1041 {
1042 return false;
1043 }
1044 }
1045
1046
1047 void GetWindowingWithDefault(float& center,
1048 float& width) const
1049 {
1050 if (!GetWindowing(center, width))
1051 {
1052 center = 128;
1053 width = 256;
1054 }
1055 }
1056
1057
1058 void SetWindowing(float center,
1059 float width)
1060
1061 {
1062 hasWindowing_ = true;
1063 windowingCenter_ = center;
1064 windowingWidth_ = width;
1065 }
1066
1067
1068 Layer& LoadText(const Orthanc::Font& font,
1069 const std::string& utf8)
1070 {
1071 std::auto_ptr<AlphaLayer> alpha(new AlphaLayer(*this));
1072 alpha->LoadText(font, utf8);
1073
1074 return RegisterLayer(alpha.release());
1075 }
1076
1077
1078 Layer& LoadTestBlock(unsigned int width,
1079 unsigned int height)
1080 {
1081 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false));
1082
1083 for (unsigned int padding = 0;
1084 (width > 2 * padding) && (height > 2 * padding);
1085 padding++)
1086 {
1087 uint8_t color;
1088 if (255 > 10 * padding)
1089 {
1090 color = 255 - 10 * padding;
1091 }
1092 else
1093 {
1094 color = 0;
1095 }
1096
1097 Orthanc::ImageAccessor region;
1098 block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding);
1099 Orthanc::ImageProcessing::Set(region, color);
1100 }
1101
1102 std::auto_ptr<AlphaLayer> alpha(new AlphaLayer(*this));
1103 alpha->SetAlpha(block.release());
1104
1105 return RegisterLayer(alpha.release());
1106 }
1107
1108
1109 Layer& LoadDicomFrame(const std::string& instance,
1110 unsigned int frame,
1111 bool httpCompression)
1112 {
1113 Layer& layer = RegisterLayer(new DicomLayer);
1114
1115 {
1116 IWebService::Headers headers;
1117 std::string uri = "/instances/" + instance + "/tags";
1118 orthanc_.GetBinaryAsync(uri, headers,
1119 new Callable<RadiologyScene, OrthancApiClient::BinaryResponseReadyMessage>
1120 (*this, &RadiologyScene::OnTagsReceived), NULL,
1121 new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
1122 }
1123
1124 {
1125 IWebService::Headers headers;
1126 headers["Accept"] = "image/x-portable-arbitrarymap";
1127
1128 if (httpCompression)
1129 {
1130 headers["Accept-Encoding"] = "gzip";
1131 }
1132
1133 std::string uri = "/instances/" + instance + "/frames/" + boost::lexical_cast<std::string>(frame) + "/image-uint16";
1134 orthanc_.GetBinaryAsync(uri, headers,
1135 new Callable<RadiologyScene, OrthancApiClient::BinaryResponseReadyMessage>
1136 (*this, &RadiologyScene::OnFrameReceived), NULL,
1137 new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
1138 }
1139
1140 return layer;
1141 }
1142
1143
1144 void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
1145 {
1146 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue();
1147
1148 LOG(INFO) << "JSON received: " << message.GetUri().c_str()
1149 << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
1150
1151 Layers::iterator layer = layers_.find(index);
1152 if (layer != layers_.end())
1153 {
1154 assert(layer->second != NULL);
1155
1156 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize());
1157 dynamic_cast<DicomLayer*>(layer->second)->SetDicomTags(dicom);
1158
1159 float c, w;
1160 if (!hasWindowing_ &&
1161 layer->second->GetDefaultWindowing(c, w))
1162 {
1163 hasWindowing_ = true;
1164 windowingCenter_ = c;
1165 windowingWidth_ = w;
1166 }
1167
1168 EmitMessage(GeometryChangedMessage(*this));
1169 }
1170 }
1171
1172
1173 void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
1174 {
1175 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue();
1176
1177 LOG(INFO) << "DICOM frame received: " << message.GetUri().c_str()
1178 << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
1179
1180 Layers::iterator layer = layers_.find(index);
1181 if (layer != layers_.end())
1182 {
1183 assert(layer->second != NULL);
1184
1185 std::string content;
1186 if (message.GetAnswerSize() > 0)
1187 {
1188 content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize());
1189 }
1190
1191 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader);
1192 reader->ReadFromMemory(content);
1193 dynamic_cast<DicomLayer*>(layer->second)->SetSourceImage(reader.release());
1194
1195 EmitMessage(ContentChangedMessage(*this));
1196 }
1197 }
1198
1199
1200 Extent2D GetSceneExtent() const
1201 {
1202 Extent2D extent;
1203
1204 for (Layers::const_iterator it = layers_.begin();
1205 it != layers_.end(); ++it)
1206 {
1207 assert(it->second != NULL);
1208 extent.Union(it->second->GetExtent());
1209 }
1210
1211 return extent;
1212 }
1213
1214
1215 void Render(Orthanc::ImageAccessor& buffer,
1216 const Matrix& viewTransform,
1217 ImageInterpolation interpolation) const
1218 {
1219 Orthanc::ImageProcessing::Set(buffer, 0);
1220
1221 // Render layers in the background-to-foreground order
1222 for (size_t index = 0; index < countLayers_; index++)
1223 {
1224 Layers::const_iterator it = layers_.find(index);
1225 if (it != layers_.end())
1226 {
1227 assert(it->second != NULL);
1228 it->second->Render(buffer, viewTransform, interpolation);
1229 }
1230 }
1231 }
1232
1233
1234 bool LookupLayer(size_t& index /* out */,
1235 double x,
1236 double y) const
1237 {
1238 // Render layers in the foreground-to-background order
1239 for (size_t i = countLayers_; i > 0; i--)
1240 {
1241 index = i - 1;
1242 Layers::const_iterator it = layers_.find(index);
1243 if (it != layers_.end())
1244 {
1245 assert(it->second != NULL);
1246 if (it->second->Contains(x, y))
1247 {
1248 return true;
1249 }
1250 }
1251 }
1252
1253 return false;
1254 }
1255
1256
1257 void DrawBorder(CairoContext& context,
1258 unsigned int layer,
1259 double zoom)
1260 {
1261 Layers::const_iterator found = layers_.find(layer);
1262
1263 if (found != layers_.end())
1264 {
1265 context.SetSourceColor(255, 0, 0);
1266 found->second->DrawBorders(context, zoom);
1267 }
1268 }
1269
1270
1271 void GetRange(float& minValue,
1272 float& maxValue) const
1273 {
1274 bool first = true;
1275
1276 for (Layers::const_iterator it = layers_.begin();
1277 it != layers_.end(); it++)
1278 {
1279 assert(it->second != NULL);
1280
1281 float a, b;
1282 if (it->second->GetRange(a, b))
1283 {
1284 if (first)
1285 {
1286 minValue = a;
1287 maxValue = b;
1288 first = false;
1289 }
1290 else
1291 {
1292 minValue = std::min(a, minValue);
1293 maxValue = std::max(b, maxValue);
1294 }
1295 }
1296 }
1297
1298 if (first)
1299 {
1300 minValue = 0;
1301 maxValue = 0;
1302 }
1303 }
1304
1305
1306 // Export using PAM is faster than using PNG, but requires Orthanc
1307 // core >= 1.4.3
1308 void Export(const Orthanc::DicomMap& dicom,
1309 double pixelSpacingX,
1310 double pixelSpacingY,
1311 bool invert,
1312 ImageInterpolation interpolation,
1313 bool usePam)
1314 {
1315 if (pixelSpacingX <= 0 ||
1316 pixelSpacingY <= 0)
1317 {
1318 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
1319 }
1320
1321 LOG(INFO) << "Exporting DICOM";
1322
1323 Extent2D extent = GetSceneExtent();
1324
1325 int w = std::ceil(extent.GetWidth() / pixelSpacingX);
1326 int h = std::ceil(extent.GetHeight() / pixelSpacingY);
1327
1328 if (w < 0 || h < 0)
1329 {
1330 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
1331 }
1332
1333 Orthanc::Image layers(Orthanc::PixelFormat_Float32,
1334 static_cast<unsigned int>(w),
1335 static_cast<unsigned int>(h), false);
1336
1337 Matrix view = LinearAlgebra::Product(
1338 CreateScalingMatrix(1.0 / pixelSpacingX, 1.0 / pixelSpacingY),
1339 CreateOffsetMatrix(-extent.GetX1(), -extent.GetY1()));
1340
1341 Render(layers, view, interpolation);
1342
1343 Orthanc::Image rendered(Orthanc::PixelFormat_Grayscale16,
1344 layers.GetWidth(), layers.GetHeight(), false);
1345 Orthanc::ImageProcessing::Convert(rendered, layers);
1346
1347 std::string base64;
1348
1349 {
1350 std::string content;
1351
1352 if (usePam)
1353 {
1354 Orthanc::PamWriter writer;
1355 writer.WriteToMemory(content, rendered);
1356 }
1357 else
1358 {
1359 Orthanc::PngWriter writer;
1360 writer.WriteToMemory(content, rendered);
1361 }
1362
1363 Orthanc::Toolbox::EncodeBase64(base64, content);
1364 }
1365
1366 std::set<Orthanc::DicomTag> tags;
1367 dicom.GetTags(tags);
1368
1369 Json::Value json = Json::objectValue;
1370 json["Tags"] = Json::objectValue;
1371
1372 for (std::set<Orthanc::DicomTag>::const_iterator
1373 tag = tags.begin(); tag != tags.end(); ++tag)
1374 {
1375 const Orthanc::DicomValue& value = dicom.GetValue(*tag);
1376 if (!value.IsNull() &&
1377 !value.IsBinary())
1378 {
1379 json["Tags"][tag->Format()] = value.GetContent();
1380 }
1381 }
1382
1383 json["Tags"][Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION.Format()] =
1384 (invert ? "MONOCHROME1" : "MONOCHROME2");
1385
1386 // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to
1387 // avoid floating-point numbers to grow over 16 characters,
1388 // which would be invalid according to DICOM standard
1389 // ("dciodvfy" would complain).
1390 char buf[32];
1391 sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX);
1392
1393 json["Tags"][Orthanc::DICOM_TAG_PIXEL_SPACING.Format()] = buf;
1394
1395 float center, width;
1396 if (GetWindowing(center, width))
1397 {
1398 json["Tags"][Orthanc::DICOM_TAG_WINDOW_CENTER.Format()] =
1399 boost::lexical_cast<std::string>(boost::math::iround(center));
1400
1401 json["Tags"][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()] =
1402 boost::lexical_cast<std::string>(boost::math::iround(width));
1403 }
1404
1405 // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme
1406 json["Content"] = ("data:" +
1407 std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) +
1408 ";base64," + base64);
1409
1410 orthanc_.PostJsonAsyncExpectJson(
1411 "/tools/create-dicom", json,
1412 new Callable<RadiologyScene, OrthancApiClient::JsonResponseReadyMessage>
1413 (*this, &RadiologyScene::OnDicomExported),
1414 NULL, NULL);
1415 }
1416
1417
1418 void OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message)
1419 {
1420 LOG(INFO) << "DICOM export was successful:"
1421 << message.GetJson().toStyledString();
1422 }
1423 };
1424
1425
1426 class UndoRedoStack : public boost::noncopyable
1427 {
1428 public:
1429 class ICommand : public boost::noncopyable
1430 {
1431 public:
1432 virtual ~ICommand()
1433 {
1434 }
1435
1436 virtual void Undo() const = 0;
1437
1438 virtual void Redo() const = 0;
1439 };
1440
1441 private:
1442 typedef std::list<ICommand*> Stack;
1443
1444 Stack stack_;
1445 Stack::iterator current_;
1446
1447 void Clear(Stack::iterator from)
1448 {
1449 for (Stack::iterator it = from; it != stack_.end(); ++it)
1450 {
1451 assert(*it != NULL);
1452 delete *it;
1453 }
1454
1455 stack_.erase(from, stack_.end());
1456 }
1457
1458 public:
1459 UndoRedoStack() :
1460 current_(stack_.end())
1461 {
1462 }
1463
1464 ~UndoRedoStack()
1465 {
1466 Clear(stack_.begin());
1467 }
1468
1469 void Add(ICommand* command)
1470 {
1471 if (command == NULL)
1472 {
1473 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
1474 }
1475
1476 Clear(current_);
1477
1478 stack_.push_back(command);
1479 current_ = stack_.end();
1480 }
1481
1482 void Undo()
1483 {
1484 if (current_ != stack_.begin())
1485 {
1486 --current_;
1487
1488 assert(*current_ != NULL);
1489 (*current_)->Undo();
1490 }
1491 }
1492
1493 void Redo()
1494 {
1495 if (current_ != stack_.end())
1496 {
1497 assert(*current_ != NULL);
1498 (*current_)->Redo();
1499
1500 ++current_;
1501 }
1502 }
1503 };
1504
1505
1506 class RadiologyLayerCommand : public UndoRedoStack::ICommand
1507 { 55 {
1508 private: 56 private:
1509 RadiologyScene& scene_; 57 RadiographyScene& scene_;
1510 size_t layer_; 58 size_t layer_;
1511 59
1512 protected: 60 protected:
1513 virtual void UndoInternal(RadiologyScene::Layer& layer) const = 0; 61 virtual void UndoInternal(RadiographyScene::Layer& layer) const = 0;
1514 62
1515 virtual void RedoInternal(RadiologyScene::Layer& layer) const = 0; 63 virtual void RedoInternal(RadiographyScene::Layer& layer) const = 0;
1516 64
1517 public: 65 public:
1518 RadiologyLayerCommand(RadiologyScene& scene, 66 RadiographyLayerCommand(RadiographyScene& scene,
1519 size_t layer) : 67 size_t layer) :
1520 scene_(scene), 68 scene_(scene),
1521 layer_(layer) 69 layer_(layer)
1522 { 70 {
1523 } 71 }
1524 72
1525 RadiologyLayerCommand(const RadiologyScene::LayerAccessor& accessor) : 73 RadiographyLayerCommand(const RadiographyScene::LayerAccessor& accessor) :
1526 scene_(accessor.GetScene()), 74 scene_(accessor.GetScene()),
1527 layer_(accessor.GetIndex()) 75 layer_(accessor.GetIndex())
1528 { 76 {
1529 } 77 }
1530 78
1531 virtual void Undo() const 79 virtual void Undo() const
1532 { 80 {
1533 RadiologyScene::LayerAccessor accessor(scene_, layer_); 81 RadiographyScene::LayerAccessor accessor(scene_, layer_);
1534 82
1535 if (accessor.IsValid()) 83 if (accessor.IsValid())
1536 { 84 {
1537 UndoInternal(accessor.GetLayer()); 85 UndoInternal(accessor.GetLayer());
1538 } 86 }
1539 } 87 }
1540 88
1541 virtual void Redo() const 89 virtual void Redo() const
1542 { 90 {
1543 RadiologyScene::LayerAccessor accessor(scene_, layer_); 91 RadiographyScene::LayerAccessor accessor(scene_, layer_);
1544 92
1545 if (accessor.IsValid()) 93 if (accessor.IsValid())
1546 { 94 {
1547 RedoInternal(accessor.GetLayer()); 95 RedoInternal(accessor.GetLayer());
1548 } 96 }
1549 } 97 }
1550 }; 98 };
1551 99
1552 100
1553 class RadiologyLayerRotateTracker : public IWorldSceneMouseTracker 101 class RadiographyLayerRotateTracker : public IWorldSceneMouseTracker
1554 { 102 {
1555 private: 103 private:
1556 UndoRedoStack& undoRedoStack_; 104 UndoRedoStack& undoRedoStack_;
1557 RadiologyScene::LayerAccessor accessor_; 105 RadiographyScene::LayerAccessor accessor_;
1558 double centerX_; 106 double centerX_;
1559 double centerY_; 107 double centerY_;
1560 double originalAngle_; 108 double originalAngle_;
1561 double clickAngle_; 109 double clickAngle_;
1562 bool roundAngles_; 110 bool roundAngles_;
1581 return false; 129 return false;
1582 } 130 }
1583 } 131 }
1584 132
1585 133
1586 class UndoRedoCommand : public RadiologyLayerCommand 134 class UndoRedoCommand : public RadiographyLayerCommand
1587 { 135 {
1588 private: 136 private:
1589 double sourceAngle_; 137 double sourceAngle_;
1590 double targetAngle_; 138 double targetAngle_;
1591 139
1593 { 141 {
1594 return boost::math::iround(angle * 180.0 / boost::math::constants::pi<double>()); 142 return boost::math::iround(angle * 180.0 / boost::math::constants::pi<double>());
1595 } 143 }
1596 144
1597 protected: 145 protected:
1598 virtual void UndoInternal(RadiologyScene::Layer& layer) const 146 virtual void UndoInternal(RadiographyScene::Layer& layer) const
1599 { 147 {
1600 LOG(INFO) << "Undo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; 148 LOG(INFO) << "Undo - Set angle to " << ToDegrees(sourceAngle_) << " degrees";
1601 layer.SetAngle(sourceAngle_); 149 layer.SetAngle(sourceAngle_);
1602 } 150 }
1603 151
1604 virtual void RedoInternal(RadiologyScene::Layer& layer) const 152 virtual void RedoInternal(RadiographyScene::Layer& layer) const
1605 { 153 {
1606 LOG(INFO) << "Redo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; 154 LOG(INFO) << "Redo - Set angle to " << ToDegrees(sourceAngle_) << " degrees";
1607 layer.SetAngle(targetAngle_); 155 layer.SetAngle(targetAngle_);
1608 } 156 }
1609 157
1610 public: 158 public:
1611 UndoRedoCommand(const RadiologyLayerRotateTracker& tracker) : 159 UndoRedoCommand(const RadiographyLayerRotateTracker& tracker) :
1612 RadiologyLayerCommand(tracker.accessor_), 160 RadiographyLayerCommand(tracker.accessor_),
1613 sourceAngle_(tracker.originalAngle_), 161 sourceAngle_(tracker.originalAngle_),
1614 targetAngle_(tracker.accessor_.GetLayer().GetAngle()) 162 targetAngle_(tracker.accessor_.GetLayer().GetAngle())
1615 { 163 {
1616 } 164 }
1617 }; 165 };
1618 166
1619 167
1620 public: 168 public:
1621 RadiologyLayerRotateTracker(UndoRedoStack& undoRedoStack, 169 RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack,
1622 RadiologyScene& scene, 170 RadiographyScene& scene,
1623 const ViewportGeometry& view, 171 const ViewportGeometry& view,
1624 size_t layer, 172 size_t layer,
1625 double x, 173 double x,
1626 double y, 174 double y,
1627 bool roundAngles) : 175 bool roundAngles) :
1628 undoRedoStack_(undoRedoStack), 176 undoRedoStack_(undoRedoStack),
1629 accessor_(scene, layer), 177 accessor_(scene, layer),
1630 roundAngles_(roundAngles) 178 roundAngles_(roundAngles)
1631 { 179 {
1632 if (accessor_.IsValid()) 180 if (accessor_.IsValid())
1686 } 234 }
1687 } 235 }
1688 }; 236 };
1689 237
1690 238
1691 class RadiologyLayerMoveTracker : public IWorldSceneMouseTracker 239 class RadiographyLayerMoveTracker : public IWorldSceneMouseTracker
1692 { 240 {
1693 private: 241 private:
1694 UndoRedoStack& undoRedoStack_; 242 UndoRedoStack& undoRedoStack_;
1695 RadiologyScene::LayerAccessor accessor_; 243 RadiographyScene::LayerAccessor accessor_;
1696 double clickX_; 244 double clickX_;
1697 double clickY_; 245 double clickY_;
1698 double panX_; 246 double panX_;
1699 double panY_; 247 double panY_;
1700 bool oneAxis_; 248 bool oneAxis_;
1701 249
1702 class UndoRedoCommand : public RadiologyLayerCommand 250 class UndoRedoCommand : public RadiographyLayerCommand
1703 { 251 {
1704 private: 252 private:
1705 double sourceX_; 253 double sourceX_;
1706 double sourceY_; 254 double sourceY_;
1707 double targetX_; 255 double targetX_;
1708 double targetY_; 256 double targetY_;
1709 257
1710 protected: 258 protected:
1711 virtual void UndoInternal(RadiologyScene::Layer& layer) const 259 virtual void UndoInternal(RadiographyScene::Layer& layer) const
1712 { 260 {
1713 layer.SetPan(sourceX_, sourceY_); 261 layer.SetPan(sourceX_, sourceY_);
1714 } 262 }
1715 263
1716 virtual void RedoInternal(RadiologyScene::Layer& layer) const 264 virtual void RedoInternal(RadiographyScene::Layer& layer) const
1717 { 265 {
1718 layer.SetPan(targetX_, targetY_); 266 layer.SetPan(targetX_, targetY_);
1719 } 267 }
1720 268
1721 public: 269 public:
1722 UndoRedoCommand(const RadiologyLayerMoveTracker& tracker) : 270 UndoRedoCommand(const RadiographyLayerMoveTracker& tracker) :
1723 RadiologyLayerCommand(tracker.accessor_), 271 RadiographyLayerCommand(tracker.accessor_),
1724 sourceX_(tracker.panX_), 272 sourceX_(tracker.panX_),
1725 sourceY_(tracker.panY_), 273 sourceY_(tracker.panY_),
1726 targetX_(tracker.accessor_.GetLayer().GetPanX()), 274 targetX_(tracker.accessor_.GetLayer().GetPanX()),
1727 targetY_(tracker.accessor_.GetLayer().GetPanY()) 275 targetY_(tracker.accessor_.GetLayer().GetPanY())
1728 { 276 {
1729 } 277 }
1730 }; 278 };
1731 279
1732 280
1733 public: 281 public:
1734 RadiologyLayerMoveTracker(UndoRedoStack& undoRedoStack, 282 RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack,
1735 RadiologyScene& scene, 283 RadiographyScene& scene,
1736 size_t layer, 284 size_t layer,
1737 double x, 285 double x,
1738 double y, 286 double y,
1739 bool oneAxis) : 287 bool oneAxis) :
1740 undoRedoStack_(undoRedoStack), 288 undoRedoStack_(undoRedoStack),
1741 accessor_(scene, layer), 289 accessor_(scene, layer),
1742 clickX_(x), 290 clickX_(x),
1743 clickY_(y), 291 clickY_(y),
1744 oneAxis_(oneAxis) 292 oneAxis_(oneAxis)
1797 } 345 }
1798 } 346 }
1799 }; 347 };
1800 348
1801 349
1802 class RadiologyLayerCropTracker : public IWorldSceneMouseTracker 350 class RadiographyLayerCropTracker : public IWorldSceneMouseTracker
1803 { 351 {
1804 private: 352 private:
1805 UndoRedoStack& undoRedoStack_; 353 UndoRedoStack& undoRedoStack_;
1806 RadiologyScene::LayerAccessor accessor_; 354 RadiographyScene::LayerAccessor accessor_;
1807 RadiologyScene::Corner corner_; 355 RadiographyScene::Corner corner_;
1808 unsigned int cropX_; 356 unsigned int cropX_;
1809 unsigned int cropY_; 357 unsigned int cropY_;
1810 unsigned int cropWidth_; 358 unsigned int cropWidth_;
1811 unsigned int cropHeight_; 359 unsigned int cropHeight_;
1812 360
1813 class UndoRedoCommand : public RadiologyLayerCommand 361 class UndoRedoCommand : public RadiographyLayerCommand
1814 { 362 {
1815 private: 363 private:
1816 unsigned int sourceCropX_; 364 unsigned int sourceCropX_;
1817 unsigned int sourceCropY_; 365 unsigned int sourceCropY_;
1818 unsigned int sourceCropWidth_; 366 unsigned int sourceCropWidth_;
1821 unsigned int targetCropY_; 369 unsigned int targetCropY_;
1822 unsigned int targetCropWidth_; 370 unsigned int targetCropWidth_;
1823 unsigned int targetCropHeight_; 371 unsigned int targetCropHeight_;
1824 372
1825 protected: 373 protected:
1826 virtual void UndoInternal(RadiologyScene::Layer& layer) const 374 virtual void UndoInternal(RadiographyScene::Layer& layer) const
1827 { 375 {
1828 layer.SetCrop(sourceCropX_, sourceCropY_, sourceCropWidth_, sourceCropHeight_); 376 layer.SetCrop(sourceCropX_, sourceCropY_, sourceCropWidth_, sourceCropHeight_);
1829 } 377 }
1830 378
1831 virtual void RedoInternal(RadiologyScene::Layer& layer) const 379 virtual void RedoInternal(RadiographyScene::Layer& layer) const
1832 { 380 {
1833 layer.SetCrop(targetCropX_, targetCropY_, targetCropWidth_, targetCropHeight_); 381 layer.SetCrop(targetCropX_, targetCropY_, targetCropWidth_, targetCropHeight_);
1834 } 382 }
1835 383
1836 public: 384 public:
1837 UndoRedoCommand(const RadiologyLayerCropTracker& tracker) : 385 UndoRedoCommand(const RadiographyLayerCropTracker& tracker) :
1838 RadiologyLayerCommand(tracker.accessor_), 386 RadiographyLayerCommand(tracker.accessor_),
1839 sourceCropX_(tracker.cropX_), 387 sourceCropX_(tracker.cropX_),
1840 sourceCropY_(tracker.cropY_), 388 sourceCropY_(tracker.cropY_),
1841 sourceCropWidth_(tracker.cropWidth_), 389 sourceCropWidth_(tracker.cropWidth_),
1842 sourceCropHeight_(tracker.cropHeight_) 390 sourceCropHeight_(tracker.cropHeight_)
1843 { 391 {
1846 } 394 }
1847 }; 395 };
1848 396
1849 397
1850 public: 398 public:
1851 RadiologyLayerCropTracker(UndoRedoStack& undoRedoStack, 399 RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack,
1852 RadiologyScene& scene, 400 RadiographyScene& scene,
1853 const ViewportGeometry& view, 401 const ViewportGeometry& view,
1854 size_t layer, 402 size_t layer,
1855 double x, 403 double x,
1856 double y, 404 double y,
1857 RadiologyScene::Corner corner) : 405 RadiographyScene::Corner corner) :
1858 undoRedoStack_(undoRedoStack), 406 undoRedoStack_(undoRedoStack),
1859 accessor_(scene, layer), 407 accessor_(scene, layer),
1860 corner_(corner) 408 corner_(corner)
1861 { 409 {
1862 if (accessor_.IsValid()) 410 if (accessor_.IsValid())
1891 { 439 {
1892 if (accessor_.IsValid()) 440 if (accessor_.IsValid())
1893 { 441 {
1894 unsigned int x, y; 442 unsigned int x, y;
1895 443
1896 RadiologyScene::Layer& layer = accessor_.GetLayer(); 444 RadiographyScene::Layer& layer = accessor_.GetLayer();
1897 if (layer.GetPixel(x, y, sceneX, sceneY)) 445 if (layer.GetPixel(x, y, sceneX, sceneY))
1898 { 446 {
1899 unsigned int targetX, targetWidth; 447 unsigned int targetX, targetWidth;
1900 448
1901 if (corner_ == RadiologyScene::Corner_TopLeft || 449 if (corner_ == RadiographyScene::Corner_TopLeft ||
1902 corner_ == RadiologyScene::Corner_BottomLeft) 450 corner_ == RadiographyScene::Corner_BottomLeft)
1903 { 451 {
1904 targetX = std::min(x, cropX_ + cropWidth_); 452 targetX = std::min(x, cropX_ + cropWidth_);
1905 targetWidth = cropX_ + cropWidth_ - targetX; 453 targetWidth = cropX_ + cropWidth_ - targetX;
1906 } 454 }
1907 else 455 else
1910 targetWidth = std::max(x, cropX_) - cropX_; 458 targetWidth = std::max(x, cropX_) - cropX_;
1911 } 459 }
1912 460
1913 unsigned int targetY, targetHeight; 461 unsigned int targetY, targetHeight;
1914 462
1915 if (corner_ == RadiologyScene::Corner_TopLeft || 463 if (corner_ == RadiographyScene::Corner_TopLeft ||
1916 corner_ == RadiologyScene::Corner_TopRight) 464 corner_ == RadiographyScene::Corner_TopRight)
1917 { 465 {
1918 targetY = std::min(y, cropY_ + cropHeight_); 466 targetY = std::min(y, cropY_ + cropHeight_);
1919 targetHeight = cropY_ + cropHeight_ - targetY; 467 targetHeight = cropY_ + cropHeight_ - targetY;
1920 } 468 }
1921 else 469 else
1929 } 477 }
1930 } 478 }
1931 }; 479 };
1932 480
1933 481
1934 class RadiologyLayerResizeTracker : public IWorldSceneMouseTracker 482 class RadiographyLayerResizeTracker : public IWorldSceneMouseTracker
1935 { 483 {
1936 private: 484 private:
1937 UndoRedoStack& undoRedoStack_; 485 UndoRedoStack& undoRedoStack_;
1938 RadiologyScene::LayerAccessor accessor_; 486 RadiographyScene::LayerAccessor accessor_;
1939 bool roundScaling_; 487 bool roundScaling_;
1940 double originalSpacingX_; 488 double originalSpacingX_;
1941 double originalSpacingY_; 489 double originalSpacingY_;
1942 double originalPanX_; 490 double originalPanX_;
1943 double originalPanY_; 491 double originalPanY_;
1944 RadiologyScene::Corner oppositeCorner_; 492 RadiographyScene::Corner oppositeCorner_;
1945 double oppositeX_; 493 double oppositeX_;
1946 double oppositeY_; 494 double oppositeY_;
1947 double baseScaling_; 495 double baseScaling_;
1948 496
1949 static double ComputeDistance(double x1, 497 static double ComputeDistance(double x1,
1950 double y1, 498 double y1,
1951 double x2, 499 double x2,
1952 double y2) 500 double y2)
1954 double dx = x1 - x2; 502 double dx = x1 - x2;
1955 double dy = y1 - y2; 503 double dy = y1 - y2;
1956 return sqrt(dx * dx + dy * dy); 504 return sqrt(dx * dx + dy * dy);
1957 } 505 }
1958 506
1959 class UndoRedoCommand : public RadiologyLayerCommand 507 class UndoRedoCommand : public RadiographyLayerCommand
1960 { 508 {
1961 private: 509 private:
1962 double sourceSpacingX_; 510 double sourceSpacingX_;
1963 double sourceSpacingY_; 511 double sourceSpacingY_;
1964 double sourcePanX_; 512 double sourcePanX_;
1967 double targetSpacingY_; 515 double targetSpacingY_;
1968 double targetPanX_; 516 double targetPanX_;
1969 double targetPanY_; 517 double targetPanY_;
1970 518
1971 protected: 519 protected:
1972 virtual void UndoInternal(RadiologyScene::Layer& layer) const 520 virtual void UndoInternal(RadiographyScene::Layer& layer) const
1973 { 521 {
1974 layer.SetPixelSpacing(sourceSpacingX_, sourceSpacingY_); 522 layer.SetPixelSpacing(sourceSpacingX_, sourceSpacingY_);
1975 layer.SetPan(sourcePanX_, sourcePanY_); 523 layer.SetPan(sourcePanX_, sourcePanY_);
1976 } 524 }
1977 525
1978 virtual void RedoInternal(RadiologyScene::Layer& layer) const 526 virtual void RedoInternal(RadiographyScene::Layer& layer) const
1979 { 527 {
1980 layer.SetPixelSpacing(targetSpacingX_, targetSpacingY_); 528 layer.SetPixelSpacing(targetSpacingX_, targetSpacingY_);
1981 layer.SetPan(targetPanX_, targetPanY_); 529 layer.SetPan(targetPanX_, targetPanY_);
1982 } 530 }
1983 531
1984 public: 532 public:
1985 UndoRedoCommand(const RadiologyLayerResizeTracker& tracker) : 533 UndoRedoCommand(const RadiographyLayerResizeTracker& tracker) :
1986 RadiologyLayerCommand(tracker.accessor_), 534 RadiographyLayerCommand(tracker.accessor_),
1987 sourceSpacingX_(tracker.originalSpacingX_), 535 sourceSpacingX_(tracker.originalSpacingX_),
1988 sourceSpacingY_(tracker.originalSpacingY_), 536 sourceSpacingY_(tracker.originalSpacingY_),
1989 sourcePanX_(tracker.originalPanX_), 537 sourcePanX_(tracker.originalPanX_),
1990 sourcePanY_(tracker.originalPanY_), 538 sourcePanY_(tracker.originalPanY_),
1991 targetSpacingX_(tracker.accessor_.GetLayer().GetPixelSpacingX()), 539 targetSpacingX_(tracker.accessor_.GetLayer().GetPixelSpacingX()),
1996 } 544 }
1997 }; 545 };
1998 546
1999 547
2000 public: 548 public:
2001 RadiologyLayerResizeTracker(UndoRedoStack& undoRedoStack, 549 RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack,
2002 RadiologyScene& scene, 550 RadiographyScene& scene,
2003 size_t layer, 551 size_t layer,
2004 double x, 552 double x,
2005 double y, 553 double y,
2006 RadiologyScene::Corner corner, 554 RadiographyScene::Corner corner,
2007 bool roundScaling) : 555 bool roundScaling) :
2008 undoRedoStack_(undoRedoStack), 556 undoRedoStack_(undoRedoStack),
2009 accessor_(scene, layer), 557 accessor_(scene, layer),
2010 roundScaling_(roundScaling) 558 roundScaling_(roundScaling)
2011 { 559 {
2012 if (accessor_.IsValid() && 560 if (accessor_.IsValid() &&
2017 originalPanX_ = accessor_.GetLayer().GetPanX(); 565 originalPanX_ = accessor_.GetLayer().GetPanX();
2018 originalPanY_ = accessor_.GetLayer().GetPanY(); 566 originalPanY_ = accessor_.GetLayer().GetPanY();
2019 567
2020 switch (corner) 568 switch (corner)
2021 { 569 {
2022 case RadiologyScene::Corner_TopLeft: 570 case RadiographyScene::Corner_TopLeft:
2023 oppositeCorner_ = RadiologyScene::Corner_BottomRight; 571 oppositeCorner_ = RadiographyScene::Corner_BottomRight;
2024 break; 572 break;
2025 573
2026 case RadiologyScene::Corner_TopRight: 574 case RadiographyScene::Corner_TopRight:
2027 oppositeCorner_ = RadiologyScene::Corner_BottomLeft; 575 oppositeCorner_ = RadiographyScene::Corner_BottomLeft;
2028 break; 576 break;
2029 577
2030 case RadiologyScene::Corner_BottomLeft: 578 case RadiographyScene::Corner_BottomLeft:
2031 oppositeCorner_ = RadiologyScene::Corner_TopRight; 579 oppositeCorner_ = RadiographyScene::Corner_TopRight;
2032 break; 580 break;
2033 581
2034 case RadiologyScene::Corner_BottomRight: 582 case RadiographyScene::Corner_BottomRight:
2035 oppositeCorner_ = RadiologyScene::Corner_TopLeft; 583 oppositeCorner_ = RadiographyScene::Corner_TopLeft;
2036 break; 584 break;
2037 585
2038 default: 586 default:
2039 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 587 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
2040 } 588 }
2089 if (roundScaling_) 637 if (roundScaling_)
2090 { 638 {
2091 scaling = boost::math::round<double>((scaling / ROUND_SCALING) * ROUND_SCALING); 639 scaling = boost::math::round<double>((scaling / ROUND_SCALING) * ROUND_SCALING);
2092 } 640 }
2093 641
2094 RadiologyScene::Layer& layer = accessor_.GetLayer(); 642 RadiographyScene::Layer& layer = accessor_.GetLayer();
2095 layer.SetPixelSpacing(scaling * originalSpacingX_, 643 layer.SetPixelSpacing(scaling * originalSpacingX_,
2096 scaling * originalSpacingY_); 644 scaling * originalSpacingY_);
2097 645
2098 // Keep the opposite corner at a fixed location 646 // Keep the opposite corner at a fixed location
2099 double ox, oy; 647 double ox, oy;
2103 } 651 }
2104 } 652 }
2105 }; 653 };
2106 654
2107 655
2108 class RadiologyWindowingTracker : public IWorldSceneMouseTracker 656 class RadiographyWindowingTracker : public IWorldSceneMouseTracker
2109 { 657 {
2110 public: 658 public:
2111 enum Action 659 enum Action
2112 { 660 {
2113 Action_IncreaseWidth, 661 Action_IncreaseWidth,
2115 Action_IncreaseCenter, 663 Action_IncreaseCenter,
2116 Action_DecreaseCenter 664 Action_DecreaseCenter
2117 }; 665 };
2118 666
2119 private: 667 private:
2120 UndoRedoStack& undoRedoStack_; 668 UndoRedoStack& undoRedoStack_;
2121 RadiologyScene& scene_; 669 RadiographyScene& scene_;
2122 int clickX_; 670 int clickX_;
2123 int clickY_; 671 int clickY_;
2124 Action leftAction_; 672 Action leftAction_;
2125 Action rightAction_; 673 Action rightAction_;
2126 Action upAction_; 674 Action upAction_;
2127 Action downAction_; 675 Action downAction_;
2128 float strength_; 676 float strength_;
2129 float sourceCenter_; 677 float sourceCenter_;
2130 float sourceWidth_; 678 float sourceWidth_;
2131 679
2132 static void ComputeAxisEffect(int& deltaCenter, 680 static void ComputeAxisEffect(int& deltaCenter,
2133 int& deltaWidth, 681 int& deltaWidth,
2134 int delta, 682 int delta,
2135 Action actionNegative, 683 Action actionNegative,
2187 735
2188 736
2189 class UndoRedoCommand : public UndoRedoStack::ICommand 737 class UndoRedoCommand : public UndoRedoStack::ICommand
2190 { 738 {
2191 private: 739 private:
2192 RadiologyScene& scene_; 740 RadiographyScene& scene_;
2193 float sourceCenter_; 741 float sourceCenter_;
2194 float sourceWidth_; 742 float sourceWidth_;
2195 float targetCenter_; 743 float targetCenter_;
2196 float targetWidth_; 744 float targetWidth_;
2197 745
2198 public: 746 public:
2199 UndoRedoCommand(const RadiologyWindowingTracker& tracker) : 747 UndoRedoCommand(const RadiographyWindowingTracker& tracker) :
2200 scene_(tracker.scene_), 748 scene_(tracker.scene_),
2201 sourceCenter_(tracker.sourceCenter_), 749 sourceCenter_(tracker.sourceCenter_),
2202 sourceWidth_(tracker.sourceWidth_) 750 sourceWidth_(tracker.sourceWidth_)
2203 { 751 {
2204 scene_.GetWindowingWithDefault(targetCenter_, targetWidth_); 752 scene_.GetWindowingWithDefault(targetCenter_, targetWidth_);
2215 } 763 }
2216 }; 764 };
2217 765
2218 766
2219 public: 767 public:
2220 RadiologyWindowingTracker(UndoRedoStack& undoRedoStack, 768 RadiographyWindowingTracker(UndoRedoStack& undoRedoStack,
2221 RadiologyScene& scene, 769 RadiographyScene& scene,
2222 int x, 770 int x,
2223 int y, 771 int y,
2224 Action leftAction, 772 Action leftAction,
2225 Action rightAction, 773 Action rightAction,
2226 Action upAction, 774 Action upAction,
2227 Action downAction) : 775 Action downAction) :
2228 undoRedoStack_(undoRedoStack), 776 undoRedoStack_(undoRedoStack),
2229 scene_(scene), 777 scene_(scene),
2230 clickX_(x), 778 clickX_(x),
2231 clickY_(y), 779 clickY_(y),
2232 leftAction_(leftAction), 780 leftAction_(leftAction),
2299 scene_.SetWindowing(newCenter, newWidth); 847 scene_.SetWindowing(newCenter, newWidth);
2300 } 848 }
2301 }; 849 };
2302 850
2303 851
2304 class RadiologyWidget : 852 class RadiographyWidget :
2305 public WorldSceneWidget, 853 public WorldSceneWidget,
2306 public IObserver 854 public IObserver
2307 { 855 {
2308 private: 856 private:
2309 RadiologyScene& scene_; 857 RadiographyScene& scene_;
2310 std::auto_ptr<Orthanc::Image> floatBuffer_; 858 std::auto_ptr<Orthanc::Image> floatBuffer_;
2311 std::auto_ptr<CairoSurface> cairoBuffer_; 859 std::auto_ptr<CairoSurface> cairoBuffer_;
2312 bool invert_; 860 bool invert_;
2313 ImageInterpolation interpolation_; 861 ImageInterpolation interpolation_;
2314 bool hasSelection_; 862 bool hasSelection_;
2426 974
2427 return true; 975 return true;
2428 } 976 }
2429 977
2430 public: 978 public:
2431 RadiologyWidget(MessageBroker& broker, 979 RadiographyWidget(MessageBroker& broker,
2432 RadiologyScene& scene, 980 RadiographyScene& scene,
2433 const std::string& name) : 981 const std::string& name) :
2434 WorldSceneWidget(name), 982 WorldSceneWidget(name),
2435 IObserver(broker), 983 IObserver(broker),
2436 scene_(scene), 984 scene_(scene),
2437 invert_(false), 985 invert_(false),
2438 interpolation_(ImageInterpolation_Nearest), 986 interpolation_(ImageInterpolation_Nearest),
2439 hasSelection_(false), 987 hasSelection_(false),
2440 selectedLayer_(0) // Dummy initialization 988 selectedLayer_(0) // Dummy initialization
2441 { 989 {
2442 scene.RegisterObserverCallback( 990 scene.RegisterObserverCallback(
2443 new Callable<RadiologyWidget, RadiologyScene::GeometryChangedMessage> 991 new Callable<RadiographyWidget, RadiographyScene::GeometryChangedMessage>
2444 (*this, &RadiologyWidget::OnGeometryChanged)); 992 (*this, &RadiographyWidget::OnGeometryChanged));
2445 993
2446 scene.RegisterObserverCallback( 994 scene.RegisterObserverCallback(
2447 new Callable<RadiologyWidget, RadiologyScene::ContentChangedMessage> 995 new Callable<RadiographyWidget, RadiographyScene::ContentChangedMessage>
2448 (*this, &RadiologyWidget::OnContentChanged)); 996 (*this, &RadiographyWidget::OnContentChanged));
2449 } 997 }
2450 998
2451 RadiologyScene& GetScene() const 999 RadiographyScene& GetScene() const
2452 { 1000 {
2453 return scene_; 1001 return scene_;
2454 } 1002 }
2455 1003
2456 void Unselect() 1004 void Unselect()
2475 { 1023 {
2476 return false; 1024 return false;
2477 } 1025 }
2478 } 1026 }
2479 1027
2480 void OnGeometryChanged(const RadiologyScene::GeometryChangedMessage& message) 1028 void OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message)
2481 { 1029 {
2482 LOG(INFO) << "Geometry has changed"; 1030 LOG(INFO) << "Geometry has changed";
2483 FitContent(); 1031 FitContent();
2484 } 1032 }
2485 1033
2486 void OnContentChanged(const RadiologyScene::ContentChangedMessage& message) 1034 void OnContentChanged(const RadiographyScene::ContentChangedMessage& message)
2487 { 1035 {
2488 LOG(INFO) << "Content has changed"; 1036 LOG(INFO) << "Content has changed";
2489 NotifyContentChanged(); 1037 NotifyContentChanged();
2490 } 1038 }
2491 1039
2525 }; 1073 };
2526 1074
2527 1075
2528 namespace Samples 1076 namespace Samples
2529 { 1077 {
2530 class RadiologyEditorInteractor : 1078 class RadiographyEditorInteractor :
2531 public IWorldSceneInteractor, 1079 public IWorldSceneInteractor,
2532 public IObserver 1080 public IObserver
2533 { 1081 {
2534 private: 1082 private:
2535 enum Tool 1083 enum Tool
2551 return 10.0; 1099 return 10.0;
2552 } 1100 }
2553 1101
2554 1102
2555 public: 1103 public:
2556 RadiologyEditorInteractor(MessageBroker& broker) : 1104 RadiographyEditorInteractor(MessageBroker& broker) :
2557 IObserver(broker), 1105 IObserver(broker),
2558 tool_(Tool_Move) 1106 tool_(Tool_Move)
2559 { 1107 {
2560 } 1108 }
2561 1109
2567 int viewportY, 1115 int viewportY,
2568 double x, 1116 double x,
2569 double y, 1117 double y,
2570 IStatusBar* statusBar) 1118 IStatusBar* statusBar)
2571 { 1119 {
2572 RadiologyWidget& widget = dynamic_cast<RadiologyWidget&>(worldWidget); 1120 RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
2573 1121
2574 if (button == MouseButton_Left) 1122 if (button == MouseButton_Left)
2575 { 1123 {
2576 size_t selected; 1124 size_t selected;
2577 1125
2578 if (tool_ == Tool_Windowing) 1126 if (tool_ == Tool_Windowing)
2579 { 1127 {
2580 return new RadiologyWindowingTracker(undoRedoStack_, widget.GetScene(), 1128 return new RadiographyWindowingTracker(
2581 viewportX, viewportY, 1129 undoRedoStack_, widget.GetScene(),
2582 RadiologyWindowingTracker::Action_DecreaseWidth, 1130 viewportX, viewportY,
2583 RadiologyWindowingTracker::Action_IncreaseWidth, 1131 RadiographyWindowingTracker::Action_DecreaseWidth,
2584 RadiologyWindowingTracker::Action_DecreaseCenter, 1132 RadiographyWindowingTracker::Action_IncreaseWidth,
2585 RadiologyWindowingTracker::Action_IncreaseCenter); 1133 RadiographyWindowingTracker::Action_DecreaseCenter,
1134 RadiographyWindowingTracker::Action_IncreaseCenter);
2586 } 1135 }
2587 else if (!widget.LookupSelectedLayer(selected)) 1136 else if (!widget.LookupSelectedLayer(selected))
2588 { 1137 {
2589 // No layer is currently selected 1138 // No layer is currently selected
2590 size_t layer; 1139 size_t layer;
2596 return NULL; 1145 return NULL;
2597 } 1146 }
2598 else if (tool_ == Tool_Crop || 1147 else if (tool_ == Tool_Crop ||
2599 tool_ == Tool_Resize) 1148 tool_ == Tool_Resize)
2600 { 1149 {
2601 RadiologyScene::LayerAccessor accessor(widget.GetScene(), selected); 1150 RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected);
2602 RadiologyScene::Corner corner; 1151 RadiographyScene::Corner corner;
2603 if (accessor.GetLayer().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize())) 1152 if (accessor.GetLayer().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize()))
2604 { 1153 {
2605 switch (tool_) 1154 switch (tool_)
2606 { 1155 {
2607 case Tool_Crop: 1156 case Tool_Crop:
2608 return new RadiologyLayerCropTracker(undoRedoStack_, widget.GetScene(), view, selected, x, y, corner); 1157 return new RadiographyLayerCropTracker
1158 (undoRedoStack_, widget.GetScene(), view, selected, x, y, corner);
2609 1159
2610 case Tool_Resize: 1160 case Tool_Resize:
2611 return new RadiologyLayerResizeTracker(undoRedoStack_, widget.GetScene(), selected, x, y, corner, 1161 return new RadiographyLayerResizeTracker
2612 (modifiers & KeyboardModifiers_Shift)); 1162 (undoRedoStack_, widget.GetScene(), selected, x, y, corner,
1163 (modifiers & KeyboardModifiers_Shift));
2613 1164
2614 default: 1165 default:
2615 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 1166 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
2616 } 1167 }
2617 } 1168 }
2640 if (layer == selected) 1191 if (layer == selected)
2641 { 1192 {
2642 switch (tool_) 1193 switch (tool_)
2643 { 1194 {
2644 case Tool_Move: 1195 case Tool_Move:
2645 return new RadiologyLayerMoveTracker(undoRedoStack_, widget.GetScene(), layer, x, y, 1196 return new RadiographyLayerMoveTracker
2646 (modifiers & KeyboardModifiers_Shift)); 1197 (undoRedoStack_, widget.GetScene(), layer, x, y,
1198 (modifiers & KeyboardModifiers_Shift));
2647 1199
2648 case Tool_Rotate: 1200 case Tool_Rotate:
2649 return new RadiologyLayerRotateTracker(undoRedoStack_, widget.GetScene(), view, layer, x, y, 1201 return new RadiographyLayerRotateTracker
2650 (modifiers & KeyboardModifiers_Shift)); 1202 (undoRedoStack_, widget.GetScene(), view, layer, x, y,
1203 (modifiers & KeyboardModifiers_Shift));
2651 1204
2652 default: 1205 default:
2653 break; 1206 break;
2654 } 1207 }
2655 1208
2679 const ViewportGeometry& view, 1232 const ViewportGeometry& view,
2680 double x, 1233 double x,
2681 double y, 1234 double y,
2682 IStatusBar* statusBar) 1235 IStatusBar* statusBar)
2683 { 1236 {
2684 RadiologyWidget& widget = dynamic_cast<RadiologyWidget&>(worldWidget); 1237 RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
2685 1238
2686 #if 0 1239 #if 0
2687 if (statusBar != NULL) 1240 if (statusBar != NULL)
2688 { 1241 {
2689 char buf[64]; 1242 char buf[64];
2696 1249
2697 if (widget.LookupSelectedLayer(selected) && 1250 if (widget.LookupSelectedLayer(selected) &&
2698 (tool_ == Tool_Crop || 1251 (tool_ == Tool_Crop ||
2699 tool_ == Tool_Resize)) 1252 tool_ == Tool_Resize))
2700 { 1253 {
2701 RadiologyScene::LayerAccessor accessor(widget.GetScene(), selected); 1254 RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected);
2702 1255
2703 RadiologyScene::Corner corner; 1256 RadiographyScene::Corner corner;
2704 if (accessor.GetLayer().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize())) 1257 if (accessor.GetLayer().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize()))
2705 { 1258 {
2706 accessor.GetLayer().GetCorner(x, y, corner); 1259 accessor.GetLayer().GetCorner(x, y, corner);
2707 1260
2708 double z = 1.0 / view.GetZoom(); 1261 double z = 1.0 / view.GetZoom();
2731 KeyboardKeys key, 1284 KeyboardKeys key,
2732 char keyChar, 1285 char keyChar,
2733 KeyboardModifiers modifiers, 1286 KeyboardModifiers modifiers,
2734 IStatusBar* statusBar) 1287 IStatusBar* statusBar)
2735 { 1288 {
2736 RadiologyWidget& widget = dynamic_cast<RadiologyWidget&>(worldWidget); 1289 RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
2737 1290
2738 switch (keyChar) 1291 switch (keyChar)
2739 { 1292 {
2740 case 'a': 1293 case 'a':
2741 widget.FitContent(); 1294 widget.FitContent();
2765 tags.SetValue(Orthanc::DICOM_TAG_SERIES_NUMBER, "1", false); 1318 tags.SetValue(Orthanc::DICOM_TAG_SERIES_NUMBER, "1", false);
2766 tags.SetValue(Orthanc::DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.1", false); 1319 tags.SetValue(Orthanc::DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.1", false);
2767 tags.SetValue(Orthanc::DICOM_TAG_STUDY_ID, "STUDY", false); 1320 tags.SetValue(Orthanc::DICOM_TAG_STUDY_ID, "STUDY", false);
2768 tags.SetValue(Orthanc::DICOM_TAG_VIEW_POSITION, "", false); 1321 tags.SetValue(Orthanc::DICOM_TAG_VIEW_POSITION, "", false);
2769 1322
2770 widget.GetScene().Export(tags, 0.1, 0.1, widget.IsInverted(), 1323 widget.GetScene().ExportDicom(tags, 0.1, 0.1, widget.IsInverted(),
2771 widget.GetInterpolation(), EXPORT_USING_PAM); 1324 widget.GetInterpolation(), EXPORT_USING_PAM);
2772 break; 1325 break;
2773 } 1326 }
2774 1327
2775 case 'i': 1328 case 'i':
2776 widget.SwitchInvert(); 1329 widget.SwitchInvert();
2841 public SampleSingleCanvasApplicationBase, 1394 public SampleSingleCanvasApplicationBase,
2842 public IObserver 1395 public IObserver
2843 { 1396 {
2844 private: 1397 private:
2845 std::auto_ptr<OrthancApiClient> orthancApiClient_; 1398 std::auto_ptr<OrthancApiClient> orthancApiClient_;
2846 std::auto_ptr<RadiologyScene> scene_; 1399 std::auto_ptr<RadiographyScene> scene_;
2847 RadiologyEditorInteractor interactor_; 1400 RadiographyEditorInteractor interactor_;
2848 1401
2849 public: 1402 public:
2850 SingleFrameEditorApplication(MessageBroker& broker) : 1403 SingleFrameEditorApplication(MessageBroker& broker) :
2851 IObserver(broker), 1404 IObserver(broker),
2852 interactor_(broker) 1405 interactor_(broker)
2905 orthancApiClient_.reset(new OrthancApiClient(GetBroker(), context_->GetWebService())); 1458 orthancApiClient_.reset(new OrthancApiClient(GetBroker(), context_->GetWebService()));
2906 1459
2907 Orthanc::FontRegistry fonts; 1460 Orthanc::FontRegistry fonts;
2908 fonts.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); 1461 fonts.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
2909 1462
2910 scene_.reset(new RadiologyScene(GetBroker(), *orthancApiClient_)); 1463 scene_.reset(new RadiographyScene(GetBroker(), *orthancApiClient_));
2911 scene_->LoadDicomFrame(instance, frame, false); //.SetPan(200, 0); 1464 scene_->LoadDicomFrame(instance, frame, false); //.SetPan(200, 0);
2912 //scene_->LoadDicomFrame("61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false); 1465 //scene_->LoadDicomFrame("61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false);
2913 1466
2914 { 1467 {
2915 RadiologyScene::Layer& layer = scene_->LoadText(fonts.GetFont(0), "Hello\nworld"); 1468 RadiographyScene::Layer& layer = scene_->LoadText(fonts.GetFont(0), "Hello\nworld");
2916 //dynamic_cast<RadiologyScene::Layer&>(layer).SetForegroundValue(256); 1469 //dynamic_cast<RadiographyScene::Layer&>(layer).SetForegroundValue(256);
2917 dynamic_cast<RadiologyScene::Layer&>(layer).SetResizeable(true); 1470 dynamic_cast<RadiographyScene::Layer&>(layer).SetResizeable(true);
2918 } 1471 }
2919 1472
2920 { 1473 {
2921 RadiologyScene::Layer& layer = scene_->LoadTestBlock(100, 50); 1474 RadiographyScene::Layer& layer = scene_->LoadTestBlock(100, 50);
2922 //dynamic_cast<RadiologyScene::Layer&>(layer).SetForegroundValue(256); 1475 //dynamic_cast<RadiographyScene::Layer&>(layer).SetForegroundValue(256);
2923 dynamic_cast<RadiologyScene::Layer&>(layer).SetResizeable(true); 1476 dynamic_cast<RadiographyScene::Layer&>(layer).SetResizeable(true);
2924 dynamic_cast<RadiologyScene::Layer&>(layer).SetPan(0, 200); 1477 dynamic_cast<RadiographyScene::Layer&>(layer).SetPan(0, 200);
2925 } 1478 }
2926 1479
2927 1480
2928 mainWidget_ = new RadiologyWidget(GetBroker(), *scene_, "main-widget"); 1481 mainWidget_ = new RadiographyWidget(GetBroker(), *scene_, "main-widget");
2929 mainWidget_->SetTransmitMouseOver(true); 1482 mainWidget_->SetTransmitMouseOver(true);
2930 mainWidget_->SetInteractor(interactor_); 1483 mainWidget_->SetInteractor(interactor_);
2931 1484
2932 //scene_->SetWindowing(128, 256); 1485 //scene_->SetWindowing(128, 256);
2933 } 1486 }