Mercurial > hg > orthanc-stone
comparison OrthancStone/Sources/Scene2DViewport/MeasureToolsToolbox.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/MeasureToolsToolbox.cpp@7ec8fea061b9 |
children | 8563ea5d8ae4 |
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 "MeasureToolsToolbox.h" | |
22 #include "PredeclaredTypes.h" | |
23 #include "LayerHolder.h" | |
24 #include "ViewportController.h" | |
25 | |
26 #include "../Scene2D/TextSceneLayer.h" | |
27 #include "../Scene2D/Scene2D.h" | |
28 #include "../StoneException.h" | |
29 | |
30 #include <boost/math/constants/constants.hpp> | |
31 | |
32 namespace | |
33 { | |
34 double g_pi = boost::math::constants::pi<double>(); | |
35 } | |
36 | |
37 namespace OrthancStone | |
38 { | |
39 void GetPositionOnBisectingLine( | |
40 ScenePoint2D& result | |
41 , const ScenePoint2D& p1 | |
42 , const ScenePoint2D& c | |
43 , const ScenePoint2D& p2 | |
44 , const double d) | |
45 { | |
46 // TODO: fix correct half-plane | |
47 double p1cAngle = atan2(p1.GetY() - c.GetY(), p1.GetX() - c.GetX()); | |
48 double p2cAngle = atan2(p2.GetY() - c.GetY(), p2.GetX() - c.GetX()); | |
49 double angle = 0.5 * (p1cAngle + p2cAngle); | |
50 double unitVectorX = cos(angle); | |
51 double unitVectorY = sin(angle); | |
52 double posX = c.GetX() + d * unitVectorX; | |
53 double posY = c.GetX() + d * unitVectorY; | |
54 result = ScenePoint2D(posX, posY); | |
55 } | |
56 | |
57 double RadiansToDegrees(double angleRad) | |
58 { | |
59 static const double factor = 180.0 / g_pi; | |
60 return angleRad * factor; | |
61 } | |
62 | |
63 void AddSquare(PolylineSceneLayer::Chain& chain, | |
64 const Scene2D& scene, | |
65 const ScenePoint2D& centerS, | |
66 const double& sideLengthS) | |
67 { | |
68 /* | |
69 The scene is required here because we need to draw the square with its | |
70 sides parallel to the SCREEN axis, not the SCENE axis | |
71 */ | |
72 | |
73 // get the scaling factor | |
74 const double sceneToCanvas = | |
75 scene.GetSceneToCanvasTransform().ComputeZoom(); | |
76 | |
77 chain.clear(); | |
78 chain.reserve(4); | |
79 ScenePoint2D centerC = centerS.Apply(scene.GetSceneToCanvasTransform()); | |
80 //TODO: take DPI into account | |
81 double handleLX = centerC.GetX() - sideLengthS * sceneToCanvas * 0.5; | |
82 double handleTY = centerC.GetY() - sideLengthS * sceneToCanvas * 0.5; | |
83 double handleRX = centerC.GetX() + sideLengthS * sceneToCanvas * 0.5; | |
84 double handleBY = centerC.GetY() + sideLengthS * sceneToCanvas * 0.5; | |
85 ScenePoint2D LTC(handleLX, handleTY); | |
86 ScenePoint2D RTC(handleRX, handleTY); | |
87 ScenePoint2D RBC(handleRX, handleBY); | |
88 ScenePoint2D LBC(handleLX, handleBY); | |
89 | |
90 ScenePoint2D startLT = LTC.Apply(scene.GetCanvasToSceneTransform()); | |
91 ScenePoint2D startRT = RTC.Apply(scene.GetCanvasToSceneTransform()); | |
92 ScenePoint2D startRB = RBC.Apply(scene.GetCanvasToSceneTransform()); | |
93 ScenePoint2D startLB = LBC.Apply(scene.GetCanvasToSceneTransform()); | |
94 | |
95 chain.push_back(startLT); | |
96 chain.push_back(startRT); | |
97 chain.push_back(startRB); | |
98 chain.push_back(startLB); | |
99 } | |
100 #if 0 | |
101 void AddArc( | |
102 PolylineSceneLayer::Chain & chain | |
103 , const Scene2D & scene | |
104 , const ScenePoint2D & p1 | |
105 , const ScenePoint2D & c | |
106 , const ScenePoint2D & p2 | |
107 , const double& radiusS | |
108 , const bool clockwise | |
109 , const int subdivisionsCount) | |
110 { | |
111 double p1cAngle = atan2(p1.GetY() - c.GetY(), p1.GetX() - c.GetX()); | |
112 double p2cAngle = atan2(p2.GetY() - c.GetY(), p2.GetX() - c.GetX()); | |
113 AddArc( | |
114 chain, scene, c, radiusS, p1cAngle, p2cAngle, | |
115 clockwise, subdivisionsCount); | |
116 } | |
117 #endif | |
118 | |
119 void AddShortestArc( | |
120 PolylineSceneLayer::Chain& chain | |
121 , const ScenePoint2D& p1 | |
122 , const ScenePoint2D& c | |
123 , const ScenePoint2D& p2 | |
124 , const double& radiusS | |
125 , const int subdivisionsCount) | |
126 { | |
127 double p1cAngle = atan2(p1.GetY() - c.GetY(), p1.GetX() - c.GetX()); | |
128 double p2cAngle = atan2(p2.GetY() - c.GetY(), p2.GetX() - c.GetX()); | |
129 AddShortestArc( | |
130 chain, c, radiusS, p1cAngle, p2cAngle, subdivisionsCount); | |
131 } | |
132 | |
133 void AddShortestArc( | |
134 PolylineSceneLayer::Chain& chain | |
135 , const ScenePoint2D& centerS | |
136 , const double& radiusS | |
137 , const double startAngleRad | |
138 , const double endAngleRad | |
139 , const int subdivisionsCount) | |
140 { | |
141 // this gives a signed difference between angle which | |
142 // is the smallest difference (in magnitude) between | |
143 // the angles | |
144 double delta = NormalizeAngle(endAngleRad - startAngleRad); | |
145 | |
146 chain.clear(); | |
147 chain.reserve(subdivisionsCount + 1); | |
148 | |
149 double angleIncr = delta / static_cast<double>(subdivisionsCount); | |
150 | |
151 double theta = startAngleRad; | |
152 for (int i = 0; i < subdivisionsCount + 1; ++i) | |
153 { | |
154 double offsetX = radiusS * cos(theta); | |
155 double offsetY = radiusS * sin(theta); | |
156 double pointX = centerS.GetX() + offsetX; | |
157 double pointY = centerS.GetY() + offsetY; | |
158 chain.push_back(ScenePoint2D(pointX, pointY)); | |
159 theta += angleIncr; | |
160 } | |
161 } | |
162 | |
163 #if 0 | |
164 void AddArc( | |
165 PolylineSceneLayer::Chain & chain | |
166 , const Scene2D & scene | |
167 , const ScenePoint2D & centerS | |
168 , const double& radiusS | |
169 , const double startAngleRad | |
170 , const double endAngleRad | |
171 , const bool clockwise | |
172 , const int subdivisionsCount) | |
173 { | |
174 double startAngleRadN = NormalizeAngle(startAngleRad); | |
175 double endAngleRadN = NormalizeAngle(endAngleRad); | |
176 | |
177 double angle1Rad = std::min(startAngleRadN, endAngleRadN); | |
178 double angle2Rad = std::max(startAngleRadN, endAngleRadN); | |
179 | |
180 // now we are sure angle1Rad < angle2Rad | |
181 // this means that if we draw from 1 to 2, it will be clockwise ( | |
182 // increasing angles). | |
183 // let's fix this: | |
184 if (!clockwise) | |
185 { | |
186 angle2Rad -= 2 * g_pi; | |
187 // now we are sure angle2Rad < angle1Rad (since they were normalized) | |
188 // and, thus, going from 1 to 2 means the angle values will DECREASE, | |
189 // which is the definition of anticlockwise | |
190 } | |
191 | |
192 chain.clear(); | |
193 chain.reserve(subdivisionsCount + 1); | |
194 | |
195 double angleIncr = (angle2Rad - angle1Rad) | |
196 / static_cast<double>(subdivisionsCount); | |
197 | |
198 double theta = angle1Rad; | |
199 for (int i = 0; i < subdivisionsCount + 1; ++i) | |
200 { | |
201 double offsetX = radiusS * cos(theta); | |
202 double offsetY = radiusS * sin(theta); | |
203 double pointX = centerS.GetX() + offsetX; | |
204 double pointY = centerS.GetY() + offsetY; | |
205 chain.push_back(ScenePoint2D(pointX, pointY)); | |
206 theta += angleIncr; | |
207 } | |
208 } | |
209 #endif | |
210 | |
211 void AddCircle(PolylineSceneLayer::Chain& chain, | |
212 const ScenePoint2D& centerS, | |
213 const double& radiusS, | |
214 const int numSubdivisions) | |
215 { | |
216 //ScenePoint2D centerC = centerS.Apply(scene.GetSceneToCanvasTransform()); | |
217 //TODO: take DPI into account | |
218 | |
219 // TODO: automatically compute the number for segments for smooth | |
220 // display based on the radius in pixels. | |
221 | |
222 chain.clear(); | |
223 chain.reserve(numSubdivisions); | |
224 | |
225 double angleIncr = (2.0 * g_pi) | |
226 / static_cast<double>(numSubdivisions); | |
227 | |
228 double theta = 0; | |
229 for (int i = 0; i < numSubdivisions; ++i) | |
230 { | |
231 double offsetX = radiusS * cos(theta); | |
232 double offsetY = radiusS * sin(theta); | |
233 double pointX = centerS.GetX() + offsetX; | |
234 double pointY = centerS.GetY() + offsetY; | |
235 chain.push_back(ScenePoint2D(pointX, pointY)); | |
236 theta += angleIncr; | |
237 } | |
238 } | |
239 | |
240 double NormalizeAngle(double angle) | |
241 { | |
242 double retAngle = angle; | |
243 while (retAngle < -1.0 * g_pi) | |
244 retAngle += 2 * g_pi; | |
245 while (retAngle >= g_pi) | |
246 retAngle -= 2 * g_pi; | |
247 return retAngle; | |
248 } | |
249 | |
250 double MeasureAngle(const ScenePoint2D& p1, const ScenePoint2D& c, const ScenePoint2D& p2) | |
251 { | |
252 double p1cAngle = atan2(p1.GetY() - c.GetY(), p1.GetX() - c.GetX()); | |
253 double p2cAngle = atan2(p2.GetY() - c.GetY(), p2.GetX() - c.GetX()); | |
254 double delta = p2cAngle - p1cAngle; | |
255 return NormalizeAngle(delta); | |
256 } | |
257 | |
258 | |
259 #if 0 | |
260 void AddEllipse(PolylineSceneLayer::Chain & chain, | |
261 const Scene2D & scene, | |
262 const ScenePoint2D & centerS, | |
263 const double& halfHAxis, | |
264 const double& halfVAxis) | |
265 { | |
266 chain.clear(); | |
267 chain.reserve(4); | |
268 ScenePoint2D centerC = centerS.Apply(scene.GetSceneToCanvasTransform()); | |
269 //TODO: take DPI into account | |
270 double handleLX = centerC.GetX() - sideLength / 2; | |
271 double handleTY = centerC.GetY() - sideLength / 2; | |
272 double handleRX = centerC.GetX() + sideLength / 2; | |
273 double handleBY = centerC.GetY() + sideLength / 2; | |
274 ScenePoint2D LTC(handleLX, handleTY); | |
275 ScenePoint2D RTC(handleRX, handleTY); | |
276 ScenePoint2D RBC(handleRX, handleBY); | |
277 ScenePoint2D LBC(handleLX, handleBY); | |
278 | |
279 ScenePoint2D startLT = LTC.Apply(scene.GetCanvasToSceneTransform()); | |
280 ScenePoint2D startRT = RTC.Apply(scene.GetCanvasToSceneTransform()); | |
281 ScenePoint2D startRB = RBC.Apply(scene.GetCanvasToSceneTransform()); | |
282 ScenePoint2D startLB = LBC.Apply(scene.GetCanvasToSceneTransform()); | |
283 | |
284 chain.push_back(startLT); | |
285 chain.push_back(startRT); | |
286 chain.push_back(startRB); | |
287 chain.push_back(startLB); | |
288 } | |
289 #endif | |
290 | |
291 #if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1 | |
292 /** | |
293 This utility function assumes that the layer holder contains 5 text layers | |
294 and will use the first four ones for the text background and the fifth one | |
295 for the actual text | |
296 */ | |
297 void SetTextLayerOutlineProperties( | |
298 Scene2D& scene | |
299 , boost::shared_ptr<LayerHolder> layerHolder | |
300 , const char* text | |
301 , ScenePoint2D p | |
302 , int startingLayerIndex) | |
303 { | |
304 double xoffsets[5] = { 2, 0, -2, 0, 0 }; | |
305 double yoffsets[5] = { 0, -2, 0, 2, 0 }; | |
306 | |
307 // get the scaling factor | |
308 const double pixelToScene = | |
309 scene.GetCanvasToSceneTransform().ComputeZoom(); | |
310 | |
311 for (int i = startingLayerIndex; i < startingLayerIndex + 5; ++i) | |
312 { | |
313 TextSceneLayer* textLayer = layerHolder->GetTextLayer(i); | |
314 if (textLayer != NULL) | |
315 { | |
316 textLayer->SetText(text); | |
317 | |
318 if (i == startingLayerIndex + 4) | |
319 { | |
320 textLayer->SetColor(TEXT_COLOR_RED, | |
321 TEXT_COLOR_GREEN, | |
322 TEXT_COLOR_BLUE); | |
323 } | |
324 else | |
325 { | |
326 textLayer->SetColor(TEXT_OUTLINE_COLOR_RED, | |
327 TEXT_OUTLINE_COLOR_GREEN, | |
328 TEXT_OUTLINE_COLOR_BLUE); | |
329 } | |
330 | |
331 ScenePoint2D textAnchor; | |
332 int offIndex = i - startingLayerIndex; | |
333 ORTHANC_ASSERT(offIndex >= 0 && offIndex < 5); | |
334 textLayer->SetPosition( | |
335 p.GetX() + xoffsets[offIndex] * pixelToScene, | |
336 p.GetY() + yoffsets[offIndex] * pixelToScene); | |
337 } | |
338 } | |
339 } | |
340 #else | |
341 void SetTextLayerProperties( | |
342 Scene2D& scene | |
343 , boost::shared_ptr<LayerHolder> layerHolder | |
344 , const char* text | |
345 , ScenePoint2D p | |
346 , int layerIndex) | |
347 { | |
348 TextSceneLayer* textLayer = layerHolder->GetTextLayer(layerIndex); | |
349 if (textLayer != NULL) | |
350 { | |
351 textLayer->SetText(text); | |
352 textLayer->SetColor(TEXT_COLOR_RED, TEXT_COLOR_GREEN, TEXT_COLOR_BLUE); | |
353 | |
354 ScenePoint2D textAnchor; | |
355 textLayer->SetPosition(p.GetX(), p.GetY()); | |
356 } | |
357 } | |
358 #endif | |
359 | |
360 std::ostream& operator<<(std::ostream& os, const ScenePoint2D& p) | |
361 { | |
362 os << "x = " << p.GetX() << " , y = " << p.GetY(); | |
363 return os; | |
364 } | |
365 | |
366 } |