fw4spl
SScaleValues.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-2017.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #include "scene2D/adaptor/SScaleValues.hpp"
8 
9 #include <fwRenderQt/data/InitQtPen.hpp>
10 #include <fwRenderQt/Scene2DGraphicsView.hpp>
11 
12 #include <fwServices/macros.hpp>
13 
14 #include <QGraphicsItemGroup>
15 
16 fwServicesRegisterMacro( ::fwRenderQt::IAdaptor, ::scene2D::adaptor::SScaleValues);
17 
18 namespace scene2D
19 {
20 namespace adaptor
21 {
22 
23 static const ::fwServices::IService::KeyType s_VIEWPORT_INPUT = "viewport";
24 
26  m_min(0.f),
27  m_max(0.f),
28  m_interval(10.),
29  m_step(1),
30  m_fontSize(8.f),
31  m_showUnit(true),
32  m_unit(nullptr),
33  m_layer(nullptr)
34 {
35 }
36 
37 //---------------------------------------------------------------------------------------------------------------
38 
40 {
41 }
42 
43 //---------------------------------------------------------------------------------------------------------------
44 
46 {
47  this->configureParams(); // xAxis, yAxis, zValue, opacity
48 
49  const ConfigType config = this->getConfigTree().get_child("config.<xmlattr>");
50 
51  // Set the min/max values
52  SLM_ASSERT("'min' atttribute is missing.", config.count("min"));
53  SLM_ASSERT("'min' atttribute is missing.", config.count("max"));
54 
55  m_min = config.get<double>("min");
56  m_max = config.get<double>("max");
57 
58  // Interval configuration
59  if (config.count("interval"))
60  {
61  m_interval = config.get<float>("interval");
62  }
63 
64  // Color configuration
65  if (config.count("color"))
66  {
67  ::fwRenderQt::data::InitQtPen::setPenColor(m_pen, config.get<std::string>("color"), m_opacity);
68  }
69  else
70  {
72  }
73 
74  // Font size configuratiion
75  if (config.count("fontSize"))
76  {
77  m_fontSize = config.get<float>("fontSize");
78  }
79 
80  // Show unit
81  if (config.count("showUnit"))
82  {
83  const std::string showUnit = config.get<std::string>("showUnit");
84  SLM_ASSERT("Unsupported value "+showUnit+" for 'showUnit' attribute (true or false expected).",
85  showUnit == "true" || showUnit == "false");
86  m_showUnit = (showUnit == "true");
87  }
88 
89  // Unit text configuration
90  m_displayedUnit = config.get<std::string>("unit", "");
91 
92  SLM_ASSERT("'align' attribute is missing. "
93  "Please add an 'align' attribute with value 'left', 'right', 'top' or 'bottom'",
94  config.count("align"));
95 
96  // 'align' attribute configuration
97  m_align = config.get<std::string>("align");
98 
99  SLM_ASSERT("Unsupported value for 'align' attribute.",
100  m_align == "left" || m_align == "right" || m_align == "top" || m_align == "bottom");
101 }
102 
103 //---------------------------------------------------------------------------------------------------------------
104 
105 void SScaleValues::buildValues()
106 {
107  SLM_ASSERT("m_interval can not be equal to 0", m_interval != 0);
108 
109  m_values.clear();
110 
111  double val = this->getStartVal();
112  const int range = (int) ceil(this->getEndVal() - val);
113  const int nbValues = (int)(ceil(range/ m_interval)) + 1;
114 
115  std::string format;
116 
117  if(m_align == "left")
118  {
119  format = " %1";
120  }
121  else if(m_align == "right")
122  {
123  format = "%1 ";
124  }
125  else
126  {
127  format = "%1";
128  }
129 
130  // Build the values as graphic items
131  for(int i = 0; i < nbValues; ++i, val += m_interval)
132  {
133  QGraphicsSimpleTextItem* text = new QGraphicsSimpleTextItem();
134  text->setText( QString::fromStdString(format).arg(val) );
135  text->setFont( m_font );
136  text->setBrush( m_brush );
137  text->setCacheMode( QGraphicsItem::DeviceCoordinateCache );
138 
139  m_values.push_back( text );
140  }
141 
142  // Add the values to the item group
143  for( QGraphicsItem* item : m_values )
144  {
145  m_layer->addToGroup( item );
146  }
147 
148  // Build the unit graphic item
149  m_unit = new QGraphicsSimpleTextItem();
150  m_unit->setText( QString::fromStdString( m_displayedUnit ) );
151  m_unit->setFont( m_font );
152  m_unit->setBrush( m_brush );
153  m_unit->setCacheMode( QGraphicsItem::DeviceCoordinateCache );
154 
155  // Add it to the items to be displayed if required
156  if( m_showUnit )
157  {
158  m_layer->addToGroup( m_unit );
159  }
160 
161  // Set the layer position (according to the related axis) and zValue
162  m_layer->setPos( m_xAxis->getOrigin(), m_yAxis->getOrigin() );
163  m_layer->setZValue( m_zValue );
164 
165  // Add the layer containing grid's lines to the scene
166  this->getScene2DRender()->getScene()->addItem( m_layer );
167 }
168 
169 //---------------------------------------------------------------------------------------
170 
172 {
173  // Initialize the layer
174  m_layer = new QGraphicsItemGroup();
175 
176  m_brush = QBrush( m_pen.color() );
177  m_pen.setCosmetic(true);
178 
179  m_font.setPointSize( m_fontSize );
180  m_font.setLetterSpacing( QFont::AbsoluteSpacing, 0.25 );
181  m_font.setKerning( true );
182  m_font.setFixedPitch( true );
183 
184  this->buildValues();
185  this->updating();
186 }
187 
188 //---------------------------------------------------------------------------------------
189 
190 double SScaleValues::getStartVal()
191 {
192  return (int)( m_min / m_interval ) * m_interval;
193 }
194 
195 //---------------------------------------------------------------------------------------
196 double SScaleValues::getEndVal()
197 {
198  return (int)( m_max / m_interval ) * m_interval;
199 }
200 
201 //---------------------------------------------------------------------------------------
202 
204 {
205  this->initializeViewSize();
206  this->initializeViewportSize();
207 
208  this->rescaleValues();
209 }
210 
211 //---------------------------------------------------------------------------------------
212 
213 void SScaleValues::rescaleValues()
214 {
215  ::fwRenderQt::data::Viewport::csptr viewport =
216  this->getInput< ::fwRenderQt::data::Viewport>(s_VIEWPORT_INPUT);
217 
218  const double viewportX = viewport->getX();
219  const double viewportWidth = viewport->getWidth();
220  const double viewportHeight = viewport->getHeight();
221 
222  const double viewportSizeRatio = viewportHeight / viewportWidth;
223  const double viewInitialSizeRatio = m_viewInitialSize.first / m_viewInitialSize.second;
224 
225  const Scene2DRatio ratio = this->getRatio(); // Total ratio
226  const double viewportWidthRatio = this->getViewportSizeRatio().first;
227 
228  double scaleX = m_fontSize;
229  double scaleY = m_fontSize * viewportSizeRatio;
230  scaleY /= viewportWidthRatio;
231  scaleY *= viewInitialSizeRatio;
232 
233  scaleX = scaleX * ratio.first;
234  scaleY = scaleY * ratio.second;
235 
236  QTransform transform;
237  transform.scale( scaleX, scaleY );
238 
239  int step = 0;
240  double valueSize; // the size (width or height) of the current value
241  Point2DType coord; // coordinates of the current scale value item
242  Point2DType size; // size of the current scale value item
243  bool suggestResampling = false; /* scale value items resampling is suggested because of
244  a lack of sufficient width to display all of them */
245 
246  const size_t valuesSize = m_values.size();
247  float val = getStartVal();
248 
249  if(m_align == "left" || m_align == "right")
250  {
251  const double valueSizeRatio = m_interval / scaleY;
252 
253  float coeff = 0.f;
254 
255  double textPosX;
256 
257  if(m_align == "left")
258  {
259  textPosX = viewportX;
260  }
261  else
262  {
263  coeff = -1.f;
264  textPosX = viewportX + viewportWidth;
265  }
266 
267  for(int i = 0; i < valuesSize; ++i, val += m_interval)
268  {
269  valueSize = m_values[i]->boundingRect().height();
270 
271  size = this->mapAdaptorToScene(Point2DType(m_values[i]->boundingRect().width(), valueSize),
272  m_xAxis, m_yAxis);
273 
274  step = (int)(valueSize / valueSizeRatio) + 1;
275 
276  if( step > m_step )
277  {
278  m_step = step;
279  suggestResampling = true;
280  }
281 
282  coord = this->mapAdaptorToScene(Point2DType(textPosX, val), m_xAxis, m_yAxis);
283 
284  m_values[i]->setTransform( transform );
285 
286  m_values[i]->setPos(
287  coord.first + coeff * size.first * scaleX,
288  coord.second - (m_interval - size.second / 2) * scaleY );
289  }
290 
291  m_unit->setTransform( transform );
292 
293  val = viewportHeight * 0.8f;
294 
295  coord = this->mapAdaptorToScene(Point2DType(textPosX, val), m_xAxis, m_yAxis);
296 
297  coeff = (m_align == "left") ? 1 : -1.5;
298 
299  m_unit->setPos(
300  coord.first + coeff * 2 * size.first * scaleX,
301  coord.second + size.second * scaleY);
302  }
303  else // axis centered on top or bottom
304  {
305  const double valueSizeRatio = m_interval / scaleX;
306 
307  float coeff = 0.5f;
308 
309  const double textPosY = (m_align == "bottom")
310  ? viewport->getY()
311  : viewportHeight * 0.9;
312 
313  for(int i = 0; i < valuesSize; ++i, val += m_interval)
314  {
315  valueSize = m_values[i]->boundingRect().width();
316 
317  size = this->mapAdaptorToScene(Point2DType(valueSize, m_values[i]->boundingRect().height()),
318  m_xAxis, m_yAxis);
319 
320  step = (int)(valueSize / valueSizeRatio) + 1;
321 
322  if( step > m_step )
323  {
324  m_step = step;
325  suggestResampling = true;
326  }
327 
328  coord = this->mapAdaptorToScene(Point2DType(val, textPosY),
329  m_xAxis, m_yAxis);
330 
331  m_values[i]->setTransform( transform );
332 
333  m_values[i]->setPos(
334  coord.first - size.first / 2 * scaleX,
335  coord.second - coeff * size.second / 2 * scaleY );
336  }
337 
338  m_unit->setTransform( transform );
339 
340  val = viewportHeight * 0.8;
341 
342  size = this->mapAdaptorToScene(Point2DType(m_unit->boundingRect().width(), m_unit->boundingRect().height()),
343  m_xAxis, m_yAxis);
344 
345  coord = this->mapAdaptorToScene(Point2DType(viewportX + viewportWidth / 2, textPosY),
346  m_xAxis, m_yAxis);
347 
348  coeff = (m_align == "left") ? 1 : -1.5;
349 
350  m_unit->setPos(
351  coord.first - size.first * scaleX,
352  coord.second - 1.5 * size.second * scaleY);
353  }
354 
355  if( suggestResampling )
356  {
357  this->showHideScaleValues();
358  }
359  else if( !suggestResampling && step != m_step )
360  {
361  m_step = step;
362  this->showHideScaleValues();
363  }
364 }
365 
366 //---------------------------------------------------------------------------------------
367 
368 void SScaleValues::showHideScaleValues()
369 {
370  double value;
371  const int size = (int)m_values.size();
372  const double startVal = this->getStartVal();
373 
374  for(int i = 0; i < size; ++i)
375  {
376  value = i * m_interval + startVal; // compute the value at index 'i'
377 
378  // Display this value or not according to the current step between two consecutive values
379  // and in keeping the displaying of the value '0':
380  m_values[i]->setVisible( fmod(value, (m_step * m_interval)) == 0.0 );
381  }
382 }
383 
384 //---------------------------------------------------------------------------------------
385 
387 {
388  if( _event.getType() == ::fwRenderQt::data::Event::Resize)
389  {
390  this->updating();
391  }
392 }
393 
394 //---------------------------------------------------------------------------------------
395 
397 {
398  // Remove the layer (and therefore all its related items) from the scene
399  this->getScene2DRender()->getScene()->removeItem(m_layer);
400 }
401 
402 //----------------------------------------------------------------------------------------------------------
403 
405 {
406  KeyConnectionsMap connections;
407  connections.push( s_VIEWPORT_INPUT, ::fwRenderQt::data::Viewport::s_MODIFIED_SIG, s_UPDATE_SLOT );
408  return connections;
409 }
410 
411 } // namespace adaptor
412 
413 } // namespace scene2D
414 
Root class for all scene2d adaptors.
::fwRenderQt::data::Axis::sptr m_xAxis
The x Axis.
This class is a helper to define the connections of a service and its data.
Definition: IService.hpp:454
std::pair< float, float > Scene2DRatio
<width, height>
FWRENDERQT_API void initializeViewSize()
Initialize the source values used for computing view&#39;s size ratio.
SCENE2D_API void stopping() override
Clean the lines vector and remove the layer from the scene.
FWRENDERQT_API Point2DType mapAdaptorToScene(const Point2DType &_xy, const ::fwRenderQt::data::Axis::sptr &_xAxis, const ::fwRenderQt::data::Axis::sptr &_yAxis) const
FWRENDERQT_API std::shared_ptr< ::fwRenderQt::SRender > getScene2DRender() const
Get the render that manages the IAdaptor.
std::pair< double, double > Point2DType
Point2D coordinate <X, Y>
static FWRENDERQT_API void setPenColor(QPen &_pen, std::string _color)
Set a pen a color.
Definition: InitQtPen.cpp:18
This bundles contains data and services used to display a 2D Qt scene.
virtual SCENE2D_API ~SScaleValues() noexcept
Basic destructor, do nothing.
#define SLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:308
FWRENDERQT_API void initializeViewportSize()
Initialize the source values used for computing viewport&#39;s size ratio.
SCENE2D_API void updating() override
Do nothing.
::fwRenderQt::data::Axis::sptr m_yAxis
The y Axis.
float m_opacity
Opacity of the adaptor. Default value set to 1 (opaque).
SCENE2D_API void configuring() override
Configure the service before starting. Apply the configuration to service.
SCENE2D_API KeyConnectionsMap getAutoConnections() const override
Returns proposals to connect service slots to associated objects signals, this method is used for obj...
FWRENDERQT_API ViewportSizeRatio getViewportSizeRatio() const
Return the ratio between viewport&#39;s initial size and its current size.
SScaleValues adaptor. Display values and units on the axis.
FWRENDERQT_API void configureParams()
Parse the xml configuration for Axis, z value and opacity.
SCENE2D_API SScaleValues() noexcept
Constructor, set the x and y spacing to 10.
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_MODIFIED_SIG
Key in m_signals map of signal m_sigModified.
SCENE2D_API void starting() override
Initialize the layer, set the pen style to DashLine and call the draw() function. ...
SCENE2D_API void processInteraction(::fwRenderQt::data::Event &_event) override
Manage the given events.
This class manage events on the scene 2D (mouse event, keyboard event , ...).
Definition: Event.hpp:26
static FWSERVICES_APIconst::fwCom::Slots::SlotKeyType s_UPDATE_SLOT
Slot to call start method.
Definition: IService.hpp:177
FWSERVICES_API ConfigType getConfigTree() const
Return the configuration, in an boost property tree.
Definition: IService.cpp:247