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 }