comparison OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp @ 1998:1fa3f484008e

added arrows to AnnotationsSceneLayer::Segment
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 02 Nov 2022 13:08:50 +0100
parents f03a827f8b47
children 709b90ae0f89
comparison
equal deleted inserted replaced
1997:c622219a3388 1998:1fa3f484008e
32 #include <OrthancException.h> 32 #include <OrthancException.h>
33 33
34 #include <boost/math/constants/constants.hpp> 34 #include <boost/math/constants/constants.hpp>
35 #include <list> 35 #include <list>
36 36
37 static const double PI = boost::math::constants::pi<double>();
38
37 static const double HANDLE_SIZE = 10.0; 39 static const double HANDLE_SIZE = 10.0;
38 static const double PI = boost::math::constants::pi<double>(); 40 static const double ARROW_LENGTH = 1.5 * HANDLE_SIZE;
41 static const double ARROW_ANGLE = 20.0 * PI / 180.0;
39 42
40 static const char* const KEY_ANNOTATIONS = "annotations"; 43 static const char* const KEY_ANNOTATIONS = "annotations";
41 static const char* const KEY_TYPE = "type"; 44 static const char* const KEY_TYPE = "type";
45 static const char* const KEY_TEXT = "text";
42 static const char* const KEY_X = "x"; 46 static const char* const KEY_X = "x";
43 static const char* const KEY_Y = "y"; 47 static const char* const KEY_Y = "y";
44 static const char* const KEY_X1 = "x1"; 48 static const char* const KEY_X1 = "x1";
45 static const char* const KEY_Y1 = "y1"; 49 static const char* const KEY_Y1 = "y1";
46 static const char* const KEY_X2 = "x2"; 50 static const char* const KEY_X2 = "x2";
49 static const char* const KEY_Y3 = "y3"; 53 static const char* const KEY_Y3 = "y3";
50 static const char* const KEY_UNITS = "units"; 54 static const char* const KEY_UNITS = "units";
51 55
52 static const char* const VALUE_ANGLE = "angle"; 56 static const char* const VALUE_ANGLE = "angle";
53 static const char* const VALUE_CIRCLE = "circle"; 57 static const char* const VALUE_CIRCLE = "circle";
54 static const char* const VALUE_SEGMENT = "segment"; 58 static const char* const VALUE_LENGTH = "length";
55 static const char* const VALUE_MILLIMETERS = "millimeters"; 59 static const char* const VALUE_MILLIMETERS = "millimeters";
56 static const char* const VALUE_PIXELS = "pixels"; 60 static const char* const VALUE_PIXELS = "pixels";
57 static const char* const VALUE_PIXEL_PROBE = "pixel-probe"; 61 static const char* const VALUE_PIXEL_PROBE = "pixel-probe";
58 static const char* const VALUE_RECTANGLE_PROBE = "rectangle-probe"; 62 static const char* const VALUE_RECTANGLE_PROBE = "rectangle-probe";
59 static const char* const VALUE_ELLIPSE_PROBE = "ellipse-probe"; 63 static const char* const VALUE_ELLIPSE_PROBE = "ellipse-probe";
64 static const char* const VALUE_TEXT_ANNOTATION = "text";
60 65
61 #if 0 66 #if 0
62 static OrthancStone::Color COLOR_PRIMITIVES(192, 192, 192); 67 static OrthancStone::Color COLOR_PRIMITIVES(192, 192, 192);
63 static OrthancStone::Color COLOR_HOVER(0, 255, 0); 68 static OrthancStone::Color COLOR_HOVER(0, 255, 0);
64 static OrthancStone::Color COLOR_TEXT(255, 0, 0); 69 static OrthancStone::Color COLOR_TEXT(255, 0, 0);
299 } 304 }
300 305
301 void SetCenter(double x, 306 void SetCenter(double x,
302 double y) 307 double y)
303 { 308 {
304 SetModified(true); 309 SetCenter(ScenePoint2D(x, y));
305 center_ = ScenePoint2D(x, y);
306 delta_ = ScenePoint2D(0, 0);
307 } 310 }
308 311
309 ScenePoint2D GetCenter() const 312 ScenePoint2D GetCenter() const
310 { 313 {
311 return center_ + delta_; 314 return center_ + delta_;
392 { 395 {
393 private: 396 private:
394 ScenePoint2D p1_; 397 ScenePoint2D p1_;
395 ScenePoint2D p2_; 398 ScenePoint2D p2_;
396 ScenePoint2D delta_; 399 ScenePoint2D delta_;
400 bool hasStartArrow_;
401 bool hasEndArrow_;
397 402
398 public: 403 public:
399 Segment(Annotation& parentAnnotation, 404 Segment(Annotation& parentAnnotation,
400 const ScenePoint2D& p1, 405 const ScenePoint2D& p1,
401 const ScenePoint2D& p2) : 406 const ScenePoint2D& p2) :
402 GeometricPrimitive(parentAnnotation, 1), // Can only be selected if no handle matches 407 GeometricPrimitive(parentAnnotation, 1), // Can only be selected if no handle matches
403 p1_(p1), 408 p1_(p1),
404 p2_(p2), 409 p2_(p2),
405 delta_(0, 0) 410 delta_(0, 0),
411 hasStartArrow_(false),
412 hasEndArrow_(false)
406 { 413 {
407 } 414 }
408 415
409 Segment(Annotation& parentAnnotation, 416 Segment(Annotation& parentAnnotation,
410 double x1, 417 double x1,
412 double x2, 419 double x2,
413 double y2) : 420 double y2) :
414 GeometricPrimitive(parentAnnotation, 1), // Can only be selected if no handle matches 421 GeometricPrimitive(parentAnnotation, 1), // Can only be selected if no handle matches
415 p1_(x1, y1), 422 p1_(x1, y1),
416 p2_(x2, y2), 423 p2_(x2, y2),
417 delta_(0, 0) 424 delta_(0, 0),
425 hasStartArrow_(false),
426 hasEndArrow_(false)
418 { 427 {
419 } 428 }
420 429
421 void SetPosition(const ScenePoint2D& p1, 430 void SetPosition(const ScenePoint2D& p1,
422 const ScenePoint2D& p2) 431 const ScenePoint2D& p2)
446 ScenePoint2D GetPosition2() const 455 ScenePoint2D GetPosition2() const
447 { 456 {
448 return p2_ + delta_; 457 return p2_ + delta_;
449 } 458 }
450 459
460 void SetStartArrow(bool enabled)
461 {
462 SetModified(true);
463 hasStartArrow_ = enabled;
464 }
465
466 bool HasStartArrow() const
467 {
468 return hasStartArrow_;
469 }
470
471 void SetEndArrow(bool enabled)
472 {
473 SetModified(true);
474 hasEndArrow_ = enabled;
475 }
476
477 bool HasEndArrow() const
478 {
479 return hasEndArrow_;
480 }
481
451 virtual bool IsHit(const ScenePoint2D& p, 482 virtual bool IsHit(const ScenePoint2D& p,
452 const Scene2D& scene) const ORTHANC_OVERRIDE 483 const Scene2D& scene) const ORTHANC_OVERRIDE
453 { 484 {
454 const double zoom = scene.GetSceneToCanvasTransform().ComputeZoom(); 485 const double zoom = scene.GetSceneToCanvasTransform().ComputeZoom();
455 return (ScenePoint2D::SquaredDistancePtSegment(p1_ + delta_, p2_ + delta_, p) * zoom * zoom <= 486 return (ScenePoint2D::SquaredDistancePtSegment(p1_ + delta_, p2_ + delta_, p) * zoom * zoom <=
457 } 488 }
458 489
459 virtual void RenderPolylineLayer(PolylineSceneLayer& polyline, 490 virtual void RenderPolylineLayer(PolylineSceneLayer& polyline,
460 const Scene2D& scene) ORTHANC_OVERRIDE 491 const Scene2D& scene) ORTHANC_OVERRIDE
461 { 492 {
462 polyline.AddSegment(p1_ + delta_, p2_ + delta_, GetActiveColor()); 493 const Color color = GetActiveColor();
494 const ScenePoint2D a(p1_ + delta_);
495 const ScenePoint2D b(p2_ + delta_);
496
497 polyline.AddSegment(a, b, color);
498
499 if (hasStartArrow_ ||
500 hasEndArrow_)
501 {
502 const double length = ARROW_LENGTH / scene.GetSceneToCanvasTransform().ComputeZoom();
503 const double angle = atan2(b.GetY() - a.GetY(), b.GetX() - a.GetX());
504
505 if (hasStartArrow_)
506 {
507 polyline.AddSegment(a, a + ScenePoint2D(
508 length * cos(angle + ARROW_ANGLE),
509 length * sin(angle + ARROW_ANGLE)), color);
510 polyline.AddSegment(a, a + ScenePoint2D(
511 length * cos(angle - ARROW_ANGLE),
512 length * sin(angle - ARROW_ANGLE)), color);
513 }
514
515 if (hasEndArrow_)
516 {
517 polyline.AddSegment(b, b + ScenePoint2D(
518 length * cos(angle + ARROW_ANGLE + PI),
519 length * sin(angle + ARROW_ANGLE + PI)), color);
520 polyline.AddSegment(b, b + ScenePoint2D(
521 length * cos(angle - ARROW_ANGLE + PI),
522 length * sin(angle - ARROW_ANGLE + PI)), color);
523 }
524 }
463 } 525 }
464 526
465 virtual void RenderOtherLayers(MacroSceneLayer& macro, 527 virtual void RenderOtherLayers(MacroSceneLayer& macro,
466 const Scene2D& scene) ORTHANC_OVERRIDE 528 const Scene2D& scene) ORTHANC_OVERRIDE
467 { 529 {
994 primitive_.MoveDone(sceneClick_, scene); // TODO Check this 1056 primitive_.MoveDone(sceneClick_, scene); // TODO Check this
995 } 1057 }
996 }; 1058 };
997 1059
998 1060
999 class AnnotationsSceneLayer::SegmentAnnotation : public Annotation 1061 class AnnotationsSceneLayer::LengthAnnotation : public Annotation
1000 { 1062 {
1001 private: 1063 private:
1002 bool showLabel_; 1064 bool showLabel_;
1003 Handle& handle1_; 1065 Handle& handle1_;
1004 Handle& handle2_; 1066 Handle& handle2_;
1052 label_.SetContent(content); 1114 label_.SetContent(content);
1053 } 1115 }
1054 } 1116 }
1055 1117
1056 public: 1118 public:
1057 SegmentAnnotation(AnnotationsSceneLayer& that, 1119 LengthAnnotation(AnnotationsSceneLayer& that,
1058 Units units, 1120 Units units,
1059 bool showLabel, 1121 bool showLabel,
1060 const ScenePoint2D& p1, 1122 const ScenePoint2D& p1,
1061 const ScenePoint2D& p2) : 1123 const ScenePoint2D& p2) :
1062 Annotation(that, units), 1124 Annotation(that, units),
1063 showLabel_(showLabel), 1125 showLabel_(showLabel),
1064 handle1_(AddTypedPrimitive<Handle>(new Handle(*this, Handle::Shape_Square, p1))), 1126 handle1_(AddTypedPrimitive<Handle>(new Handle(*this, Handle::Shape_Square, p1))),
1065 handle2_(AddTypedPrimitive<Handle>(new Handle(*this, Handle::Shape_Square, p2))), 1127 handle2_(AddTypedPrimitive<Handle>(new Handle(*this, Handle::Shape_Square, p2))),
1066 segment_(AddTypedPrimitive<Segment>(new Segment(*this, p1, p2))), 1128 segment_(AddTypedPrimitive<Segment>(new Segment(*this, p1, p2))),
1116 } 1178 }
1117 1179
1118 virtual void Serialize(Json::Value& target) ORTHANC_OVERRIDE 1180 virtual void Serialize(Json::Value& target) ORTHANC_OVERRIDE
1119 { 1181 {
1120 target = Json::objectValue; 1182 target = Json::objectValue;
1121 target[KEY_TYPE] = VALUE_SEGMENT; 1183 target[KEY_TYPE] = VALUE_LENGTH;
1122 target[KEY_X1] = handle1_.GetCenter().GetX(); 1184 target[KEY_X1] = handle1_.GetCenter().GetX();
1123 target[KEY_Y1] = handle1_.GetCenter().GetY(); 1185 target[KEY_Y1] = handle1_.GetCenter().GetY();
1124 target[KEY_X2] = handle2_.GetCenter().GetX(); 1186 target[KEY_X2] = handle2_.GetCenter().GetX();
1125 target[KEY_Y2] = handle2_.GetCenter().GetY(); 1187 target[KEY_Y2] = handle2_.GetCenter().GetY();
1126 } 1188 }
1136 source[KEY_X1].isNumeric() && 1198 source[KEY_X1].isNumeric() &&
1137 source[KEY_Y1].isNumeric() && 1199 source[KEY_Y1].isNumeric() &&
1138 source[KEY_X2].isNumeric() && 1200 source[KEY_X2].isNumeric() &&
1139 source[KEY_Y2].isNumeric()) 1201 source[KEY_Y2].isNumeric())
1140 { 1202 {
1141 new SegmentAnnotation(target, units, true, 1203 new LengthAnnotation(target, units, true,
1142 ScenePoint2D(source[KEY_X1].asDouble(), source[KEY_Y1].asDouble()), 1204 ScenePoint2D(source[KEY_X1].asDouble(), source[KEY_Y1].asDouble()),
1143 ScenePoint2D(source[KEY_X2].asDouble(), source[KEY_Y2].asDouble())); 1205 ScenePoint2D(source[KEY_X2].asDouble(), source[KEY_Y2].asDouble()));
1144 } 1206 }
1145 else 1207 else
1146 { 1208 {
1147 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot unserialize an segment annotation"); 1209 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot unserialize a length annotation");
1148 } 1210 }
1149 } 1211 }
1150 }; 1212 };
1151 1213
1152 1214
2177 2239
2178 class AnnotationsSceneLayer::CreateAngleTracker : public IFlexiblePointerTracker 2240 class AnnotationsSceneLayer::CreateAngleTracker : public IFlexiblePointerTracker
2179 { 2241 {
2180 private: 2242 private:
2181 AnnotationsSceneLayer& that_; 2243 AnnotationsSceneLayer& that_;
2182 SegmentAnnotation* segment_; 2244 LengthAnnotation* length_;
2183 AngleAnnotation* angle_; 2245 AngleAnnotation* angle_;
2184 AffineTransform2D canvasToScene_; 2246 AffineTransform2D canvasToScene_;
2185 2247
2186 public: 2248 public:
2187 CreateAngleTracker(AnnotationsSceneLayer& that, 2249 CreateAngleTracker(AnnotationsSceneLayer& that,
2188 Units units, 2250 Units units,
2189 const ScenePoint2D& sceneClick, 2251 const ScenePoint2D& sceneClick,
2190 const AffineTransform2D& canvasToScene) : 2252 const AffineTransform2D& canvasToScene) :
2191 that_(that), 2253 that_(that),
2192 segment_(NULL), 2254 length_(NULL),
2193 angle_(NULL), 2255 angle_(NULL),
2194 canvasToScene_(canvasToScene) 2256 canvasToScene_(canvasToScene)
2195 { 2257 {
2196 segment_ = new SegmentAnnotation(that, units, false /* no length label */, sceneClick, sceneClick); 2258 length_ = new LengthAnnotation(that, units, false /* no length label */, sceneClick, sceneClick);
2197 } 2259 }
2198 2260
2199 virtual void PointerMove(const PointerEvent& event, 2261 virtual void PointerMove(const PointerEvent& event,
2200 const Scene2D& scene) ORTHANC_OVERRIDE 2262 const Scene2D& scene) ORTHANC_OVERRIDE
2201 { 2263 {
2202 if (segment_ != NULL) 2264 if (length_ != NULL)
2203 { 2265 {
2204 segment_->GetHandle(1).SetCenter(event.GetMainPosition().Apply(canvasToScene_)); 2266 length_->GetHandle(1).SetCenter(event.GetMainPosition().Apply(canvasToScene_));
2205 segment_->SignalMove(segment_->GetHandle(1), scene); 2267 length_->SignalMove(length_->GetHandle(1), scene);
2206 that_.BroadcastMessage(AnnotationChangedMessage(that_)); 2268 that_.BroadcastMessage(AnnotationChangedMessage(that_));
2207 } 2269 }
2208 2270
2209 if (angle_ != NULL) 2271 if (angle_ != NULL)
2210 { 2272 {
2215 } 2277 }
2216 2278
2217 virtual void PointerUp(const PointerEvent& event, 2279 virtual void PointerUp(const PointerEvent& event,
2218 const Scene2D& scene) ORTHANC_OVERRIDE 2280 const Scene2D& scene) ORTHANC_OVERRIDE
2219 { 2281 {
2220 if (segment_ != NULL) 2282 if (length_ != NULL)
2221 { 2283 {
2222 // End of first step: The first segment is available, now create the angle 2284 // End of first step: The first segment is available, now create the angle
2223 2285
2224 angle_ = new AngleAnnotation(that_, segment_->GetUnits(), segment_->GetHandle(0).GetCenter(), 2286 angle_ = new AngleAnnotation(that_, length_->GetUnits(), length_->GetHandle(0).GetCenter(),
2225 segment_->GetHandle(1).GetCenter(), 2287 length_->GetHandle(1).GetCenter(),
2226 segment_->GetHandle(1).GetCenter()); 2288 length_->GetHandle(1).GetCenter());
2227 2289
2228 that_.DeleteAnnotation(segment_); 2290 that_.DeleteAnnotation(length_);
2229 segment_ = NULL; 2291 length_ = NULL;
2230 2292
2231 that_.BroadcastMessage(AnnotationChangedMessage(that_)); 2293 that_.BroadcastMessage(AnnotationChangedMessage(that_));
2232 } 2294 }
2233 else 2295 else
2234 { 2296 {
2243 { 2305 {
2244 } 2306 }
2245 2307
2246 virtual bool IsAlive() const ORTHANC_OVERRIDE 2308 virtual bool IsAlive() const ORTHANC_OVERRIDE
2247 { 2309 {
2248 return (segment_ != NULL || 2310 return (length_ != NULL ||
2249 angle_ != NULL); 2311 angle_ != NULL);
2250 } 2312 }
2251 2313
2252 virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE 2314 virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE
2253 { 2315 {
2254 if (segment_ != NULL) 2316 if (length_ != NULL)
2255 { 2317 {
2256 that_.DeleteAnnotation(segment_); 2318 that_.DeleteAnnotation(length_);
2257 segment_ = NULL; 2319 length_ = NULL;
2258 } 2320 }
2259 2321
2260 if (angle_ != NULL) 2322 if (angle_ != NULL)
2261 { 2323 {
2262 that_.DeleteAnnotation(angle_); 2324 that_.DeleteAnnotation(angle_);
2409 units_ = units; 2471 units_ = units;
2410 } 2472 }
2411 } 2473 }
2412 2474
2413 2475
2414 void AnnotationsSceneLayer::AddSegmentAnnotation(const ScenePoint2D& p1, 2476 void AnnotationsSceneLayer::AddLengthAnnotation(const ScenePoint2D& p1,
2415 const ScenePoint2D& p2) 2477 const ScenePoint2D& p2)
2416 { 2478 {
2417 annotations_.insert(new SegmentAnnotation(*this, units_, true /* show label */, p1, p2)); 2479 annotations_.insert(new LengthAnnotation(*this, units_, true /* show label */, p1, p2));
2418 } 2480 }
2419 2481
2420 2482
2421 void AnnotationsSceneLayer::AddCircleAnnotation(const ScenePoint2D& p1, 2483 void AnnotationsSceneLayer::AddCircleAnnotation(const ScenePoint2D& p1,
2422 const ScenePoint2D& p2) 2484 const ScenePoint2D& p2)
2576 } 2638 }
2577 else 2639 else
2578 { 2640 {
2579 switch (activeTool_) 2641 switch (activeTool_)
2580 { 2642 {
2581 case Tool_Segment: 2643 case Tool_Length:
2582 { 2644 {
2583 Annotation* annotation = new SegmentAnnotation(*this, units_, true /* show label */, s, s); 2645 Annotation* annotation = new LengthAnnotation(*this, units_, true /* show label */, s, s);
2584 return new CreateTwoHandlesTracker(*annotation, scene.GetCanvasToSceneTransform()); 2646 return new CreateTwoHandlesTracker(*annotation, scene.GetCanvasToSceneTransform());
2585 } 2647 }
2586 2648
2587 case Tool_Circle: 2649 case Tool_Circle:
2588 { 2650 {
2606 { 2668 {
2607 Annotation* annotation = new EllipseProbeAnnotation(*this, units_, s, s); 2669 Annotation* annotation = new EllipseProbeAnnotation(*this, units_, s, s);
2608 return new CreateTwoHandlesTracker(*annotation, scene.GetCanvasToSceneTransform()); 2670 return new CreateTwoHandlesTracker(*annotation, scene.GetCanvasToSceneTransform());
2609 } 2671 }
2610 2672
2673 /*case Tool_TextAnnotation:
2674 {
2675 Annotation* annotation = new TextAnnotation(*this, units_, true show label, s, s);
2676 return new CreateTwoHandlesTracker(*annotation, scene.GetCanvasToSceneTransform());
2677 }*/
2678
2611 default: 2679 default:
2612 return NULL; 2680 return NULL;
2613 } 2681 }
2614 } 2682 }
2615 } 2683 }
2695 } 2763 }
2696 else if (type == VALUE_CIRCLE) 2764 else if (type == VALUE_CIRCLE)
2697 { 2765 {
2698 CircleAnnotation::Unserialize(*this, units_, annotations[i]); 2766 CircleAnnotation::Unserialize(*this, units_, annotations[i]);
2699 } 2767 }
2700 else if (type == VALUE_SEGMENT) 2768 else if (type == VALUE_LENGTH)
2701 { 2769 {
2702 SegmentAnnotation::Unserialize(*this, units_, annotations[i]); 2770 LengthAnnotation::Unserialize(*this, units_, annotations[i]);
2703 } 2771 }
2704 else if (type == VALUE_PIXEL_PROBE) 2772 else if (type == VALUE_PIXEL_PROBE)
2705 { 2773 {
2706 PixelProbeAnnotation::Unserialize(*this, units_, annotations[i]); 2774 PixelProbeAnnotation::Unserialize(*this, units_, annotations[i]);
2707 } 2775 }
2711 } 2779 }
2712 else if (type == VALUE_ELLIPSE_PROBE) 2780 else if (type == VALUE_ELLIPSE_PROBE)
2713 { 2781 {
2714 EllipseProbeAnnotation::Unserialize(*this, units_, annotations[i]); 2782 EllipseProbeAnnotation::Unserialize(*this, units_, annotations[i]);
2715 } 2783 }
2784 /*else if (type == VALUE_TEXT_ANNOTATION)
2785 {
2786 TextAnnotation::Unserialize(*this, units_, annotations[i]);
2787 }*/
2716 else 2788 else
2717 { 2789 {
2718 LOG(ERROR) << "Cannot unserialize unknown type of annotation: " << type; 2790 LOG(ERROR) << "Cannot unserialize unknown type of annotation: " << type;
2719 } 2791 }
2720 } 2792 }