Salome HOME
updated copyright message
[modules/gui.git] / src / Plot2d / Plot2d.cxx
1 // Copyright (C) 2007-2023  CEA, EDF, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 // File   : Plot2d.cxx
21 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
22 //
23 #include "Plot2d.h"
24
25 #include <QPainter>
26
27 #include <qwt_plot.h>
28 #include <qwt_plot_curve.h>
29
30 const int MSIZE  = 9;
31 const int MAX_ATTEMPTS        = 10;     // max attempts
32
33 // color tolerance (used to compare color values)
34 const long COLOR_DISTANCE = 100;
35
36
37
38 /*!
39   Constructor
40 */
41 Plot2d_Point::Plot2d_Point()
42   : x( 0. ), y( 0. ), deviationPtr(0)
43 {
44 }
45
46 /*!
47   Constructor
48 */
49 Plot2d_Point::Plot2d_Point( double theX, double theY, const QString& theText )
50   : x( theX ), y( theY ), deviationPtr(0), text( theText )
51 {
52 }
53
54 /*!
55   Destructor.
56 */
57 Plot2d_Point::~Plot2d_Point() {
58   clearDeviation();
59 }
60
61 /*!
62   Free memory allocated for the deviation data.
63 */
64 void Plot2d_Point::clearDeviation() {
65   if(deviationPtr)
66     delete deviationPtr;
67   deviationPtr = 0;
68 }
69
70 /*!
71   Return true in case if deviation data is assigned to the point.
72 */
73 bool Plot2d_Point::hasDeviation() const {
74   return !(deviationPtr == 0);
75 }
76
77 /*!
78   Assign deviation data to the point.
79 */
80 void Plot2d_Point::setDeviation(double min, double max) {
81  clearDeviation();
82  deviationPtr = new double[2];
83  deviationPtr[0] = min;deviationPtr[1] = max;
84 }
85
86 /*!
87   Return true in case if deviation data is assigned to the point
88   and store deviation data in the input parameters.
89 */
90 bool Plot2d_Point::deviation(double& min, double& max) const {
91   if(hasDeviation()) {
92     min = deviationPtr[0];
93     max = deviationPtr[1];
94   }
95   return false;
96 }
97
98 /*!
99   Return minimal deviation value.
100 */
101 bool Plot2d_Point::minDeviation(double& min) const {
102   if(hasDeviation()) {
103     min = deviationPtr[0];
104     return true;
105   } else {
106     min = 0;
107   }
108   return false;
109 }
110
111 /*!
112   Return minimal deviation value.
113 */
114 bool Plot2d_Point::maxDeviation(double& max) const {
115   if(hasDeviation()) {
116     max = deviationPtr[1];
117     return true;
118   } else {
119     max = 0;
120   }
121   return false;
122 }
123
124
125
126 /*!
127   \brief Convert Plot2d marker type to Qwt marker type.
128   \param m Plot2d marker type
129   \return Qwt marker type
130 */
131 QwtSymbol::Style Plot2d::plot2qwtMarker( Plot2d::MarkerType m )
132 {
133   QwtSymbol::Style ms = QwtSymbol::NoSymbol;  
134   switch ( m ) {
135   case Plot2d::Circle:
136     ms = QwtSymbol::Ellipse;   break;
137   case Plot2d::Rectangle:
138     ms = QwtSymbol::Rect;      break;
139   case Plot2d::Diamond:
140     ms = QwtSymbol::Diamond;   break;
141   case Plot2d::DTriangle:
142     ms = QwtSymbol::DTriangle; break;
143   case Plot2d::UTriangle:
144     ms = QwtSymbol::UTriangle; break;
145   case Plot2d::LTriangle:
146     ms = QwtSymbol::LTriangle; break;
147   case Plot2d::RTriangle:
148     ms = QwtSymbol::RTriangle; break;
149   case Plot2d::Cross:
150     ms = QwtSymbol::Cross;     break;
151   case Plot2d::XCross:
152     ms = QwtSymbol::XCross;    break;
153   case Plot2d::None:
154   default:
155     ms = QwtSymbol::NoSymbol;  break;
156   }
157   return ms;
158 }
159
160 /*!
161   \brief Convert Qwt marker type to Plot2d marker type.
162   \param m Qwt marker type
163   \return Plot2d marker type
164 */
165 Plot2d::MarkerType Plot2d::qwt2plotMarker( QwtSymbol::Style m )
166 {
167   Plot2d::MarkerType ms = Plot2d::None;  
168   switch ( m ) {
169   case QwtSymbol::Ellipse:
170     ms = Plot2d::Circle;    break;
171   case QwtSymbol::Rect:
172     ms = Plot2d::Rectangle; break;
173   case QwtSymbol::Diamond:
174     ms = Plot2d::Diamond;   break;
175   case QwtSymbol::DTriangle:
176     ms = Plot2d::DTriangle; break;
177   case QwtSymbol::UTriangle:
178     ms = Plot2d::UTriangle; break;
179   case QwtSymbol::RTriangle:
180     ms = Plot2d::RTriangle; break;
181   case QwtSymbol::LTriangle:
182     ms = Plot2d::LTriangle; break;
183   case QwtSymbol::Cross:
184     ms = Plot2d::Cross;     break;
185   case QwtSymbol::XCross:
186     ms = Plot2d::XCross;    break;
187   case QwtSymbol::NoSymbol:
188   default:
189     ms = Plot2d::None;      break;
190   }
191   return ms;
192 }
193
194 /*!
195   \brief Convert Plot2d line type to Qt/Qwt line type.
196   \param p Plot2d line type
197   \return Qt/Qwt line type
198 */
199 Qt::PenStyle Plot2d::plot2qwtLine( Plot2d::LineType p )
200 {
201   Qt::PenStyle ps = Qt::NoPen;
202   switch ( p ) {
203   case Plot2d::Solid:
204     ps = Qt::SolidLine;      break;
205   case Plot2d::Dash:
206     ps = Qt::DashLine;       break;
207   case Plot2d::Dot:
208     ps = Qt::DotLine;        break;
209   case Plot2d::DashDot:
210     ps = Qt::DashDotLine;    break;
211   case Plot2d::DashDotDot:
212     ps = Qt::DashDotDotLine; break;
213   case Plot2d::NoPen:
214   default:
215     ps = Qt::NoPen;          break;
216   }
217   return ps;
218 }
219
220 /*!
221   \brief Convert Qt/Qwt line type to Plot2d line type.
222   \param p Qt/Qwt line type
223   \return Plot2d line type
224 */
225 Plot2d::LineType Plot2d::qwt2plotLine( Qt::PenStyle p )
226 {
227   Plot2d::LineType ps = Plot2d::NoPen;
228   switch ( p ) {
229   case Qt::SolidLine:
230     ps = Plot2d::Solid;      break;
231   case Qt::DashLine:
232     ps = Plot2d::Dash;       break;
233   case Qt::DotLine:
234     ps = Plot2d::Dot;        break;
235   case Qt::DashDotLine:
236     ps = Plot2d::DashDot;    break;
237   case Qt::DashDotDotLine:
238     ps = Plot2d::DashDotDot; break;
239   case Qt::NoPen:
240   default:
241     ps = Plot2d::NoPen;      break;
242   }
243   return ps;
244 }
245
246 /*!
247   \brief Draw line.
248   \param painter painter
249   \param p1 starting point
250   \param p2 ending point
251   \param type line type
252   \param color line color
253   \param width line width
254 */
255 void Plot2d::drawLine( QPainter* painter, const QPoint& p1, const QPoint& p2, 
256                        Qt::PenStyle type, const QColor& color, int width )
257 {
258   painter->save();
259   QPen pen( type );
260   pen.setColor( color );
261   pen.setWidth( width );
262   painter->setPen( pen );
263   painter->drawLine( p1, p2 );
264   painter->restore();
265 }
266
267 /*!
268   \brief Draw line.
269   \param painter painter
270   \param p1 starting point
271   \param p2 ending point
272   \param type line type
273   \param color line color
274   \param width line width
275 */
276 void Plot2d::drawLine( QPainter* painter, const QPoint& p1, const QPoint& p2, 
277                        Plot2d::LineType type, const QColor& color, int width )
278 {
279   drawLine( painter, p1, p2, plot2qwtLine( type ), color, width );
280 }
281
282 /*!
283   \brief Draw line.
284   \param painter painter
285   \param x1 X coordinate of the starting point
286   \param y1 Y coordinate of the starting point
287   \param x2 X coordinate of the ending point
288   \param y2 Y coordinate of the ending point
289   \param type line type
290   \param color line color
291   \param width line width
292 */
293 void Plot2d::drawLine( QPainter* painter, int x1, int y1, int x2, int y2,
294                        Qt::PenStyle type, const QColor& color, int width )
295 {
296   drawLine( painter, QPoint( x1, y1 ), QPoint( x2, y2 ), type, color, width );
297 }
298
299 /*!
300   \brief Draw line.
301   \param painter painter
302   \param x1 X coordinate of the starting point
303   \param y1 Y coordinate of the starting point
304   \param x2 X coordinate of the ending point
305   \param y2 Y coordinate of the ending point
306   \param type line type
307   \param color line color
308   \param width line width
309 */
310 void Plot2d::drawLine( QPainter* painter, int x1, int y1, int x2, int y2,
311                        Plot2d::LineType type, const QColor& color, int width )
312 {
313   drawLine( painter, QPoint( x1, y1 ), QPoint( x2, y2 ), 
314             plot2qwtLine( type), color, width );
315 }
316
317 /*!
318   \brief Draw marker.
319   \param painter painter
320   \param p central point
321   \param r marker rectangle
322   \param type marker type
323   \param color marker color
324 */
325 void Plot2d::drawMarker( QPainter* painter, const QPoint& p, const QRect& r,
326                          QwtSymbol::Style type, const QColor& color )
327 {
328   painter->save();
329   painter->setPen( color );
330   painter->setBrush( color );
331   
332   QRect ar = r;
333   ar.moveCenter( p );
334   const int w2 = ar.width()  / 2;
335   const int h2 = ar.height() / 2;
336
337   switch( type ) {
338   case QwtSymbol::Ellipse:
339     painter->drawEllipse( ar );
340     break;
341   case QwtSymbol::Rect:
342     painter->drawRect( ar );
343     painter->fillRect( ar, QBrush( color ) );
344     break;
345   case QwtSymbol::Diamond:
346     {
347       QPolygon polygon;
348       polygon << QPoint( ar.x() + w2, ar.y() );
349       polygon << QPoint( ar.right(), ar.y() + h2 );
350       polygon << QPoint( ar.x() + w2, ar.bottom() );
351       polygon << QPoint( ar.x(), ar.y() + h2 );
352       painter->drawPolygon( polygon );
353       break;
354     }
355   case QwtSymbol::Cross:
356     painter->drawLine( ar.left() + w2, ar.top(), ar.left() + w2, ar.bottom() );
357     painter->drawLine( ar.left(), ar.top() + h2, ar.right(), ar.top() + h2 );
358     break;
359   case QwtSymbol::XCross:
360     painter->drawLine( ar.left(), ar.top(), ar.right(), ar.bottom() );
361     painter->drawLine( ar.left(), ar.bottom(), ar.right(), ar.top() );
362     break;
363   case QwtSymbol::UTriangle:
364     {
365       QPolygon polygon;
366       polygon << QPoint( ar.left() + w2, ar.top() );
367       polygon << QPoint( ar.right(), ar.bottom() );
368       polygon << QPoint( ar.left(), ar.bottom() );
369       painter->drawPolygon( polygon );
370       break;
371     }
372   case QwtSymbol::DTriangle:
373     {
374       QPolygon polygon;
375       polygon << QPoint( ar.left() + w2, ar.bottom() );
376       polygon << QPoint( ar.right(), ar.top() );
377       polygon << QPoint( ar.left(), ar.top() );
378       painter->drawPolygon( polygon );
379       break;
380     }
381   case QwtSymbol::RTriangle:
382     {
383       QPolygon polygon;
384       polygon << QPoint( ar.left(), ar.top() );
385       polygon << QPoint( ar.right(), ar.top() + h2 );
386       polygon << QPoint( ar.left(), ar.bottom() );
387       painter->drawPolygon( polygon );
388       break;
389     }
390   case QwtSymbol::LTriangle:
391     {
392       QPolygon polygon;
393       polygon << QPoint( ar.left(), ar.top() + h2 );
394       polygon << QPoint( ar.right(), ar.top() );
395       polygon << QPoint( ar.right(), ar.bottom() );
396       painter->drawPolygon( polygon );
397       break;
398     }
399   default:
400     break;
401   }
402   painter->restore();
403 }
404
405 /*!
406   \brief Draw marker.
407   \param painter painter
408   \param p central point
409   \param r marker rectangle
410   \param type marker type
411   \param color marker color
412 */
413 void Plot2d::drawMarker( QPainter* painter, const QPoint& p, const QRect& r,
414                          Plot2d::MarkerType type, const QColor& color )
415 {
416   drawMarker( painter, p, r, plot2qwtMarker( type ), color ); 
417 }
418
419 /*!
420   \brief Draw marker.
421   \param painter painter
422   \param x X coordinate of the central point
423   \param y Y coordinate of the central point
424   \param w marker rectangle width
425   \param h marker rectangle height
426   \param type marker type
427   \param color marker color
428 */
429 void Plot2d::drawMarker( QPainter* painter, int x, int y, int w, int h,
430                          QwtSymbol::Style type, const QColor& color )
431 {
432   drawMarker( painter, QPoint( x, y ), QRect( 0, 0, w, h ), type, color ); 
433 }
434
435 /*!
436   \brief Draw marker.
437   \param painter painter
438   \param x X coordinate of the central point
439   \param y Y coordinate of the central point
440   \param w marker rectangle width
441   \param h marker rectangle height
442   \param type marker type
443   \param color marker color
444 */
445 void Plot2d::drawMarker( QPainter* painter, int x, int y, int w, int h,
446                          Plot2d::MarkerType type, const QColor& color )
447 {
448   drawMarker( painter, QPoint( x, y ), QRect( 0, 0, w, h ), plot2qwtMarker( type ), color ); 
449 }
450
451
452 /*!
453   \brief Create icon pixmap according to the marker type.
454   \param size icon size
455   \param type marker type
456   \param color icon color
457   \return icon
458 */
459 QPixmap Plot2d::markerIcon(const QSize &size, const QColor& color, Plot2d::MarkerType type )
460 {
461
462   QPixmap px( size );
463   px.fill( QColor( 255, 255, 255, 0 ) );
464   QPainter p( &px );
465   Plot2d::drawMarker( &p, size.width()/2, size.height()/2, MSIZE, MSIZE, type, color );
466   return px;
467 }
468
469
470 /*!
471   \brief Create icon pixmap according to the line type.
472   \param size icon size
473   \param type line type
474   \param color icon color
475   \return icon
476 */
477 QPixmap Plot2d::lineIcon( const QSize& size,  const QColor& color, Plot2d::LineType type )
478 {
479
480   QPixmap px( size );
481   px.fill( QColor( 255, 255, 255, 0 ) );
482   QPainter p( &px );
483   drawLine( &p, 5, size.height()/2, size.width()-5, size.height()/2, type,
484             color, 1 );
485   return px;
486 }
487
488 /*!
489   Gets new unique marker for item if possible
490 */
491 void Plot2d::getNextMarker( const int rtti, const QwtPlot* thePlot, QwtSymbol::Style& typeMarker,
492                             QColor& color, Qt::PenStyle& typeLine ) 
493 {
494   bool bOk = false;
495   int cnt = 0;
496   while ( !bOk ) {
497     int aRed    = (int)( 256.0 * rand() / RAND_MAX );  // generate random color
498     int aGreen  = (int)( 256.0 * rand() / RAND_MAX );  // ...
499     int aBlue   = (int)( 256.0 * rand() / RAND_MAX );  // ...
500     int aMarker = (int)( 9.0 * rand() / RAND_MAX ) + 1;// 9 markers types( not including empty )
501     int aLine   = (int)( 5.0 * rand() / RAND_MAX ) + 1;// 5 line types ( not including empty )
502     
503     typeMarker = ( QwtSymbol::Style )aMarker;
504     color      = QColor( aRed, aGreen, aBlue );
505     typeLine   = ( Qt::PenStyle )aLine;
506     
507     bOk = ( ++cnt == MAX_ATTEMPTS ) || !existMarker( rtti, thePlot, typeMarker, color, typeLine );
508   }
509 }
510
511 /*!
512   Checks if marker belongs to any enitity
513 */
514 bool Plot2d::existMarker( const int rtti, const QwtPlot* thePlot, const QwtSymbol::Style typeMarker,
515                           const QColor& color, const Qt::PenStyle typeLine ) 
516 {
517   bool ok = false;
518   
519   QColor bgColor = thePlot->palette().color( QPalette::Background );
520   if ( closeColors( color, bgColor ) ) {
521     ok = true;
522   }
523   else {
524     QwtPlotItemList anItems = thePlot->itemList();
525     QwtPlotItemIterator anIt = anItems.begin(), aLast = anItems.end();
526     QwtPlotItem* anItem;
527     for ( ; anIt != aLast && !ok; anIt++ ) {
528       anItem = *anIt;
529       if ( anItem && anItem->rtti() == rtti ) {
530         QwtPlotCurve* crv = dynamic_cast<QwtPlotCurve*>( anItem );
531         if ( crv ) {
532           QwtSymbol::Style aStyle = crv->symbol()->style();
533           QColor           aColor = crv->pen().color();
534           Qt::PenStyle     aLine  = crv->pen().style();
535           ok = closeColors( aColor, color ) && aStyle == typeMarker && aLine == typeLine;
536         }
537       }
538     }
539   }
540   return ok;
541 }
542
543 /*!
544   Checks if two colors are close to each other
545   uses COLOR_DISTANCE variable as max tolerance for comparing of colors
546 */
547
548 bool Plot2d::closeColors( const QColor& color1,
549                           const QColor& color2,
550                           int distance )
551 {
552   long tol = 
553     qAbs( color2.red()   - color1.red()   ) + 
554     qAbs( color2.green() - color1.green() ) +
555     qAbs( color2.blue()  - color1.blue()  ) -
556     ( distance < 0 ? COLOR_DISTANCE : distance );
557
558   return tol <= 0;
559 }