Salome HOME
17063 [EDF] Selection by a circle in the OCC Viewer
[modules/gui.git] / src / Qtx / QtxRubberBand.cxx
1 // Copyright (C) 2007-2019  CEA/DEN, EDF R&D, 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:      QtxRubberBand.cxx
21 // Author:    Alexander A. BORODIN
22 //
23 #include "QtxRubberBand.h"
24
25 #include <QBitmap>
26 #include <QImage>
27 #include <QPaintEvent>
28 #include <QPainter>
29 #include <QPalette>
30 #include <QShowEvent>
31 #include <QVectorIterator>
32
33 #include <math.h>
34
35 /*!
36   \class QtxAbstractRubberBand
37   \brief Analog of class QRubberBand with possibility of creation non-rectangular contour for selection.
38   
39   Currently this class does not support Style functionality in full.
40 */
41
42 /*!
43   \brief Constructor
44   \param theParent parent widget
45  */
46
47 QtxAbstractRubberBand::QtxAbstractRubberBand( QWidget* theParent)
48   : QWidget( theParent/*,Qt::ToolTip*/ ),
49     myPoints(),
50     myIsClosed( false )
51 {
52   setAttribute(Qt::WA_TransparentForMouseEvents);
53 #ifndef WIN32
54   setAttribute(Qt::WA_NoSystemBackground);
55 #endif //WIN32
56   setAttribute(Qt::WA_WState_ExplicitShowHide);
57   setVisible(false);
58   theParent->installEventFilter(this);
59   setGeometry( QRect(QPoint(0,0), theParent->size() ) );
60 }
61
62 /*!
63   \brief Destructor
64  */
65 QtxAbstractRubberBand::~QtxAbstractRubberBand()
66 {
67 }
68
69 void QtxAbstractRubberBand::clearGeometry()
70 {
71   myPoints.clear();
72 }
73
74 bool QtxAbstractRubberBand::isClosed()
75 {
76   return myIsClosed;
77 }
78
79 void QtxAbstractRubberBand::paintEvent( QPaintEvent* theEvent )
80 {
81   if ( !myPoints.empty() )
82     {
83       QPixmap tiledPixmap(16, 16);
84      
85       QPainter pixmapPainter(&tiledPixmap);
86       pixmapPainter.setPen(Qt::NoPen);
87       pixmapPainter.setBrush(QBrush( Qt::black, Qt::Dense4Pattern ));
88       pixmapPainter.setBackground(QBrush( Qt::white ));
89       pixmapPainter.setBackgroundMode(Qt::OpaqueMode);
90       pixmapPainter.drawRect(0, 0, tiledPixmap.width(), tiledPixmap.height());
91       pixmapPainter.end();
92       // ### workaround for borked XRENDER
93       tiledPixmap = QPixmap::fromImage(tiledPixmap.toImage());
94
95
96       
97       QPainter aPainter( this );
98       aPainter.setRenderHint( QPainter::Antialiasing );
99       QRect r = myPoints.boundingRect();
100       //aPainter.setClipRegion( r.normalized().adjusted( -1, -1, 2, 2 ) );
101       aPainter.drawTiledPixmap( 0, 0, width(), height(), tiledPixmap);
102
103       aPainter.end();
104
105     /*
106
107
108
109 #ifdef WIN32
110       QPixmap anImage( size() );
111 #else
112       QImage anImage( size(), QImage::Format_ARGB32_Premultiplied );
113 #endif
114
115       anImage.fill( Qt::transparent );
116       QPainter aImgPainter( &anImage );
117       aImgPainter.setRenderHint( QPainter::Antialiasing );
118       aImgPainter.setCompositionMode(QPainter::CompositionMode_Source);
119
120       QPen aPen( Qt::black );
121       aPen.setWidth( 2 );
122       aImgPainter.setPen( aPen );
123     
124       aImgPainter.drawPolyline( myPoints );
125       if ( myIsClosed && myPoints.last() != myPoints.first() )
126         aImgPainter.drawLine( myPoints.last(), myPoints.first() );
127
128       //aImgPainter.setPen(Qt::NoPen);
129       //aImgPainter.setBrush(QBrush( Qt::white, Qt::Dense4Pattern));
130       //aImgPainter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
131       //aImgPainter.drawRect(0, 0, width(), height());
132       aImgPainter.end();
133
134       QPainter aPainter( this );
135       aPainter.drawPolyline( myPoints );
136       if ( myIsClosed && myPoints.last() != myPoints.first() )
137         aPainter.drawLine( myPoints.last(), myPoints.first() );
138       
139 #ifdef WIN32
140       aPainter.drawPixmap( 0, 0, anImage );
141 #else
142       aPainter.drawImage( 0, 0, anImage );
143       #endif
144       aPainter.end();*/
145       
146     }
147 }
148
149 void QtxAbstractRubberBand::showEvent( QShowEvent* theEvent )
150 {
151   raise();
152   theEvent->ignore();
153 }
154
155 void QtxAbstractRubberBand::moveEvent( QMoveEvent* )
156 {
157 }
158
159 void QtxAbstractRubberBand::resizeEvent( QResizeEvent* )
160 {
161 }
162
163 bool QtxAbstractRubberBand::eventFilter( QObject* obj, QEvent* e )
164 {
165   if ( obj && obj == parent() && e->type() == QEvent::Resize )
166     {
167       QWidget* p = (QWidget*)parent();
168       setGeometry( QRect(QPoint(0,0), p->size() ) );
169     }
170   return QWidget::eventFilter( obj, e );
171 }
172
173 QRegion createRegion( const QPointF& p1, const QPointF& p2 )
174 {
175   if ( p1 == p2 )
176     return QRegion();
177
178   QLineF n = QLineF( p1, p2 ).normalVector();//.unitVector();
179   n.setLength( 1 );
180   n.translate( p1 * -1 );
181   QPointF nPoint = n.p2();
182
183   QPolygonF p;
184   p << p1 + nPoint << p2 + nPoint << p2 - nPoint << p1 - nPoint << p1 + nPoint;
185
186   return QRegion( p.toPolygon() );
187 }
188
189 void QtxAbstractRubberBand::updateMask()
190 {
191   QRegion r;
192
193   QVectorIterator<QPoint> it(myPoints);
194   while( it.hasNext() )
195     {
196       QPoint p = it.next();
197       if( !it.hasNext() )
198         break;
199
200       QPoint np = it.peekNext();
201       
202       if ( p == np ) continue;
203
204       r += createRegion( p, np );
205     }
206
207   if ( isClosed() )
208     r += createRegion( myPoints.last(), myPoints.first() );
209
210   if ( !r.isEmpty() )
211     setMask( r );
212 }
213
214
215 QtxRectRubberBand::QtxRectRubberBand(QWidget* parent)
216   :QtxAbstractRubberBand( parent )      
217 {
218   myPoints.resize( 4 );
219   myIsClosed = true;
220 }
221
222 QtxRectRubberBand::~QtxRectRubberBand()
223 {
224 }
225
226 void QtxRectRubberBand::initGeometry( const QRect& theRect )
227 {
228   myPoints.clear();
229   myPoints << theRect.topLeft() << theRect.topRight() << theRect.bottomRight() << theRect.bottomLeft();
230   //setMask( QRegion( myPoints ) );
231   updateMask();
232 }
233
234 void QtxRectRubberBand::setStartPoint( const QPoint& thePoint )
235 {
236   myPoints[0] = thePoint;
237   myPoints[1].setY( thePoint.y() );
238   myPoints[3].setX( thePoint.x() );
239   updateMask();
240 }
241
242 void QtxRectRubberBand::setEndPoint( const QPoint& thePoint)
243 {
244   myPoints[2] = thePoint;       
245   myPoints[1].setX( thePoint.x() );
246   myPoints[3].setY( thePoint.y() );
247   updateMask();
248 }
249
250 void QtxRectRubberBand::clearGeometry()
251 {
252   QMutableVectorIterator<QPoint> i(myPoints);
253   while (i.hasNext())
254     {
255       i.next();
256       i.setValue( QPoint( -1, -1 ) );
257     }
258 }
259
260
261 QtxPolyRubberBand::QtxPolyRubberBand(QWidget* parent)
262   :QtxAbstractRubberBand( parent )
263 {
264 }
265
266 QtxPolyRubberBand::~QtxPolyRubberBand()
267 {
268 }
269
270 void QtxPolyRubberBand::initGeometry( const QPolygon& thePoints )
271 {
272   myPoints = thePoints;
273   updateMask();
274 }
275
276 void QtxPolyRubberBand::initGeometry( const QPoint& thePoint )
277 {
278   myPoints.clear();  
279   myPoints << thePoint;
280   updateMask();
281 }
282
283 void QtxPolyRubberBand::addNode( const QPoint& thePoint )
284 {
285   myPoints << thePoint;
286   updateMask();
287 }
288
289 void QtxPolyRubberBand::replaceLastNode( const QPoint& thePoint )
290 {
291   if ( !myPoints.empty() )
292     {
293       myPoints.pop_back();
294       myPoints << thePoint;
295       updateMask();
296     }
297 }
298
299 void QtxPolyRubberBand::removeLastNode()
300 {
301   if ( !myPoints.empty() )
302     {
303       myPoints.pop_back();
304       updateMask();
305     }
306 }
307
308 void QtxPolyRubberBand::setClosed( bool theFlag )
309 {
310   if (myIsClosed != theFlag )
311     {
312       myIsClosed = theFlag;
313       updateMask();
314     }
315 }
316
317 QtxCircleRubberBand::QtxCircleRubberBand(QWidget* parent)
318   :QtxAbstractRubberBand(parent), myHasCenter(false)
319 {
320   myPoints.resize(2);
321   myIsClosed = true;
322 }
323
324 QtxCircleRubberBand::~QtxCircleRubberBand()
325 {
326 }
327
328 void QtxCircleRubberBand::initGeometry(const QPoint& thePoint)
329 {
330   myIsClosed = false;
331   myHasCenter = true;
332   myPoints.clear();
333   myPoints << thePoint;
334   updateMask();
335 }
336
337 void QtxCircleRubberBand::setRadius(const QPoint& thePoint)
338 {
339   if (myPoints.size() == 1)
340     myPoints << thePoint;
341   else
342     myPoints.setPoint(1, thePoint);
343   myIsClosed = true;
344   updateMask();
345 }
346
347 void QtxCircleRubberBand::updateMask()
348 {
349   int aLen = radius();
350   if (aLen > MIN_RADIUS) {
351     QRegion aReg1(myPoints[0].x() - aLen,
352       myPoints[0].y() - aLen, aLen * 2, aLen * 2, QRegion::Ellipse);
353     QRegion aReg2(myPoints[0].x() - aLen + 2,
354       myPoints[0].y() - aLen + 2, aLen * 2 - 4, aLen * 2 - 4, QRegion::Ellipse);
355     setMask(aReg1 - aReg2);
356   }
357 }
358
359 bool QtxCircleRubberBand::isCenterDefined() const
360 {
361   return myHasCenter;
362 }
363
364 void QtxCircleRubberBand::clearGeometry()
365 {
366   QtxAbstractRubberBand::clearGeometry();
367   myHasCenter = false;
368   myIsClosed = false;
369 }
370
371 QPoint rotatePoint(const QPoint& theStart, const QPoint& theCenter, double theAngle)
372 {
373   double cosTheta = cos(theAngle);
374   double sinTheta = sin(theAngle);
375   int aX = (int)(cosTheta * (theStart.x() - theCenter.x()) -
376     sinTheta * (theStart.y() - theCenter.y()) + theCenter.x());
377   int aY = (int)(sinTheta * (theStart.x() - theCenter.x()) +
378     cosTheta * (theStart.y() - theCenter.y()) + theCenter.y());
379   return QPoint(aX, aY);
380 }
381
382 static double m_pi = 4 * atan(1);
383 static double angle_deg = 360. / CIRCLE_NB_POINTS;
384 static double angle_rad = angle_deg * (m_pi / 180.);
385
386
387 void QtxCircleRubberBand::getPoligon(QPolygon* thePoints) const
388 {
389   int aLen = radius();
390   if (aLen > MIN_RADIUS) {
391     thePoints->clear();
392     QPoint aCenter = myPoints[0];
393     QPoint aStart = myPoints[1];
394     for (int i = 0; i < CIRCLE_NB_POINTS; i++) {
395       thePoints->append(aStart);
396       aStart = rotatePoint(aStart, aCenter, angle_rad);
397     }
398   }
399 }
400
401 int QtxCircleRubberBand::radius() const
402 {
403   if (myPoints.size() < 2)
404     return -1;
405   QPoint aDist = myPoints[1] - myPoints[0];
406   return (int)std::sqrt(std::pow(aDist.x(), 2) + std::pow(aDist.y(), 2));
407 }