comparison Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp @ 1804:5a872e69c74f

reorganization
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 20 May 2021 14:48:51 +0200
parents d1849468729b
children de7cea710008
comparison
equal deleted inserted replaced
1803:d1849468729b 1804:5a872e69c74f
22 #include "SdlSimpleViewerApplication.h" 22 #include "SdlSimpleViewerApplication.h"
23 #include "../SdlHelpers.h" 23 #include "../SdlHelpers.h"
24 #include "../../Common/SampleHelpers.h" 24 #include "../../Common/SampleHelpers.h"
25 25
26 #include "../../../../OrthancStone/Sources/Loaders/GenericLoadersContext.h" 26 #include "../../../../OrthancStone/Sources/Loaders/GenericLoadersContext.h"
27 #include "../../../../OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h"
27 #include "../../../../OrthancStone/Sources/Scene2DViewport/AngleMeasureTool.h" 28 #include "../../../../OrthancStone/Sources/Scene2DViewport/AngleMeasureTool.h"
28 #include "../../../../OrthancStone/Sources/Scene2DViewport/LineMeasureTool.h" 29 #include "../../../../OrthancStone/Sources/Scene2DViewport/LineMeasureTool.h"
29 #include "../../../../OrthancStone/Sources/Scene2DViewport/UndoStack.h" 30 #include "../../../../OrthancStone/Sources/Scene2DViewport/UndoStack.h"
30 #include "../../../../OrthancStone/Sources/StoneEnumerations.h"
31 #include "../../../../OrthancStone/Sources/StoneException.h" 31 #include "../../../../OrthancStone/Sources/StoneException.h"
32 #include "../../../../OrthancStone/Sources/StoneInitialization.h" 32 #include "../../../../OrthancStone/Sources/StoneInitialization.h"
33 #include "../../../../OrthancStone/Sources/Viewport/DefaultViewportInteractor.h" 33 #include "../../../../OrthancStone/Sources/Viewport/DefaultViewportInteractor.h"
34 #include "../../../Platforms/Sdl/SdlViewport.h" 34 #include "../../../Platforms/Sdl/SdlViewport.h"
35 35
41 #include <SDL.h> 41 #include <SDL.h>
42 42
43 #include <string> 43 #include <string>
44 44
45 45
46 46 static std::string orthancUrl;
47 #if 1 47 static std::string instanceId;
48 #include "../../../../OrthancStone/Sources/Scene2D/MacroSceneLayer.h" 48 static int frameIndex = 0;
49
50 #include <boost/math/constants/constants.hpp>
51
52 static const double HANDLE_SIZE = 10.0;
53 static const double PI = boost::math::constants::pi<double>();
54
55 static const char* const KEY_ANNOTATIONS = "annotations";
56 static const char* const KEY_TYPE = "type";
57 static const char* const KEY_X1 = "x1";
58 static const char* const KEY_Y1 = "y1";
59 static const char* const KEY_X2 = "x2";
60 static const char* const KEY_Y2 = "y2";
61 static const char* const KEY_X3 = "x3";
62 static const char* const KEY_Y3 = "y3";
63
64 static const char* const VALUE_ANGLE = "angle";
65 static const char* const VALUE_CIRCLE = "circle";
66 static const char* const VALUE_SEGMENT = "segment";
67
68
69 namespace OrthancStone
70 {
71 class AnnotationsLayer : public IObservable
72 {
73 public:
74 ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, AnnotationAddedMessage, AnnotationsLayer);
75 ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, AnnotationRemovedMessage, AnnotationsLayer);
76
77 enum Tool
78 {
79 Tool_Edit,
80 Tool_None,
81 Tool_Segment,
82 Tool_Angle,
83 Tool_Circle,
84 Tool_Erase
85 };
86
87 private:
88 class Annotation;
89
90 class GeometricPrimitive : public boost::noncopyable
91 {
92 private:
93 bool modified_;
94 Annotation& parentAnnotation_;
95 Color color_;
96 Color hoverColor_;
97 bool isHover_;
98 int depth_;
99
100 public:
101 GeometricPrimitive(Annotation& parentAnnotation,
102 int depth) :
103 modified_(true),
104 parentAnnotation_(parentAnnotation),
105 color_(192, 192, 192),
106 hoverColor_(0, 255, 0),
107 isHover_(false),
108 depth_(depth)
109 {
110 }
111
112 virtual ~GeometricPrimitive()
113 {
114 }
115
116 Annotation& GetParentAnnotation() const
117 {
118 return parentAnnotation_;
119 }
120
121 int GetDepth() const
122 {
123 return depth_;
124 }
125
126 void SetHover(bool hover)
127 {
128 if (hover != isHover_)
129 {
130 isHover_ = hover;
131 modified_ = true;
132 }
133 }
134
135 bool IsHover() const
136 {
137 return isHover_;
138 }
139
140 void SetModified(bool modified)
141 {
142 modified_ = modified;
143 }
144
145 bool IsModified() const
146 {
147 return modified_;
148 }
149
150 void SetColor(const Color& color)
151 {
152 SetModified(true);
153 color_ = color;
154 }
155
156 void SetHoverColor(const Color& color)
157 {
158 SetModified(true);
159 hoverColor_ = color;
160 }
161
162 const Color& GetColor() const
163 {
164 return color_;
165 }
166
167 const Color& GetHoverColor() const
168 {
169 return hoverColor_;
170 }
171
172 virtual bool IsHit(const ScenePoint2D& p,
173 const Scene2D& scene) const = 0;
174
175 // Always called, even if not modified
176 virtual void RenderPolylineLayer(PolylineSceneLayer& polyline,
177 const Scene2D& scene) = 0;
178
179 // Only called if modified
180 virtual void RenderOtherLayers(MacroSceneLayer& macro,
181 const Scene2D& scene) = 0;
182
183 virtual void MovePreview(const ScenePoint2D& delta) = 0;
184
185 virtual void MoveDone(const ScenePoint2D& delta) = 0;
186 };
187
188
189 class Annotation : public boost::noncopyable
190 {
191 private:
192 typedef std::list<GeometricPrimitive*> GeometricPrimitives;
193
194 AnnotationsLayer& that_;
195 GeometricPrimitives primitives_;
196
197 public:
198 Annotation(AnnotationsLayer& that) :
199 that_(that)
200 {
201 that.AddAnnotation(this);
202 }
203
204 virtual ~Annotation()
205 {
206 for (GeometricPrimitives::iterator it = primitives_.begin(); it != primitives_.end(); ++it)
207 {
208 that_.DeletePrimitive(*it);
209 }
210 }
211
212 GeometricPrimitive* AddPrimitive(GeometricPrimitive* primitive)
213 {
214 if (primitive == NULL)
215 {
216 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
217 }
218 else
219 {
220 assert(that_.primitives_.find(primitive) == that_.primitives_.end());
221 primitives_.push_back(primitive); // For automated deallocation
222 that_.primitives_.insert(primitive);
223 return primitive;
224 }
225 }
226
227 template <typename T>
228 T& AddTypedPrimitive(T* primitive)
229 {
230 AddPrimitive(primitive);
231 return *primitive;
232 }
233
234 virtual void SignalMove(GeometricPrimitive& primitive) = 0;
235
236 virtual void Serialize(Json::Value& target) = 0;
237 };
238
239
240 class Handle : public GeometricPrimitive
241 {
242 private:
243 ScenePoint2D center_;
244 ScenePoint2D delta_;
245
246 public:
247 explicit Handle(Annotation& parentAnnotation,
248 const ScenePoint2D& center) :
249 GeometricPrimitive(parentAnnotation, 0), // Highest priority
250 center_(center),
251 delta_(0, 0)
252 {
253 }
254
255 void SetSize(unsigned int size)
256 {
257 SetModified(true);
258 }
259
260 void SetCenter(const ScenePoint2D& center)
261 {
262 SetModified(true);
263 center_ = center;
264 delta_ = ScenePoint2D(0, 0);
265 }
266
267 ScenePoint2D GetCenter() const
268 {
269 return center_ + delta_;
270 }
271
272 virtual bool IsHit(const ScenePoint2D& p,
273 const Scene2D& scene) const
274 {
275 const double zoom = scene.GetSceneToCanvasTransform().ComputeZoom();
276
277 double dx = (center_.GetX() + delta_.GetX() - p.GetX()) * zoom;
278 double dy = (center_.GetY() + delta_.GetY() - p.GetY()) * zoom;
279
280 return (std::abs(dx) <= HANDLE_SIZE / 2.0 &&
281 std::abs(dy) <= HANDLE_SIZE / 2.0);
282 }
283
284 virtual void RenderPolylineLayer(PolylineSceneLayer& polyline,
285 const Scene2D& scene) ORTHANC_OVERRIDE
286 {
287 const double zoom = scene.GetSceneToCanvasTransform().ComputeZoom();
288
289 // TODO: take DPI into account
290 double x1 = center_.GetX() + delta_.GetX() - (HANDLE_SIZE / 2.0) / zoom;
291 double y1 = center_.GetY() + delta_.GetY() - (HANDLE_SIZE / 2.0) / zoom;
292 double x2 = center_.GetX() + delta_.GetX() + (HANDLE_SIZE / 2.0) / zoom;
293 double y2 = center_.GetY() + delta_.GetY() + (HANDLE_SIZE / 2.0) / zoom;
294
295 PolylineSceneLayer::Chain chain;
296 chain.reserve(4);
297 chain.push_back(ScenePoint2D(x1, y1));
298 chain.push_back(ScenePoint2D(x2, y1));
299 chain.push_back(ScenePoint2D(x2, y2));
300 chain.push_back(ScenePoint2D(x1, y2));
301
302 if (IsHover())
303 {
304 polyline.AddChain(chain, true /* closed */, GetHoverColor());
305 }
306 else
307 {
308 polyline.AddChain(chain, true /* closed */, GetColor());
309 }
310 }
311
312 virtual void RenderOtherLayers(MacroSceneLayer& macro,
313 const Scene2D& scene) ORTHANC_OVERRIDE
314 {
315 }
316
317 virtual void MovePreview(const ScenePoint2D& delta) ORTHANC_OVERRIDE
318 {
319 SetModified(true);
320 delta_ = delta;
321 GetParentAnnotation().SignalMove(*this);
322 }
323
324 virtual void MoveDone(const ScenePoint2D& delta) ORTHANC_OVERRIDE
325 {
326 SetModified(true);
327 center_ = center_ + delta;
328 delta_ = ScenePoint2D(0, 0);
329 GetParentAnnotation().SignalMove(*this);
330 }
331 };
332
333
334 class Segment : public GeometricPrimitive
335 {
336 private:
337 ScenePoint2D p1_;
338 ScenePoint2D p2_;
339 ScenePoint2D delta_;
340
341 public:
342 Segment(Annotation& parentAnnotation,
343 const ScenePoint2D& p1,
344 const ScenePoint2D& p2) :
345 GeometricPrimitive(parentAnnotation, 1), // Can only be selected if no handle matches
346 p1_(p1),
347 p2_(p2),
348 delta_(0, 0)
349 {
350 }
351
352 void SetPosition(const ScenePoint2D& p1,
353 const ScenePoint2D& p2)
354 {
355 SetModified(true);
356 p1_ = p1;
357 p2_ = p2;
358 delta_ = ScenePoint2D(0, 0);
359 }
360
361 ScenePoint2D GetPosition1() const
362 {
363 return p1_ + delta_;
364 }
365
366 ScenePoint2D GetPosition2() const
367 {
368 return p2_ + delta_;
369 }
370
371 virtual bool IsHit(const ScenePoint2D& p,
372 const Scene2D& scene) const
373 {
374 const double zoom = scene.GetSceneToCanvasTransform().ComputeZoom();
375 return (ScenePoint2D::SquaredDistancePtSegment(p1_ + delta_, p2_ + delta_, p) * zoom * zoom <=
376 (HANDLE_SIZE / 2.0) * (HANDLE_SIZE / 2.0));
377 }
378
379 virtual void RenderPolylineLayer(PolylineSceneLayer& polyline,
380 const Scene2D& scene) ORTHANC_OVERRIDE
381 {
382 PolylineSceneLayer::Chain chain;
383 chain.reserve(2);
384 chain.push_back(p1_ + delta_);
385 chain.push_back(p2_ + delta_);
386
387 if (IsHover())
388 {
389 polyline.AddChain(chain, false /* closed */, GetHoverColor());
390 }
391 else
392 {
393 polyline.AddChain(chain, false /* closed */, GetColor());
394 }
395 }
396
397 virtual void RenderOtherLayers(MacroSceneLayer& macro,
398 const Scene2D& scene) ORTHANC_OVERRIDE
399 {
400 }
401
402 virtual void MovePreview(const ScenePoint2D& delta) ORTHANC_OVERRIDE
403 {
404 SetModified(true);
405 delta_ = delta;
406 GetParentAnnotation().SignalMove(*this);
407 }
408
409 virtual void MoveDone(const ScenePoint2D& delta) ORTHANC_OVERRIDE
410 {
411 SetModified(true);
412 p1_ = p1_ + delta;
413 p2_ = p2_ + delta;
414 delta_ = ScenePoint2D(0, 0);
415 GetParentAnnotation().SignalMove(*this);
416 }
417 };
418
419
420 class Circle : public GeometricPrimitive
421 {
422 private:
423 ScenePoint2D p1_;
424 ScenePoint2D p2_;
425
426 public:
427 Circle(Annotation& parentAnnotation,
428 const ScenePoint2D& p1,
429 const ScenePoint2D& p2) :
430 GeometricPrimitive(parentAnnotation, 2),
431 p1_(p1),
432 p2_(p2)
433 {
434 }
435
436 void SetPosition(const ScenePoint2D& p1,
437 const ScenePoint2D& p2)
438 {
439 SetModified(true);
440 p1_ = p1;
441 p2_ = p2;
442 }
443
444 ScenePoint2D GetPosition1() const
445 {
446 return p1_;
447 }
448
449 ScenePoint2D GetPosition2() const
450 {
451 return p2_;
452 }
453
454 virtual bool IsHit(const ScenePoint2D& p,
455 const Scene2D& scene) const
456 {
457 return false;
458 }
459
460 virtual void RenderPolylineLayer(PolylineSceneLayer& polyline,
461 const Scene2D& scene) ORTHANC_OVERRIDE
462 {
463 static unsigned int NUM_SEGMENTS = 128;
464
465 ScenePoint2D middle((p1_.GetX() + p2_.GetX()) / 2.0,
466 (p1_.GetY() + p2_.GetY()) / 2.0);
467
468 const double radius = ScenePoint2D::DistancePtPt(middle, p1_);
469
470 double increment = 2.0 * PI / static_cast<double>(NUM_SEGMENTS - 1);
471
472 PolylineSceneLayer::Chain chain;
473 chain.reserve(NUM_SEGMENTS);
474
475 double theta = 0;
476 for (unsigned int i = 0; i < NUM_SEGMENTS; i++)
477 {
478 chain.push_back(ScenePoint2D(middle.GetX() + radius * cos(theta),
479 middle.GetY() + radius * sin(theta)));
480 theta += increment;
481 }
482
483 if (IsHover())
484 {
485 polyline.AddChain(chain, false /* closed */, GetHoverColor());
486 }
487 else
488 {
489 polyline.AddChain(chain, false /* closed */, GetColor());
490 }
491 }
492
493 virtual void RenderOtherLayers(MacroSceneLayer& macro,
494 const Scene2D& scene) ORTHANC_OVERRIDE
495 {
496 }
497
498 virtual void MovePreview(const ScenePoint2D& delta) ORTHANC_OVERRIDE
499 {
500 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // No hit is possible
501 }
502
503 virtual void MoveDone(const ScenePoint2D& delta) ORTHANC_OVERRIDE
504 {
505 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // No hit is possible
506 }
507 };
508
509
510 class Arc : public GeometricPrimitive
511 {
512 private:
513 ScenePoint2D start_;
514 ScenePoint2D middle_;
515 ScenePoint2D end_;
516 double radius_; // in pixels
517
518 void ComputeAngles(double& fullAngle,
519 double& startAngle,
520 double& endAngle) const
521 {
522 const double x1 = start_.GetX();
523 const double y1 = start_.GetY();
524 const double xc = middle_.GetX();
525 const double yc = middle_.GetY();
526 const double x2 = end_.GetX();
527 const double y2 = end_.GetY();
528
529 startAngle = atan2(y1 - yc, x1 - xc);
530 endAngle = atan2(y2 - yc, x2 - xc);
531
532 fullAngle = endAngle - startAngle;
533
534 while (fullAngle < -PI)
535 {
536 fullAngle += 2.0 * PI;
537 }
538
539 while (fullAngle >= PI)
540 {
541 fullAngle -= 2.0 * PI;
542 }
543 }
544
545 public:
546 Arc(Annotation& parentAnnotation,
547 const ScenePoint2D& start,
548 const ScenePoint2D& middle,
549 const ScenePoint2D& end) :
550 GeometricPrimitive(parentAnnotation, 2),
551 start_(start),
552 middle_(middle),
553 end_(end),
554 radius_(20)
555 {
556 }
557
558 double GetAngle() const
559 {
560 double fullAngle, startAngle, endAngle;
561 ComputeAngles(fullAngle, startAngle, endAngle);
562 return fullAngle;
563 }
564
565 void SetStart(const ScenePoint2D& p)
566 {
567 SetModified(true);
568 start_ = p;
569 }
570
571 void SetMiddle(const ScenePoint2D& p)
572 {
573 SetModified(true);
574 middle_ = p;
575 }
576
577 void SetEnd(const ScenePoint2D& p)
578 {
579 SetModified(true);
580 end_ = p;
581 }
582
583 virtual bool IsHit(const ScenePoint2D& p,
584 const Scene2D& scene) const
585 {
586 return false;
587 }
588
589 virtual void RenderPolylineLayer(PolylineSceneLayer& polyline,
590 const Scene2D& scene) ORTHANC_OVERRIDE
591 {
592 static unsigned int NUM_SEGMENTS = 64;
593
594 const double radius = radius_ / scene.GetSceneToCanvasTransform().ComputeZoom();
595
596 double fullAngle, startAngle, endAngle;
597 ComputeAngles(fullAngle, startAngle, endAngle);
598
599 double increment = fullAngle / static_cast<double>(NUM_SEGMENTS - 1);
600
601 PolylineSceneLayer::Chain chain;
602 chain.reserve(NUM_SEGMENTS);
603
604 double theta = startAngle;
605 for (unsigned int i = 0; i < NUM_SEGMENTS; i++)
606 {
607 chain.push_back(ScenePoint2D(middle_.GetX() + radius * cos(theta),
608 middle_.GetY() + radius * sin(theta)));
609 theta += increment;
610 }
611
612 if (IsHover())
613 {
614 polyline.AddChain(chain, false /* closed */, GetHoverColor());
615 }
616 else
617 {
618 polyline.AddChain(chain, false /* closed */, GetColor());
619 }
620 }
621
622 virtual void RenderOtherLayers(MacroSceneLayer& macro,
623 const Scene2D& scene) ORTHANC_OVERRIDE
624 {
625 }
626
627 virtual void MovePreview(const ScenePoint2D& delta) ORTHANC_OVERRIDE
628 {
629 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // No hit is possible
630 }
631
632 virtual void MoveDone(const ScenePoint2D& delta) ORTHANC_OVERRIDE
633 {
634 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // No hit is possible
635 }
636 };
637
638
639 class Text : public GeometricPrimitive
640 {
641 private:
642 AnnotationsLayer& that_;
643 bool first_;
644 size_t subLayer_;
645 std::unique_ptr<TextSceneLayer> content_;
646
647 public:
648 Text(AnnotationsLayer& that,
649 Annotation& parentAnnotation) :
650 GeometricPrimitive(parentAnnotation, 2),
651 that_(that),
652 first_(true)
653 {
654 }
655
656 virtual ~Text()
657 {
658 if (!first_)
659 {
660 that_.TagSubLayerToRemove(subLayer_);
661 }
662 }
663
664 void SetContent(const TextSceneLayer& content)
665 {
666 SetModified(true);
667 content_.reset(dynamic_cast<TextSceneLayer*>(content.Clone()));
668 }
669
670 virtual bool IsHit(const ScenePoint2D& p,
671 const Scene2D& scene) const
672 {
673 return false;
674 }
675
676 virtual void RenderPolylineLayer(PolylineSceneLayer& polyline,
677 const Scene2D& scene) ORTHANC_OVERRIDE
678 {
679 }
680
681 virtual void RenderOtherLayers(MacroSceneLayer& macro,
682 const Scene2D& scene) ORTHANC_OVERRIDE
683 {
684 if (content_.get() != NULL)
685 {
686 std::unique_ptr<TextSceneLayer> layer(reinterpret_cast<TextSceneLayer*>(content_->Clone()));
687
688 layer->SetColor(IsHover() ? GetHoverColor() : GetColor());
689
690 if (first_)
691 {
692 subLayer_ = macro.AddLayer(layer.release());
693 first_ = false;
694 }
695 else
696 {
697 macro.UpdateLayer(subLayer_, layer.release());
698 }
699 }
700 }
701
702 virtual void MovePreview(const ScenePoint2D& delta) ORTHANC_OVERRIDE
703 {
704 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // No hit is possible
705 }
706
707 virtual void MoveDone(const ScenePoint2D& delta) ORTHANC_OVERRIDE
708 {
709 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // No hit is possible
710 }
711 };
712
713
714 class EditPrimitiveTracker : public IFlexiblePointerTracker
715 {
716 private:
717 GeometricPrimitive& primitive_;
718 ScenePoint2D sceneClick_;
719 AffineTransform2D canvasToScene_;
720 bool alive_;
721
722 public:
723 EditPrimitiveTracker(GeometricPrimitive& primitive,
724 const ScenePoint2D& sceneClick,
725 const AffineTransform2D& canvasToScene) :
726 primitive_(primitive),
727 sceneClick_(sceneClick),
728 canvasToScene_(canvasToScene),
729 alive_(true)
730 {
731 }
732
733 virtual void PointerMove(const PointerEvent& event) ORTHANC_OVERRIDE
734 {
735 primitive_.MovePreview(event.GetMainPosition().Apply(canvasToScene_) - sceneClick_);
736 }
737
738 virtual void PointerUp(const PointerEvent& event) ORTHANC_OVERRIDE
739 {
740 primitive_.MoveDone(event.GetMainPosition().Apply(canvasToScene_) - sceneClick_);
741 alive_ = false;
742 }
743
744 virtual void PointerDown(const PointerEvent& event) ORTHANC_OVERRIDE
745 {
746 }
747
748 virtual bool IsAlive() const ORTHANC_OVERRIDE
749 {
750 return alive_;
751 }
752
753 virtual void Cancel() ORTHANC_OVERRIDE
754 {
755 primitive_.MoveDone(ScenePoint2D(0, 0));
756 }
757 };
758
759
760 class SegmentAnnotation : public Annotation
761 {
762 private:
763 bool showLabel_;
764 Handle& handle1_;
765 Handle& handle2_;
766 Segment& segment_;
767 Text& label_;
768
769 void UpdateLabel()
770 {
771 if (showLabel_)
772 {
773 TextSceneLayer content;
774
775 double x1 = handle1_.GetCenter().GetX();
776 double y1 = handle1_.GetCenter().GetY();
777 double x2 = handle2_.GetCenter().GetX();
778 double y2 = handle2_.GetCenter().GetY();
779
780 // Put the label to the right of the right-most handle
781 if (x1 < x2)
782 {
783 content.SetPosition(x2, y2);
784 }
785 else
786 {
787 content.SetPosition(x1, y1);
788 }
789
790 content.SetAnchor(BitmapAnchor_CenterLeft);
791 content.SetBorder(10);
792
793 double dx = x1 - x2;
794 double dy = y1 - y2;
795 char buf[32];
796 sprintf(buf, "%0.2f cm", sqrt(dx * dx + dy * dy) / 10.0);
797 content.SetText(buf);
798
799 label_.SetContent(content);
800 }
801 }
802
803 public:
804 SegmentAnnotation(AnnotationsLayer& that,
805 bool showLabel,
806 const ScenePoint2D& p1,
807 const ScenePoint2D& p2) :
808 Annotation(that),
809 showLabel_(showLabel),
810 handle1_(AddTypedPrimitive<Handle>(new Handle(*this, p1))),
811 handle2_(AddTypedPrimitive<Handle>(new Handle(*this, p2))),
812 segment_(AddTypedPrimitive<Segment>(new Segment(*this, p1, p2))),
813 label_(AddTypedPrimitive<Text>(new Text(that, *this)))
814 {
815 label_.SetColor(Color(255, 0, 0));
816 UpdateLabel();
817 }
818
819 Handle& GetHandle1() const
820 {
821 return handle1_;
822 }
823
824 Handle& GetHandle2() const
825 {
826 return handle2_;
827 }
828
829 virtual void SignalMove(GeometricPrimitive& primitive) ORTHANC_OVERRIDE
830 {
831 if (&primitive == &handle1_ ||
832 &primitive == &handle2_)
833 {
834 segment_.SetPosition(handle1_.GetCenter(), handle2_.GetCenter());
835 }
836 else if (&primitive == &segment_)
837 {
838 handle1_.SetCenter(segment_.GetPosition1());
839 handle2_.SetCenter(segment_.GetPosition2());
840 }
841
842 UpdateLabel();
843 }
844
845 virtual void Serialize(Json::Value& target) ORTHANC_OVERRIDE
846 {
847 target = Json::objectValue;
848 target[KEY_TYPE] = VALUE_SEGMENT;
849 target[KEY_X1] = handle1_.GetCenter().GetX();
850 target[KEY_Y1] = handle1_.GetCenter().GetY();
851 target[KEY_X2] = handle2_.GetCenter().GetX();
852 target[KEY_Y2] = handle2_.GetCenter().GetY();
853 }
854
855 static void Unserialize(AnnotationsLayer& target,
856 const Json::Value& source)
857 {
858 if (source.isMember(KEY_X1) &&
859 source.isMember(KEY_Y1) &&
860 source.isMember(KEY_X2) &&
861 source.isMember(KEY_Y2) &&
862 source[KEY_X1].isNumeric() &&
863 source[KEY_Y1].isNumeric() &&
864 source[KEY_X2].isNumeric() &&
865 source[KEY_Y2].isNumeric())
866 {
867 new SegmentAnnotation(target, true,
868 ScenePoint2D(source[KEY_X1].asDouble(), source[KEY_Y1].asDouble()),
869 ScenePoint2D(source[KEY_X2].asDouble(), source[KEY_Y2].asDouble()));
870 }
871 else
872 {
873 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot unserialize an segment annotation");
874 }
875 }
876 };
877
878
879 class AngleAnnotation : public Annotation
880 {
881 private:
882 Handle& startHandle_;
883 Handle& middleHandle_;
884 Handle& endHandle_;
885 Segment& segment1_;
886 Segment& segment2_;
887 Arc& arc_;
888 Text& label_;
889
890 void UpdateLabel()
891 {
892 TextSceneLayer content;
893
894 const double x1 = startHandle_.GetCenter().GetX();
895 const double x2 = middleHandle_.GetCenter().GetX();
896 const double y2 = middleHandle_.GetCenter().GetY();
897 const double x3 = endHandle_.GetCenter().GetX();
898
899 if (x2 < x1 &&
900 x2 < x3)
901 {
902 content.SetAnchor(BitmapAnchor_CenterRight);
903 }
904 else
905 {
906 content.SetAnchor(BitmapAnchor_CenterLeft);
907 }
908
909 content.SetPosition(x2, y2);
910 content.SetBorder(10);
911
912 char buf[32];
913 sprintf(buf, "%.01f%c%c", std::abs(arc_.GetAngle()) / PI * 180.0,
914 0xc2, 0xb0 /* two bytes corresponding to degree symbol in UTF-8 */);
915 content.SetText(buf);
916
917 label_.SetContent(content);
918 }
919
920 public:
921 AngleAnnotation(AnnotationsLayer& that,
922 const ScenePoint2D& start,
923 const ScenePoint2D& middle,
924 const ScenePoint2D& end) :
925 Annotation(that),
926 startHandle_(AddTypedPrimitive<Handle>(new Handle(*this, start))),
927 middleHandle_(AddTypedPrimitive<Handle>(new Handle(*this, middle))),
928 endHandle_(AddTypedPrimitive<Handle>(new Handle(*this, end))),
929 segment1_(AddTypedPrimitive<Segment>(new Segment(*this, start, middle))),
930 segment2_(AddTypedPrimitive<Segment>(new Segment(*this, middle, end))),
931 arc_(AddTypedPrimitive<Arc>(new Arc(*this, start, middle, end))),
932 label_(AddTypedPrimitive<Text>(new Text(that, *this)))
933 {
934 label_.SetColor(Color(255, 0, 0));
935 UpdateLabel();
936 }
937
938 Handle& GetEndHandle() const
939 {
940 return endHandle_;
941 }
942
943 virtual void SignalMove(GeometricPrimitive& primitive) ORTHANC_OVERRIDE
944 {
945 if (&primitive == &startHandle_)
946 {
947 segment1_.SetPosition(startHandle_.GetCenter(), middleHandle_.GetCenter());
948 arc_.SetStart(startHandle_.GetCenter());
949 }
950 else if (&primitive == &middleHandle_)
951 {
952 segment1_.SetPosition(startHandle_.GetCenter(), middleHandle_.GetCenter());
953 segment2_.SetPosition(middleHandle_.GetCenter(), endHandle_.GetCenter());
954 arc_.SetMiddle(middleHandle_.GetCenter());
955 }
956 else if (&primitive == &endHandle_)
957 {
958 segment2_.SetPosition(middleHandle_.GetCenter(), endHandle_.GetCenter());
959 arc_.SetEnd(endHandle_.GetCenter());
960 }
961 else if (&primitive == &segment1_)
962 {
963 startHandle_.SetCenter(segment1_.GetPosition1());
964 middleHandle_.SetCenter(segment1_.GetPosition2());
965 segment2_.SetPosition(segment1_.GetPosition2(), segment2_.GetPosition2());
966 arc_.SetStart(segment1_.GetPosition1());
967 arc_.SetMiddle(segment1_.GetPosition2());
968 }
969 else if (&primitive == &segment2_)
970 {
971 middleHandle_.SetCenter(segment2_.GetPosition1());
972 endHandle_.SetCenter(segment2_.GetPosition2());
973 segment1_.SetPosition(segment1_.GetPosition1(), segment2_.GetPosition1());
974 arc_.SetMiddle(segment2_.GetPosition1());
975 arc_.SetEnd(segment2_.GetPosition2());
976 }
977
978 UpdateLabel();
979 }
980
981 virtual void Serialize(Json::Value& target) ORTHANC_OVERRIDE
982 {
983 target = Json::objectValue;
984 target[KEY_TYPE] = VALUE_ANGLE;
985 target[KEY_X1] = startHandle_.GetCenter().GetX();
986 target[KEY_Y1] = startHandle_.GetCenter().GetY();
987 target[KEY_X2] = middleHandle_.GetCenter().GetX();
988 target[KEY_Y2] = middleHandle_.GetCenter().GetY();
989 target[KEY_X3] = endHandle_.GetCenter().GetX();
990 target[KEY_Y3] = endHandle_.GetCenter().GetY();
991 }
992
993 static void Unserialize(AnnotationsLayer& target,
994 const Json::Value& source)
995 {
996 if (source.isMember(KEY_X1) &&
997 source.isMember(KEY_Y1) &&
998 source.isMember(KEY_X2) &&
999 source.isMember(KEY_Y2) &&
1000 source.isMember(KEY_X3) &&
1001 source.isMember(KEY_Y3) &&
1002 source[KEY_X1].isNumeric() &&
1003 source[KEY_Y1].isNumeric() &&
1004 source[KEY_X2].isNumeric() &&
1005 source[KEY_Y2].isNumeric() &&
1006 source[KEY_X3].isNumeric() &&
1007 source[KEY_Y3].isNumeric())
1008 {
1009 new AngleAnnotation(target,
1010 ScenePoint2D(source[KEY_X1].asDouble(), source[KEY_Y1].asDouble()),
1011 ScenePoint2D(source[KEY_X2].asDouble(), source[KEY_Y2].asDouble()),
1012 ScenePoint2D(source[KEY_X3].asDouble(), source[KEY_Y3].asDouble()));
1013 }
1014 else
1015 {
1016 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot unserialize an angle annotation");
1017 }
1018 }
1019 };
1020
1021
1022 class CircleAnnotation : public Annotation
1023 {
1024 private:
1025 Handle& handle1_;
1026 Handle& handle2_;
1027 Segment& segment_;
1028 Circle& circle_;
1029 Text& label_;
1030
1031 void UpdateLabel()
1032 {
1033 TextSceneLayer content;
1034
1035 double x1 = handle1_.GetCenter().GetX();
1036 double y1 = handle1_.GetCenter().GetY();
1037 double x2 = handle2_.GetCenter().GetX();
1038 double y2 = handle2_.GetCenter().GetY();
1039
1040 // Put the label to the right of the right-most handle
1041 if (x1 < x2)
1042 {
1043 content.SetPosition(x2, y2);
1044 }
1045 else
1046 {
1047 content.SetPosition(x1, y1);
1048 }
1049
1050 content.SetAnchor(BitmapAnchor_CenterLeft);
1051 content.SetBorder(10);
1052
1053 double dx = x1 - x2;
1054 double dy = y1 - y2;
1055 double diameter = sqrt(dx * dx + dy * dy); // in millimeters
1056
1057 double area = PI * diameter * diameter / 4.0;
1058
1059 char buf[32];
1060 sprintf(buf, "%0.2f cm\n%0.2f cm%c%c",
1061 diameter / 10.0,
1062 area / 100.0,
1063 0xc2, 0xb2 /* two bytes corresponding to two power in UTF-8 */);
1064 content.SetText(buf);
1065
1066 label_.SetContent(content);
1067 }
1068
1069 public:
1070 CircleAnnotation(AnnotationsLayer& that,
1071 const ScenePoint2D& p1,
1072 const ScenePoint2D& p2) :
1073 Annotation(that),
1074 handle1_(AddTypedPrimitive<Handle>(new Handle(*this, p1))),
1075 handle2_(AddTypedPrimitive<Handle>(new Handle(*this, p2))),
1076 segment_(AddTypedPrimitive<Segment>(new Segment(*this, p1, p2))),
1077 circle_(AddTypedPrimitive<Circle>(new Circle(*this, p1, p2))),
1078 label_(AddTypedPrimitive<Text>(new Text(that, *this)))
1079 {
1080 label_.SetColor(Color(255, 0, 0));
1081 UpdateLabel();
1082 }
1083
1084 Handle& GetHandle2() const
1085 {
1086 return handle2_;
1087 }
1088
1089 virtual void SignalMove(GeometricPrimitive& primitive) ORTHANC_OVERRIDE
1090 {
1091 if (&primitive == &handle1_ ||
1092 &primitive == &handle2_)
1093 {
1094 segment_.SetPosition(handle1_.GetCenter(), handle2_.GetCenter());
1095 circle_.SetPosition(handle1_.GetCenter(), handle2_.GetCenter());
1096 }
1097 else if (&primitive == &segment_)
1098 {
1099 handle1_.SetCenter(segment_.GetPosition1());
1100 handle2_.SetCenter(segment_.GetPosition2());
1101 circle_.SetPosition(segment_.GetPosition1(), segment_.GetPosition2());
1102 }
1103
1104 UpdateLabel();
1105 }
1106
1107 virtual void Serialize(Json::Value& target) ORTHANC_OVERRIDE
1108 {
1109 target = Json::objectValue;
1110 target[KEY_TYPE] = VALUE_CIRCLE;
1111 target[KEY_X1] = handle1_.GetCenter().GetX();
1112 target[KEY_Y1] = handle1_.GetCenter().GetY();
1113 target[KEY_X2] = handle2_.GetCenter().GetX();
1114 target[KEY_Y2] = handle2_.GetCenter().GetY();
1115 }
1116
1117 static void Unserialize(AnnotationsLayer& target,
1118 const Json::Value& source)
1119 {
1120 if (source.isMember(KEY_X1) &&
1121 source.isMember(KEY_Y1) &&
1122 source.isMember(KEY_X2) &&
1123 source.isMember(KEY_Y2) &&
1124 source[KEY_X1].isNumeric() &&
1125 source[KEY_Y1].isNumeric() &&
1126 source[KEY_X2].isNumeric() &&
1127 source[KEY_Y2].isNumeric())
1128 {
1129 new CircleAnnotation(target,
1130 ScenePoint2D(source[KEY_X1].asDouble(), source[KEY_Y1].asDouble()),
1131 ScenePoint2D(source[KEY_X2].asDouble(), source[KEY_Y2].asDouble()));
1132 }
1133 else
1134 {
1135 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot unserialize an circle annotation");
1136 }
1137 }
1138 };
1139
1140
1141 class CreateSegmentOrCircleTracker : public IFlexiblePointerTracker
1142 {
1143 private:
1144 AnnotationsLayer& that_;
1145 Annotation* annotation_;
1146 AffineTransform2D canvasToScene_;
1147 Handle* handle2_;
1148
1149 public:
1150 CreateSegmentOrCircleTracker(AnnotationsLayer& that,
1151 bool isCircle,
1152 const ScenePoint2D& sceneClick,
1153 const AffineTransform2D& canvasToScene) :
1154 that_(that),
1155 annotation_(NULL),
1156 canvasToScene_(canvasToScene),
1157 handle2_(NULL)
1158 {
1159 if (isCircle)
1160 {
1161 annotation_ = new CircleAnnotation(that, sceneClick, sceneClick);
1162 handle2_ = &dynamic_cast<CircleAnnotation*>(annotation_)->GetHandle2();
1163 }
1164 else
1165 {
1166 annotation_ = new SegmentAnnotation(that, true /* show label */, sceneClick, sceneClick);
1167 handle2_ = &dynamic_cast<SegmentAnnotation*>(annotation_)->GetHandle2();
1168 }
1169
1170 assert(annotation_ != NULL &&
1171 handle2_ != NULL);
1172 }
1173
1174 virtual void PointerMove(const PointerEvent& event) ORTHANC_OVERRIDE
1175 {
1176 if (annotation_ != NULL)
1177 {
1178 assert(handle2_ != NULL);
1179 handle2_->SetCenter(event.GetMainPosition().Apply(canvasToScene_));
1180 annotation_->SignalMove(*handle2_);
1181 }
1182 }
1183
1184 virtual void PointerUp(const PointerEvent& event) ORTHANC_OVERRIDE
1185 {
1186 annotation_ = NULL; // IsAlive() becomes false
1187
1188 that_.BroadcastMessage(AnnotationAddedMessage(that_));
1189 }
1190
1191 virtual void PointerDown(const PointerEvent& event) ORTHANC_OVERRIDE
1192 {
1193 }
1194
1195 virtual bool IsAlive() const ORTHANC_OVERRIDE
1196 {
1197 return (annotation_ != NULL);
1198 }
1199
1200 virtual void Cancel() ORTHANC_OVERRIDE
1201 {
1202 if (annotation_ != NULL)
1203 {
1204 that_.DeleteAnnotation(annotation_);
1205 annotation_ = NULL;
1206 }
1207 }
1208 };
1209
1210
1211 class CreateAngleTracker : public IFlexiblePointerTracker
1212 {
1213 private:
1214 AnnotationsLayer& that_;
1215 SegmentAnnotation* segment_;
1216 AngleAnnotation* angle_;
1217 AffineTransform2D canvasToScene_;
1218
1219 public:
1220 CreateAngleTracker(AnnotationsLayer& that,
1221 const ScenePoint2D& sceneClick,
1222 const AffineTransform2D& canvasToScene) :
1223 that_(that),
1224 segment_(NULL),
1225 angle_(NULL),
1226 canvasToScene_(canvasToScene)
1227 {
1228 segment_ = new SegmentAnnotation(that, false /* no length label */, sceneClick, sceneClick);
1229 }
1230
1231 virtual void PointerMove(const PointerEvent& event) ORTHANC_OVERRIDE
1232 {
1233 if (segment_ != NULL)
1234 {
1235 segment_->GetHandle2().SetCenter(event.GetMainPosition().Apply(canvasToScene_));
1236 segment_->SignalMove(segment_->GetHandle2());
1237 }
1238
1239 if (angle_ != NULL)
1240 {
1241 angle_->GetEndHandle().SetCenter(event.GetMainPosition().Apply(canvasToScene_));
1242 angle_->SignalMove(angle_->GetEndHandle());
1243 }
1244 }
1245
1246 virtual void PointerUp(const PointerEvent& event) ORTHANC_OVERRIDE
1247 {
1248 if (segment_ != NULL)
1249 {
1250 // End of first step: The first segment is available, now create the angle
1251
1252 angle_ = new AngleAnnotation(that_, segment_->GetHandle1().GetCenter(),
1253 segment_->GetHandle2().GetCenter(),
1254 segment_->GetHandle2().GetCenter());
1255
1256 that_.DeleteAnnotation(segment_);
1257 segment_ = NULL;
1258 }
1259 else
1260 {
1261 angle_ = NULL; // IsAlive() becomes false
1262
1263 that_.BroadcastMessage(AnnotationAddedMessage(that_));
1264 }
1265 }
1266
1267 virtual void PointerDown(const PointerEvent& event) ORTHANC_OVERRIDE
1268 {
1269 }
1270
1271 virtual bool IsAlive() const ORTHANC_OVERRIDE
1272 {
1273 return (segment_ != NULL ||
1274 angle_ != NULL);
1275 }
1276
1277 virtual void Cancel() ORTHANC_OVERRIDE
1278 {
1279 if (segment_ != NULL)
1280 {
1281 that_.DeleteAnnotation(segment_);
1282 segment_ = NULL;
1283 }
1284
1285 if (angle_ != NULL)
1286 {
1287 that_.DeleteAnnotation(angle_);
1288 angle_ = NULL;
1289 }
1290 }
1291 };
1292
1293
1294 // Dummy tracker that is only used for deletion, in order to warn
1295 // the caller that the mouse action was taken into consideration
1296 class EraseTracker : public IFlexiblePointerTracker
1297 {
1298 public:
1299 EraseTracker()
1300 {
1301 }
1302
1303 virtual void PointerMove(const PointerEvent& event) ORTHANC_OVERRIDE
1304 {
1305 }
1306
1307 virtual void PointerUp(const PointerEvent& event) ORTHANC_OVERRIDE
1308 {
1309 }
1310
1311 virtual void PointerDown(const PointerEvent& event) ORTHANC_OVERRIDE
1312 {
1313 }
1314
1315 virtual bool IsAlive() const ORTHANC_OVERRIDE
1316 {
1317 return false;
1318 }
1319
1320 virtual void Cancel() ORTHANC_OVERRIDE
1321 {
1322 }
1323 };
1324
1325
1326 typedef std::set<GeometricPrimitive*> GeometricPrimitives;
1327 typedef std::set<Annotation*> Annotations;
1328 typedef std::set<size_t> SubLayers;
1329
1330 Tool activeTool_;
1331 size_t macroLayerIndex_;
1332 size_t polylineSubLayer_;
1333 GeometricPrimitives primitives_;
1334 Annotations annotations_;
1335 SubLayers subLayersToRemove_;
1336
1337 void AddAnnotation(Annotation* annotation)
1338 {
1339 assert(annotation != NULL);
1340 assert(annotations_.find(annotation) == annotations_.end());
1341 annotations_.insert(annotation);
1342 }
1343
1344 void DeleteAnnotation(Annotation* annotation)
1345 {
1346 if (annotation != NULL)
1347 {
1348 assert(annotations_.find(annotation) != annotations_.end());
1349 annotations_.erase(annotation);
1350 delete annotation;
1351 }
1352 }
1353
1354 void DeletePrimitive(GeometricPrimitive* primitive)
1355 {
1356 if (primitive != NULL)
1357 {
1358 assert(primitives_.find(primitive) != primitives_.end());
1359 primitives_.erase(primitive);
1360 delete primitive;
1361 }
1362 }
1363
1364 void TagSubLayerToRemove(size_t subLayerIndex)
1365 {
1366 assert(subLayersToRemove_.find(subLayerIndex) == subLayersToRemove_.end());
1367 subLayersToRemove_.insert(subLayerIndex);
1368 }
1369
1370 public:
1371 AnnotationsLayer(size_t macroLayerIndex) :
1372 activeTool_(Tool_Edit),
1373 macroLayerIndex_(macroLayerIndex),
1374 polylineSubLayer_(0) // dummy initialization
1375 {
1376 annotations_.insert(new SegmentAnnotation(*this, true /* show label */, ScenePoint2D(0, 0), ScenePoint2D(100, 100)));
1377 annotations_.insert(new AngleAnnotation(*this, ScenePoint2D(100, 50), ScenePoint2D(150, 40), ScenePoint2D(200, 50)));
1378 annotations_.insert(new CircleAnnotation(*this, ScenePoint2D(50, 200), ScenePoint2D(100, 250)));
1379 }
1380
1381 ~AnnotationsLayer()
1382 {
1383 Clear();
1384 }
1385
1386 void Clear()
1387 {
1388 for (Annotations::iterator it = annotations_.begin(); it != annotations_.end(); ++it)
1389 {
1390 assert(*it != NULL);
1391 delete *it;
1392 }
1393
1394 annotations_.clear();
1395 }
1396
1397 void SetActiveTool(Tool tool)
1398 {
1399 activeTool_ = tool;
1400 }
1401
1402 Tool GetActiveTool() const
1403 {
1404 return activeTool_;
1405 }
1406
1407 void Render(Scene2D& scene)
1408 {
1409 MacroSceneLayer* macro = NULL;
1410
1411 if (scene.HasLayer(macroLayerIndex_))
1412 {
1413 macro = &dynamic_cast<MacroSceneLayer&>(scene.GetLayer(macroLayerIndex_));
1414 }
1415 else
1416 {
1417 macro = &dynamic_cast<MacroSceneLayer&>(scene.SetLayer(macroLayerIndex_, new MacroSceneLayer));
1418 polylineSubLayer_ = macro->AddLayer(new PolylineSceneLayer);
1419 }
1420
1421 for (SubLayers::const_iterator it = subLayersToRemove_.begin(); it != subLayersToRemove_.end(); ++it)
1422 {
1423 assert(macro->HasLayer(*it));
1424 macro->DeleteLayer(*it);
1425 }
1426
1427 subLayersToRemove_.clear();
1428
1429 std::unique_ptr<PolylineSceneLayer> polyline(new PolylineSceneLayer);
1430
1431 for (GeometricPrimitives::iterator it = primitives_.begin(); it != primitives_.end(); ++it)
1432 {
1433 assert(*it != NULL);
1434 GeometricPrimitive& primitive = **it;
1435
1436 primitive.RenderPolylineLayer(*polyline, scene);
1437
1438 if (primitive.IsModified())
1439 {
1440 primitive.RenderOtherLayers(*macro, scene);
1441 primitive.SetModified(false);
1442 }
1443 }
1444
1445 macro->UpdateLayer(polylineSubLayer_, polyline.release());
1446 }
1447
1448 bool ClearHover()
1449 {
1450 bool needsRefresh = false;
1451
1452 for (GeometricPrimitives::iterator it = primitives_.begin(); it != primitives_.end(); ++it)
1453 {
1454 assert(*it != NULL);
1455 if ((*it)->IsHover())
1456 {
1457 (*it)->SetHover(false);
1458 needsRefresh = true;
1459 }
1460 }
1461
1462 return needsRefresh;
1463 }
1464
1465 bool SetMouseHover(const ScenePoint2D& p /* expressed in canvas coordinates */,
1466 const Scene2D& scene)
1467 {
1468 if (activeTool_ == Tool_None)
1469 {
1470 return ClearHover();
1471 }
1472 else
1473 {
1474 bool needsRefresh = false;
1475
1476 const ScenePoint2D s = p.Apply(scene.GetCanvasToSceneTransform());
1477
1478 for (GeometricPrimitives::iterator it = primitives_.begin(); it != primitives_.end(); ++it)
1479 {
1480 assert(*it != NULL);
1481 bool hover = (*it)->IsHit(s, scene);
1482
1483 if ((*it)->IsHover() != hover)
1484 {
1485 needsRefresh = true;
1486 }
1487
1488 (*it)->SetHover(hover);
1489 }
1490
1491 return needsRefresh;
1492 }
1493 }
1494
1495
1496 IFlexiblePointerTracker* CreateTracker(const ScenePoint2D& p /* expressed in canvas coordinates */,
1497 const Scene2D& scene)
1498 {
1499 if (activeTool_ == Tool_None)
1500 {
1501 return NULL;
1502 }
1503 else
1504 {
1505 const ScenePoint2D s = p.Apply(scene.GetCanvasToSceneTransform());
1506
1507 GeometricPrimitive* bestHit = NULL;
1508
1509 for (GeometricPrimitives::iterator it = primitives_.begin(); it != primitives_.end(); ++it)
1510 {
1511 assert(*it != NULL);
1512 if ((*it)->IsHit(s, scene))
1513 {
1514 if (bestHit == NULL ||
1515 bestHit->GetDepth() > (*it)->GetDepth())
1516 {
1517 bestHit = *it;
1518 }
1519 }
1520 }
1521
1522 if (bestHit != NULL)
1523 {
1524 if (activeTool_ == Tool_Erase)
1525 {
1526 DeleteAnnotation(&bestHit->GetParentAnnotation());
1527 BroadcastMessage(AnnotationRemovedMessage(*this));
1528 return new EraseTracker;
1529 }
1530 else
1531 {
1532 return new EditPrimitiveTracker(*bestHit, s, scene.GetCanvasToSceneTransform());
1533 }
1534 }
1535 else
1536 {
1537 switch (activeTool_)
1538 {
1539 case Tool_Segment:
1540 return new CreateSegmentOrCircleTracker(*this, false /* segment */, s, scene.GetCanvasToSceneTransform());
1541
1542 case Tool_Circle:
1543 return new CreateSegmentOrCircleTracker(*this, true /* circle */, s, scene.GetCanvasToSceneTransform());
1544
1545 case Tool_Angle:
1546 return new CreateAngleTracker(*this, s, scene.GetCanvasToSceneTransform());
1547
1548 default:
1549 return NULL;
1550 }
1551 }
1552 }
1553 }
1554
1555
1556 void Serialize(Json::Value& target) const
1557 {
1558 Json::Value annotations = Json::arrayValue;
1559
1560 for (Annotations::const_iterator it = annotations_.begin(); it != annotations_.end(); ++it)
1561 {
1562 assert(*it != NULL);
1563
1564 Json::Value item;
1565 (*it)->Serialize(item);
1566 annotations.append(item);
1567 }
1568
1569 target = Json::objectValue;
1570 target[KEY_ANNOTATIONS] = annotations;
1571 }
1572
1573
1574 void Unserialize(const Json::Value& serialized)
1575 {
1576 Clear();
1577
1578 if (serialized.type() != Json::objectValue ||
1579 !serialized.isMember(KEY_ANNOTATIONS) ||
1580 serialized[KEY_ANNOTATIONS].type() != Json::arrayValue)
1581 {
1582 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot unserialize a set of annotations");
1583 }
1584
1585 const Json::Value& annotations = serialized[KEY_ANNOTATIONS];
1586
1587 for (Json::Value::ArrayIndex i = 0; i < annotations.size(); i++)
1588 {
1589 if (annotations[i].type() != Json::objectValue ||
1590 !annotations[i].isMember(KEY_TYPE) ||
1591 annotations[i][KEY_TYPE].type() != Json::stringValue)
1592 {
1593 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
1594 }
1595
1596 const std::string& type = annotations[i][KEY_TYPE].asString();
1597
1598 if (type == VALUE_ANGLE)
1599 {
1600 AngleAnnotation::Unserialize(*this, annotations[i]);
1601 }
1602 else if (type == VALUE_CIRCLE)
1603 {
1604 CircleAnnotation::Unserialize(*this, annotations[i]);
1605 }
1606 else if (type == VALUE_SEGMENT)
1607 {
1608 SegmentAnnotation::Unserialize(*this, annotations[i]);
1609 }
1610 else
1611 {
1612 LOG(ERROR) << "Cannot unserialize unknown type of annotation: " << type;
1613 }
1614 }
1615 }
1616 };
1617 }
1618 #endif
1619
1620
1621
1622 std::string orthancUrl;
1623 std::string instanceId;
1624 int frameIndex = 0;
1625 49
1626 50
1627 static void ProcessOptions(int argc, char* argv[]) 51 static void ProcessOptions(int argc, char* argv[])
1628 { 52 {
1629 namespace po = boost::program_options; 53 namespace po = boost::program_options;
1719 //Orthanc::Logging::EnableInfoLevel(true); 143 //Orthanc::Logging::EnableInfoLevel(true);
1720 //Orthanc::Logging::EnableTraceLevel(true); 144 //Orthanc::Logging::EnableTraceLevel(true);
1721 145
1722 { 146 {
1723 147
1724 #if 1 148 #if 0
1725 boost::shared_ptr<OrthancStone::SdlViewport> viewport = 149 boost::shared_ptr<OrthancStone::SdlViewport> viewport =
1726 OrthancStone::SdlOpenGLViewport::Create("Stone of Orthanc", 800, 600); 150 OrthancStone::SdlOpenGLViewport::Create("Stone of Orthanc", 800, 600);
1727 #else 151 #else
1728 boost::shared_ptr<OrthancStone::SdlViewport> viewport = 152 boost::shared_ptr<OrthancStone::SdlViewport> viewport =
1729 OrthancStone::SdlCairoViewport::Create("Stone of Orthanc", 800, 600); 153 OrthancStone::SdlCairoViewport::Create("Stone of Orthanc", 800, 600);
1757 181
1758 boost::shared_ptr<OrthancStone::AngleMeasureTool> angleMeasureTool(OrthancStone::AngleMeasureTool::Create(viewport)); 182 boost::shared_ptr<OrthancStone::AngleMeasureTool> angleMeasureTool(OrthancStone::AngleMeasureTool::Create(viewport));
1759 bool angleMeasureFirst = true; 183 bool angleMeasureFirst = true;
1760 angleMeasureTool->Disable(); 184 angleMeasureTool->Disable();
1761 185
1762 OrthancStone::AnnotationsLayer annotations(10); 186 OrthancStone::AnnotationsSceneLayer annotations(10);
1763 annotations.SetActiveTool(OrthancStone::AnnotationsLayer::Tool_Angle); 187 annotations.SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Angle);
1764 188
1765 { 189 {
1766 Json::Value v; 190 Json::Value v;
1767 annotations.Serialize(v); 191 annotations.Serialize(v);
1768 std::cout << v.toStyledString() << std::endl; 192 std::cout << v.toStyledString() << std::endl;