Mercurial > hg > orthanc-stone
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; |