Mercurial > hg > orthanc-stone
comparison Applications/Samples/SingleFrameEditorApplication.h @ 415:c0589c3173fd
finished reorganization
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 13 Nov 2018 10:36:53 +0100 |
parents | f7616c010056 |
children | aee3d7941c9b |
comparison
equal
deleted
inserted
replaced
414:f7616c010056 | 415:c0589c3173fd |
---|---|
21 | 21 |
22 #pragma once | 22 #pragma once |
23 | 23 |
24 #include "SampleApplicationBase.h" | 24 #include "SampleApplicationBase.h" |
25 | 25 |
26 #include "../../Framework/Radiography/RadiographyLayerCropTracker.h" | |
26 #include "../../Framework/Radiography/RadiographyLayerMoveTracker.h" | 27 #include "../../Framework/Radiography/RadiographyLayerMoveTracker.h" |
28 #include "../../Framework/Radiography/RadiographyLayerResizeTracker.h" | |
27 #include "../../Framework/Radiography/RadiographyLayerRotateTracker.h" | 29 #include "../../Framework/Radiography/RadiographyLayerRotateTracker.h" |
28 #include "../../Framework/Radiography/RadiographyScene.h" | 30 #include "../../Framework/Radiography/RadiographyScene.h" |
29 #include "../../Framework/Radiography/RadiographySceneCommand.h" | 31 #include "../../Framework/Radiography/RadiographySceneCommand.h" |
30 #include "../../Framework/Radiography/RadiographyWidget.h" | 32 #include "../../Framework/Radiography/RadiographyWidget.h" |
31 | 33 #include "../../Framework/Radiography/RadiographyWindowingTracker.h" |
32 #include "../../Framework/Toolbox/UndoRedoStack.h" | |
33 | 34 |
34 #include <Core/Images/FontRegistry.h> | 35 #include <Core/Images/FontRegistry.h> |
35 #include <Core/Images/Image.h> | |
36 #include <Core/Images/ImageProcessing.h> | |
37 #include <Core/Images/PamReader.h> | |
38 #include <Core/Images/PamWriter.h> | |
39 #include <Core/Images/PngWriter.h> | |
40 #include <Core/Logging.h> | 36 #include <Core/Logging.h> |
41 #include <Core/OrthancException.h> | 37 #include <Core/OrthancException.h> |
42 #include <Core/Toolbox.h> | |
43 #include <Plugins/Samples/Common/DicomDatasetReader.h> | |
44 #include <Plugins/Samples/Common/FullOrthancDataset.h> | |
45 | 38 |
46 | 39 |
47 // Export using PAM is faster than using PNG, but requires Orthanc | 40 // Export using PAM is faster than using PNG, but requires Orthanc |
48 // core >= 1.4.3 | 41 // core >= 1.4.3 |
49 #define EXPORT_USING_PAM 1 | 42 #define EXPORT_USING_PAM 1 |
50 | 43 |
51 | 44 |
52 #include <boost/math/special_functions/round.hpp> | |
53 | |
54 | |
55 namespace OrthancStone | 45 namespace OrthancStone |
56 { | 46 { |
57 class RadiographyLayerCropTracker : public IWorldSceneMouseTracker | |
58 { | |
59 private: | |
60 UndoRedoStack& undoRedoStack_; | |
61 RadiographyScene::LayerAccessor accessor_; | |
62 Corner corner_; | |
63 unsigned int cropX_; | |
64 unsigned int cropY_; | |
65 unsigned int cropWidth_; | |
66 unsigned int cropHeight_; | |
67 | |
68 class UndoRedoCommand : public RadiographySceneCommand | |
69 { | |
70 private: | |
71 unsigned int sourceCropX_; | |
72 unsigned int sourceCropY_; | |
73 unsigned int sourceCropWidth_; | |
74 unsigned int sourceCropHeight_; | |
75 unsigned int targetCropX_; | |
76 unsigned int targetCropY_; | |
77 unsigned int targetCropWidth_; | |
78 unsigned int targetCropHeight_; | |
79 | |
80 protected: | |
81 virtual void UndoInternal(RadiographyLayer& layer) const | |
82 { | |
83 layer.SetCrop(sourceCropX_, sourceCropY_, sourceCropWidth_, sourceCropHeight_); | |
84 } | |
85 | |
86 virtual void RedoInternal(RadiographyLayer& layer) const | |
87 { | |
88 layer.SetCrop(targetCropX_, targetCropY_, targetCropWidth_, targetCropHeight_); | |
89 } | |
90 | |
91 public: | |
92 UndoRedoCommand(const RadiographyLayerCropTracker& tracker) : | |
93 RadiographySceneCommand(tracker.accessor_), | |
94 sourceCropX_(tracker.cropX_), | |
95 sourceCropY_(tracker.cropY_), | |
96 sourceCropWidth_(tracker.cropWidth_), | |
97 sourceCropHeight_(tracker.cropHeight_) | |
98 { | |
99 tracker.accessor_.GetLayer().GetCrop(targetCropX_, targetCropY_, | |
100 targetCropWidth_, targetCropHeight_); | |
101 } | |
102 }; | |
103 | |
104 | |
105 public: | |
106 RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack, | |
107 RadiographyScene& scene, | |
108 const ViewportGeometry& view, | |
109 size_t layer, | |
110 double x, | |
111 double y, | |
112 Corner corner) : | |
113 undoRedoStack_(undoRedoStack), | |
114 accessor_(scene, layer), | |
115 corner_(corner) | |
116 { | |
117 if (accessor_.IsValid()) | |
118 { | |
119 accessor_.GetLayer().GetCrop(cropX_, cropY_, cropWidth_, cropHeight_); | |
120 } | |
121 } | |
122 | |
123 virtual bool HasRender() const | |
124 { | |
125 return false; | |
126 } | |
127 | |
128 virtual void Render(CairoContext& context, | |
129 double zoom) | |
130 { | |
131 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
132 } | |
133 | |
134 virtual void MouseUp() | |
135 { | |
136 if (accessor_.IsValid()) | |
137 { | |
138 undoRedoStack_.Add(new UndoRedoCommand(*this)); | |
139 } | |
140 } | |
141 | |
142 virtual void MouseMove(int displayX, | |
143 int displayY, | |
144 double sceneX, | |
145 double sceneY) | |
146 { | |
147 if (accessor_.IsValid()) | |
148 { | |
149 unsigned int x, y; | |
150 | |
151 RadiographyLayer& layer = accessor_.GetLayer(); | |
152 if (layer.GetPixel(x, y, sceneX, sceneY)) | |
153 { | |
154 unsigned int targetX, targetWidth; | |
155 | |
156 if (corner_ == Corner_TopLeft || | |
157 corner_ == Corner_BottomLeft) | |
158 { | |
159 targetX = std::min(x, cropX_ + cropWidth_); | |
160 targetWidth = cropX_ + cropWidth_ - targetX; | |
161 } | |
162 else | |
163 { | |
164 targetX = cropX_; | |
165 targetWidth = std::max(x, cropX_) - cropX_; | |
166 } | |
167 | |
168 unsigned int targetY, targetHeight; | |
169 | |
170 if (corner_ == Corner_TopLeft || | |
171 corner_ == Corner_TopRight) | |
172 { | |
173 targetY = std::min(y, cropY_ + cropHeight_); | |
174 targetHeight = cropY_ + cropHeight_ - targetY; | |
175 } | |
176 else | |
177 { | |
178 targetY = cropY_; | |
179 targetHeight = std::max(y, cropY_) - cropY_; | |
180 } | |
181 | |
182 layer.SetCrop(targetX, targetY, targetWidth, targetHeight); | |
183 } | |
184 } | |
185 } | |
186 }; | |
187 | |
188 | |
189 class RadiographyLayerResizeTracker : public IWorldSceneMouseTracker | |
190 { | |
191 private: | |
192 UndoRedoStack& undoRedoStack_; | |
193 RadiographyScene::LayerAccessor accessor_; | |
194 bool roundScaling_; | |
195 double originalSpacingX_; | |
196 double originalSpacingY_; | |
197 double originalPanX_; | |
198 double originalPanY_; | |
199 Corner oppositeCorner_; | |
200 double oppositeX_; | |
201 double oppositeY_; | |
202 double baseScaling_; | |
203 | |
204 static double ComputeDistance(double x1, | |
205 double y1, | |
206 double x2, | |
207 double y2) | |
208 { | |
209 double dx = x1 - x2; | |
210 double dy = y1 - y2; | |
211 return sqrt(dx * dx + dy * dy); | |
212 } | |
213 | |
214 class UndoRedoCommand : public RadiographySceneCommand | |
215 { | |
216 private: | |
217 double sourceSpacingX_; | |
218 double sourceSpacingY_; | |
219 double sourcePanX_; | |
220 double sourcePanY_; | |
221 double targetSpacingX_; | |
222 double targetSpacingY_; | |
223 double targetPanX_; | |
224 double targetPanY_; | |
225 | |
226 protected: | |
227 virtual void UndoInternal(RadiographyLayer& layer) const | |
228 { | |
229 layer.SetPixelSpacing(sourceSpacingX_, sourceSpacingY_); | |
230 layer.SetPan(sourcePanX_, sourcePanY_); | |
231 } | |
232 | |
233 virtual void RedoInternal(RadiographyLayer& layer) const | |
234 { | |
235 layer.SetPixelSpacing(targetSpacingX_, targetSpacingY_); | |
236 layer.SetPan(targetPanX_, targetPanY_); | |
237 } | |
238 | |
239 public: | |
240 UndoRedoCommand(const RadiographyLayerResizeTracker& tracker) : | |
241 RadiographySceneCommand(tracker.accessor_), | |
242 sourceSpacingX_(tracker.originalSpacingX_), | |
243 sourceSpacingY_(tracker.originalSpacingY_), | |
244 sourcePanX_(tracker.originalPanX_), | |
245 sourcePanY_(tracker.originalPanY_), | |
246 targetSpacingX_(tracker.accessor_.GetLayer().GetPixelSpacingX()), | |
247 targetSpacingY_(tracker.accessor_.GetLayer().GetPixelSpacingY()), | |
248 targetPanX_(tracker.accessor_.GetLayer().GetPanX()), | |
249 targetPanY_(tracker.accessor_.GetLayer().GetPanY()) | |
250 { | |
251 } | |
252 }; | |
253 | |
254 | |
255 public: | |
256 RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack, | |
257 RadiographyScene& scene, | |
258 size_t layer, | |
259 double x, | |
260 double y, | |
261 Corner corner, | |
262 bool roundScaling) : | |
263 undoRedoStack_(undoRedoStack), | |
264 accessor_(scene, layer), | |
265 roundScaling_(roundScaling) | |
266 { | |
267 if (accessor_.IsValid() && | |
268 accessor_.GetLayer().IsResizeable()) | |
269 { | |
270 originalSpacingX_ = accessor_.GetLayer().GetPixelSpacingX(); | |
271 originalSpacingY_ = accessor_.GetLayer().GetPixelSpacingY(); | |
272 originalPanX_ = accessor_.GetLayer().GetPanX(); | |
273 originalPanY_ = accessor_.GetLayer().GetPanY(); | |
274 | |
275 switch (corner) | |
276 { | |
277 case Corner_TopLeft: | |
278 oppositeCorner_ = Corner_BottomRight; | |
279 break; | |
280 | |
281 case Corner_TopRight: | |
282 oppositeCorner_ = Corner_BottomLeft; | |
283 break; | |
284 | |
285 case Corner_BottomLeft: | |
286 oppositeCorner_ = Corner_TopRight; | |
287 break; | |
288 | |
289 case Corner_BottomRight: | |
290 oppositeCorner_ = Corner_TopLeft; | |
291 break; | |
292 | |
293 default: | |
294 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
295 } | |
296 | |
297 accessor_.GetLayer().GetCorner(oppositeX_, oppositeY_, oppositeCorner_); | |
298 | |
299 double d = ComputeDistance(x, y, oppositeX_, oppositeY_); | |
300 if (d >= std::numeric_limits<float>::epsilon()) | |
301 { | |
302 baseScaling_ = 1.0 / d; | |
303 } | |
304 else | |
305 { | |
306 // Avoid division by zero in extreme cases | |
307 accessor_.Invalidate(); | |
308 } | |
309 } | |
310 } | |
311 | |
312 virtual bool HasRender() const | |
313 { | |
314 return false; | |
315 } | |
316 | |
317 virtual void Render(CairoContext& context, | |
318 double zoom) | |
319 { | |
320 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
321 } | |
322 | |
323 virtual void MouseUp() | |
324 { | |
325 if (accessor_.IsValid() && | |
326 accessor_.GetLayer().IsResizeable()) | |
327 { | |
328 undoRedoStack_.Add(new UndoRedoCommand(*this)); | |
329 } | |
330 } | |
331 | |
332 virtual void MouseMove(int displayX, | |
333 int displayY, | |
334 double sceneX, | |
335 double sceneY) | |
336 { | |
337 static const double ROUND_SCALING = 0.1; | |
338 | |
339 if (accessor_.IsValid() && | |
340 accessor_.GetLayer().IsResizeable()) | |
341 { | |
342 double scaling = ComputeDistance(oppositeX_, oppositeY_, sceneX, sceneY) * baseScaling_; | |
343 | |
344 if (roundScaling_) | |
345 { | |
346 scaling = boost::math::round<double>((scaling / ROUND_SCALING) * ROUND_SCALING); | |
347 } | |
348 | |
349 RadiographyLayer& layer = accessor_.GetLayer(); | |
350 layer.SetPixelSpacing(scaling * originalSpacingX_, | |
351 scaling * originalSpacingY_); | |
352 | |
353 // Keep the opposite corner at a fixed location | |
354 double ox, oy; | |
355 layer.GetCorner(ox, oy, oppositeCorner_); | |
356 layer.SetPan(layer.GetPanX() + oppositeX_ - ox, | |
357 layer.GetPanY() + oppositeY_ - oy); | |
358 } | |
359 } | |
360 }; | |
361 | |
362 | |
363 class RadiographyWindowingTracker : public IWorldSceneMouseTracker | |
364 { | |
365 public: | |
366 enum Action | |
367 { | |
368 Action_IncreaseWidth, | |
369 Action_DecreaseWidth, | |
370 Action_IncreaseCenter, | |
371 Action_DecreaseCenter | |
372 }; | |
373 | |
374 private: | |
375 UndoRedoStack& undoRedoStack_; | |
376 RadiographyScene& scene_; | |
377 int clickX_; | |
378 int clickY_; | |
379 Action leftAction_; | |
380 Action rightAction_; | |
381 Action upAction_; | |
382 Action downAction_; | |
383 float strength_; | |
384 float sourceCenter_; | |
385 float sourceWidth_; | |
386 | |
387 static void ComputeAxisEffect(int& deltaCenter, | |
388 int& deltaWidth, | |
389 int delta, | |
390 Action actionNegative, | |
391 Action actionPositive) | |
392 { | |
393 if (delta < 0) | |
394 { | |
395 switch (actionNegative) | |
396 { | |
397 case Action_IncreaseWidth: | |
398 deltaWidth = -delta; | |
399 break; | |
400 | |
401 case Action_DecreaseWidth: | |
402 deltaWidth = delta; | |
403 break; | |
404 | |
405 case Action_IncreaseCenter: | |
406 deltaCenter = -delta; | |
407 break; | |
408 | |
409 case Action_DecreaseCenter: | |
410 deltaCenter = delta; | |
411 break; | |
412 | |
413 default: | |
414 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
415 } | |
416 } | |
417 else if (delta > 0) | |
418 { | |
419 switch (actionPositive) | |
420 { | |
421 case Action_IncreaseWidth: | |
422 deltaWidth = delta; | |
423 break; | |
424 | |
425 case Action_DecreaseWidth: | |
426 deltaWidth = -delta; | |
427 break; | |
428 | |
429 case Action_IncreaseCenter: | |
430 deltaCenter = delta; | |
431 break; | |
432 | |
433 case Action_DecreaseCenter: | |
434 deltaCenter = -delta; | |
435 break; | |
436 | |
437 default: | |
438 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
439 } | |
440 } | |
441 } | |
442 | |
443 | |
444 class UndoRedoCommand : public UndoRedoStack::ICommand | |
445 { | |
446 private: | |
447 RadiographyScene& scene_; | |
448 float sourceCenter_; | |
449 float sourceWidth_; | |
450 float targetCenter_; | |
451 float targetWidth_; | |
452 | |
453 public: | |
454 UndoRedoCommand(const RadiographyWindowingTracker& tracker) : | |
455 scene_(tracker.scene_), | |
456 sourceCenter_(tracker.sourceCenter_), | |
457 sourceWidth_(tracker.sourceWidth_) | |
458 { | |
459 scene_.GetWindowingWithDefault(targetCenter_, targetWidth_); | |
460 } | |
461 | |
462 virtual void Undo() const | |
463 { | |
464 scene_.SetWindowing(sourceCenter_, sourceWidth_); | |
465 } | |
466 | |
467 virtual void Redo() const | |
468 { | |
469 scene_.SetWindowing(targetCenter_, targetWidth_); | |
470 } | |
471 }; | |
472 | |
473 | |
474 public: | |
475 RadiographyWindowingTracker(UndoRedoStack& undoRedoStack, | |
476 RadiographyScene& scene, | |
477 int x, | |
478 int y, | |
479 Action leftAction, | |
480 Action rightAction, | |
481 Action upAction, | |
482 Action downAction) : | |
483 undoRedoStack_(undoRedoStack), | |
484 scene_(scene), | |
485 clickX_(x), | |
486 clickY_(y), | |
487 leftAction_(leftAction), | |
488 rightAction_(rightAction), | |
489 upAction_(upAction), | |
490 downAction_(downAction) | |
491 { | |
492 scene_.GetWindowingWithDefault(sourceCenter_, sourceWidth_); | |
493 | |
494 float minValue, maxValue; | |
495 scene.GetRange(minValue, maxValue); | |
496 | |
497 assert(minValue <= maxValue); | |
498 | |
499 float tmp; | |
500 | |
501 float delta = (maxValue - minValue); | |
502 if (delta <= 1) | |
503 { | |
504 tmp = 0; | |
505 } | |
506 else | |
507 { | |
508 // NB: Visual Studio 2008 does not provide "log2f()", so we | |
509 // implement it by ourselves | |
510 tmp = logf(delta) / logf(2.0f); | |
511 } | |
512 | |
513 strength_ = tmp - 7; | |
514 if (strength_ < 1) | |
515 { | |
516 strength_ = 1; | |
517 } | |
518 } | |
519 | |
520 virtual bool HasRender() const | |
521 { | |
522 return false; | |
523 } | |
524 | |
525 virtual void Render(CairoContext& context, | |
526 double zoom) | |
527 { | |
528 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
529 } | |
530 | |
531 virtual void MouseUp() | |
532 { | |
533 undoRedoStack_.Add(new UndoRedoCommand(*this)); | |
534 } | |
535 | |
536 | |
537 virtual void MouseMove(int displayX, | |
538 int displayY, | |
539 double sceneX, | |
540 double sceneY) | |
541 { | |
542 // https://bitbucket.org/osimis/osimis-webviewer-plugin/src/master/frontend/src/app/viewport/image-plugins/windowing-viewport-tool.class.js | |
543 | |
544 static const float SCALE = 1.0; | |
545 | |
546 int deltaCenter = 0; | |
547 int deltaWidth = 0; | |
548 | |
549 ComputeAxisEffect(deltaCenter, deltaWidth, displayX - clickX_, leftAction_, rightAction_); | |
550 ComputeAxisEffect(deltaCenter, deltaWidth, displayY - clickY_, upAction_, downAction_); | |
551 | |
552 float newCenter = sourceCenter_ + (deltaCenter / SCALE * strength_); | |
553 float newWidth = sourceWidth_ + (deltaWidth / SCALE * strength_); | |
554 scene_.SetWindowing(newCenter, newWidth); | |
555 } | |
556 }; | |
557 | |
558 | |
559 | |
560 | |
561 namespace Samples | 47 namespace Samples |
562 { | 48 { |
563 class RadiographyEditorInteractor : | 49 class RadiographyEditorInteractor : |
564 public IWorldSceneInteractor, | 50 public IWorldSceneInteractor, |
565 public IObserver | 51 public IObserver |