comparison OrthancStone/Sources/Scene2DViewport/AngleMeasureTool.cpp @ 1512:244ad1e4e76a

reorganization of folders
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 07 Jul 2020 16:21:02 +0200
parents Framework/Scene2DViewport/AngleMeasureTool.cpp@30deba7bc8e2
children 85e117739eca
comparison
equal deleted inserted replaced
1511:9dfeee74c1e6 1512:244ad1e4e76a
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-2020 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 Affero 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 * Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/
20
21 #include "AngleMeasureTool.h"
22 #include "MeasureToolsToolbox.h"
23 #include "EditAngleMeasureTracker.h"
24 #include "LayerHolder.h"
25 #include "../StoneException.h"
26
27 #include <Logging.h>
28
29 #include <boost/math/constants/constants.hpp>
30 #include <boost/make_shared.hpp>
31
32 //// <HACK>
33 //// REMOVE THIS
34 //#ifndef NDEBUG
35 //extern void
36 //TrackerSample_SetInfoDisplayMessage(std::string key, std::string value);
37 //#endif
38 //// </HACK>
39
40 namespace OrthancStone
41 {
42 // the params in the LayerHolder ctor specify the number of polyline and text
43 // layers
44 AngleMeasureTool::AngleMeasureTool(
45 boost::shared_ptr<IViewport> viewport)
46 : MeasureTool(viewport)
47 #if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
48 , layerHolder_(boost::shared_ptr<LayerHolder>(new LayerHolder(viewport,1,5)))
49 #else
50 , layerHolder_(boost::shared_ptr<LayerHolder>(new LayerHolder(viewport,1,1)))
51 #endif
52 , angleHighlightArea_(AngleHighlightArea_None)
53 {
54 }
55
56 boost::shared_ptr<AngleMeasureTool> AngleMeasureTool::Create(boost::shared_ptr<IViewport> viewport)
57 {
58 boost::shared_ptr<AngleMeasureTool> obj(new AngleMeasureTool(viewport));
59 obj->MeasureTool::PostConstructor();
60 obj->RefreshScene();
61 return obj;
62 }
63
64 AngleMeasureTool::~AngleMeasureTool()
65 {
66 // this measuring tool is a RABI for the corresponding visual layers
67 // stored in the 2D scene
68 Disable();
69 RemoveFromScene();
70 }
71
72 void AngleMeasureTool::RemoveFromScene()
73 {
74 if (layerHolder_->AreLayersCreated() && IsSceneAlive())
75 {
76 layerHolder_->DeleteLayers();
77 }
78 }
79
80 void AngleMeasureTool::SetSide1End(ScenePoint2D pt)
81 {
82 side1End_ = pt;
83 RefreshScene();
84 }
85
86 void AngleMeasureTool::SetSide2End(ScenePoint2D pt)
87 {
88 side2End_ = pt;
89 RefreshScene();
90 }
91
92 void AngleMeasureTool::SetAngleHighlightArea(AngleHighlightArea area)
93 {
94 if (angleHighlightArea_ != area)
95 {
96 angleHighlightArea_ = area;
97 RefreshScene();
98 }
99 }
100
101 void AngleMeasureTool::ResetHighlightState()
102 {
103 SetAngleHighlightArea(AngleHighlightArea_None);
104 }
105
106
107 boost::shared_ptr<OrthancStone::MeasureToolMemento> AngleMeasureTool::GetMemento() const
108 {
109 boost::shared_ptr<AngleMeasureToolMemento> memento(new AngleMeasureToolMemento());
110 memento->center_ = center_;
111 memento->side1End_ = side1End_;
112 memento->side2End_ = side2End_;
113 return memento;
114 }
115
116 void AngleMeasureTool::SetMemento(boost::shared_ptr<MeasureToolMemento> mementoBase)
117 {
118 boost::shared_ptr<AngleMeasureToolMemento> memento =
119 boost::dynamic_pointer_cast<AngleMeasureToolMemento>(mementoBase);
120
121 ORTHANC_ASSERT(memento.get() != NULL, "Internal error: wrong (or bad) memento");
122 center_ = memento->center_;
123 side1End_ = memento->side1End_;
124 side2End_ = memento->side2End_;
125 RefreshScene();
126 }
127
128 std::string AngleMeasureTool::GetDescription()
129 {
130 std::stringstream ss;
131 ss << "AngleMeasureTool. Center = " << center_ << " Side1End = "
132 << side1End_ << " Side2End = " << side2End_;
133 return ss.str();
134 }
135
136 void AngleMeasureTool::Highlight(ScenePoint2D p)
137 {
138 AngleHighlightArea angleHighlightArea = AngleHitTest(p);
139 SetAngleHighlightArea(angleHighlightArea);
140 }
141
142 AngleMeasureTool::AngleHighlightArea AngleMeasureTool::AngleHitTest(ScenePoint2D p) const
143 {
144 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock());
145 ViewportController& controller = lock->GetController();
146 Scene2D& scene = controller.GetScene();
147
148 const double pixelToScene = scene.GetCanvasToSceneTransform().ComputeZoom();
149
150 const double SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD =
151 pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD *
152 pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD;
153
154 {
155 const double sqDistanceFromSide1End =
156 ScenePoint2D::SquaredDistancePtPt(p, side1End_);
157
158 if (sqDistanceFromSide1End <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
159 return AngleHighlightArea_Side1End;
160 }
161
162 {
163 const double sqDistanceFromSide2End =
164 ScenePoint2D::SquaredDistancePtPt(p, side2End_);
165
166 if (sqDistanceFromSide2End <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
167 return AngleHighlightArea_Side2End;
168 }
169
170 {
171 const double sqDistanceFromCenter =
172 ScenePoint2D::SquaredDistancePtPt(p, center_);
173 if (sqDistanceFromCenter <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
174 return AngleHighlightArea_Center;
175 }
176
177 {
178 const double sqDistanceFromSide1 =
179 ScenePoint2D::SquaredDistancePtSegment(center_, side1End_, p);
180
181 if (sqDistanceFromSide1 <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
182 return AngleHighlightArea_Side1;
183 }
184
185 {
186 const double sqDistanceFromSide2 =
187 ScenePoint2D::SquaredDistancePtSegment(center_, side2End_, p);
188
189 if (sqDistanceFromSide2 <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
190 return AngleHighlightArea_Side2;
191 }
192
193 return AngleHighlightArea_None;
194 }
195
196 bool AngleMeasureTool::HitTest(ScenePoint2D p)
197 {
198 return AngleHitTest(p) != AngleHighlightArea_None;
199 }
200
201
202 boost::shared_ptr<IFlexiblePointerTracker> AngleMeasureTool::CreateEditionTracker(const PointerEvent& e)
203 {
204 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock());
205 ViewportController& controller = lock->GetController();
206 Scene2D& scene = controller.GetScene();
207
208 ScenePoint2D scenePos = e.GetMainPosition().Apply(
209 scene.GetCanvasToSceneTransform());
210
211 if (!HitTest(scenePos))
212 return boost::shared_ptr<IFlexiblePointerTracker>();
213
214 /**
215 new EditLineMeasureTracker(
216 boost::shared_ptr<LineMeasureTool> measureTool;
217 MessageBroker & broker,
218 boost::shared_ptr<IViewport> viewport,
219 const PointerEvent & e);
220 */
221
222 boost::shared_ptr<EditAngleMeasureTracker> editAngleMeasureTracker(
223 new EditAngleMeasureTracker(shared_from_this(), viewport_, e));
224 return editAngleMeasureTracker;
225 }
226
227 void AngleMeasureTool::SetCenter(ScenePoint2D pt)
228 {
229 center_ = pt;
230 RefreshScene();
231 }
232
233 void AngleMeasureTool::RefreshScene()
234 {
235 if (IsSceneAlive())
236 {
237 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock());
238 ViewportController& controller = lock->GetController();
239 Scene2D& scene = controller.GetScene();
240
241 if (IsEnabled())
242 {
243 layerHolder_->CreateLayersIfNeeded();
244
245 {
246 // Fill the polyline layer with the measurement lines
247 PolylineSceneLayer* polylineLayer = layerHolder_->GetPolylineLayer(0);
248 if (polylineLayer)
249 {
250 polylineLayer->ClearAllChains();
251
252 const Color color(TOOL_ANGLE_LINES_COLOR_RED,
253 TOOL_ANGLE_LINES_COLOR_GREEN,
254 TOOL_ANGLE_LINES_COLOR_BLUE);
255
256 const Color highlightColor(TOOL_ANGLE_LINES_HL_COLOR_RED,
257 TOOL_ANGLE_LINES_HL_COLOR_GREEN,
258 TOOL_ANGLE_LINES_HL_COLOR_BLUE);
259
260 // sides
261 {
262 {
263 PolylineSceneLayer::Chain chain;
264 chain.push_back(side1End_);
265 chain.push_back(center_);
266
267 if ((angleHighlightArea_ == AngleHighlightArea_Side1) ||
268 (angleHighlightArea_ == AngleHighlightArea_Side2))
269 {
270 polylineLayer->AddChain(chain, false, highlightColor);
271 }
272 else
273 {
274 polylineLayer->AddChain(chain, false, color);
275 }
276 }
277 {
278 PolylineSceneLayer::Chain chain;
279 chain.push_back(side2End_);
280 chain.push_back(center_);
281 if ((angleHighlightArea_ == AngleHighlightArea_Side1) ||
282 (angleHighlightArea_ == AngleHighlightArea_Side2))
283 {
284 polylineLayer->AddChain(chain, false, highlightColor);
285 }
286 else
287 {
288 polylineLayer->AddChain(chain, false, color);
289 }
290 }
291 }
292
293 // Create the handles
294 {
295 {
296 PolylineSceneLayer::Chain chain;
297 //TODO: take DPI into account
298 AddSquare(chain, controller.GetScene(), side1End_,
299 controller.GetHandleSideLengthS());
300
301 if (angleHighlightArea_ == AngleHighlightArea_Side1End)
302 polylineLayer->AddChain(chain, true, highlightColor);
303 else
304 polylineLayer->AddChain(chain, true, color);
305
306 }
307 {
308 PolylineSceneLayer::Chain chain;
309 //TODO: take DPI into account
310 AddSquare(chain, controller.GetScene(), side2End_,
311 controller.GetHandleSideLengthS());
312
313 if (angleHighlightArea_ == AngleHighlightArea_Side2End)
314 polylineLayer->AddChain(chain, true, highlightColor);
315 else
316 polylineLayer->AddChain(chain, true, color);
317 }
318 }
319
320 // Create the arc
321 {
322 PolylineSceneLayer::Chain chain;
323
324 AddShortestArc(chain, side1End_, center_, side2End_,
325 controller.GetAngleToolArcRadiusS());
326 if (angleHighlightArea_ == AngleHighlightArea_Center)
327 polylineLayer->AddChain(chain, false, highlightColor);
328 else
329 polylineLayer->AddChain(chain, false, color);
330 }
331 }
332 }
333 {
334 // Set the text layer
335
336 double p1cAngle = atan2(
337 side1End_.GetY() - center_.GetY(),
338 side1End_.GetX() - center_.GetX());
339
340 double p2cAngle = atan2(
341 side2End_.GetY() - center_.GetY(),
342 side2End_.GetX() - center_.GetX());
343
344 double delta = NormalizeAngle(p2cAngle - p1cAngle);
345 double theta = p1cAngle + delta / 2;
346
347 double ox = controller.GetAngleTopTextLabelDistanceS() * cos(theta);
348 double oy = controller.GetAngleTopTextLabelDistanceS() * sin(theta);
349
350 double pointX = center_.GetX() + ox;
351 double pointY = center_.GetY() + oy;
352
353 char buf[64];
354 double angleDeg = RadiansToDegrees(delta);
355
356 // http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=00B0&mode=hex
357 sprintf(buf, "%0.02f\xc2\xb0", angleDeg);
358
359 #if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
360 SetTextLayerOutlineProperties(
361 scene, layerHolder_, buf, ScenePoint2D(pointX, pointY), 0);
362 #else
363 SetTextLayerProperties(
364 scene, layerHolder_, buf, ScenePoint2D(pointX, pointY) , 0);
365 #endif
366
367 #if 0
368 // TODO:make it togglable
369 bool enableInfoDisplay = true;
370 if (enableInfoDisplay)
371 {
372 TrackerSample_SetInfoDisplayMessage("center_.GetX()",
373 boost::lexical_cast<std::string>(center_.GetX()));
374
375 TrackerSample_SetInfoDisplayMessage("center_.GetY()",
376 boost::lexical_cast<std::string>(center_.GetY()));
377
378 TrackerSample_SetInfoDisplayMessage("side1End_.GetX()",
379 boost::lexical_cast<std::string>(side1End_.GetX()));
380
381 TrackerSample_SetInfoDisplayMessage("side1End_.GetY()",
382 boost::lexical_cast<std::string>(side1End_.GetY()));
383
384 TrackerSample_SetInfoDisplayMessage("side2End_.GetX()",
385 boost::lexical_cast<std::string>(side2End_.GetX()));
386
387 TrackerSample_SetInfoDisplayMessage("side2End_.GetY()",
388 boost::lexical_cast<std::string>(side2End_.GetY()));
389
390 TrackerSample_SetInfoDisplayMessage("p1cAngle (deg)",
391 boost::lexical_cast<std::string>(RadiansToDegrees(p1cAngle)));
392
393 TrackerSample_SetInfoDisplayMessage("delta (deg)",
394 boost::lexical_cast<std::string>(RadiansToDegrees(delta)));
395
396 TrackerSample_SetInfoDisplayMessage("theta (deg)",
397 boost::lexical_cast<std::string>(RadiansToDegrees(theta)));
398
399 TrackerSample_SetInfoDisplayMessage("p2cAngle (deg)",
400 boost::lexical_cast<std::string>(RadiansToDegrees(p2cAngle)));
401
402 TrackerSample_SetInfoDisplayMessage("ox (scene)",
403 boost::lexical_cast<std::string>(ox));
404
405 TrackerSample_SetInfoDisplayMessage("offsetY (scene)",
406 boost::lexical_cast<std::string>(oy));
407
408 TrackerSample_SetInfoDisplayMessage("pointX",
409 boost::lexical_cast<std::string>(pointX));
410
411 TrackerSample_SetInfoDisplayMessage("pointY",
412 boost::lexical_cast<std::string>(pointY));
413
414 TrackerSample_SetInfoDisplayMessage("angleDeg",
415 boost::lexical_cast<std::string>(angleDeg));
416 }
417 #endif
418 }
419 }
420 else
421 {
422 RemoveFromScene();
423 }
424 lock->Invalidate();
425 }
426 }
427 }