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