comparison OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp @ 1804:5a872e69c74f

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