Salome HOME
updated copyright message
[modules/gui.git] / src / OCCViewer / OCCViewer_ViewSketcher.cxx
1 // Copyright (C) 2007-2023  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 #include "OCCViewer_ViewSketcher.h"
21 #include "OCCViewer_ViewWindow.h"
22 #include "OCCViewer_ViewPort3d.h"
23
24 #include "QtxRubberBand.h"
25
26 #include <QApplication>
27 #include <QPainter>
28 #include <QPolygon>
29 #include <QMouseEvent>
30 #include <QKeyEvent>
31
32 /****************************************************************
33 **  Class: OCCViewer_ViewSketcher
34 **  Level: Public
35 *****************************************************************/
36
37 OCCViewer_ViewSketcher::OCCViewer_ViewSketcher( OCCViewer_ViewWindow* vw, int type )
38 : QObject( vw ),
39 mySketchButton( Qt::LeftButton ),
40 mypViewWindow( vw ),
41 myType( type ),
42 mypData( 0 ),
43 myResult( Neutral ),
44 myButtonState( 0 ),
45 myHasShift( false )
46 {
47 }
48
49 OCCViewer_ViewSketcher::~OCCViewer_ViewSketcher()
50 {
51 }
52
53 void OCCViewer_ViewSketcher::activate()
54 {
55   OCCViewer_ViewPort3d* avp = mypViewWindow->getViewPort();
56
57   mySavedCursor = avp->cursor();
58   avp->setCursor( Qt::PointingHandCursor );
59   avp->installEventFilter( this );
60   qApp->installEventFilter( this );
61
62   connect( avp, SIGNAL( vpDrawExternal( QPainter* ) ), this, SLOT( onDrawViewPort() ) );
63
64   myStart = QPoint();
65   myResult = Neutral;
66
67   onActivate();
68 }
69
70 void OCCViewer_ViewSketcher::deactivate()
71 {
72   OCCViewer_ViewPort3d* avp = mypViewWindow->getViewPort();
73
74   disconnect( avp, SIGNAL( vpDrawExternal( QPainter* ) ), this, SLOT( onDrawViewPort() ) );
75
76   qApp->removeEventFilter( this );
77   avp->removeEventFilter( this );
78   avp->setCursor( mySavedCursor );
79
80   onDeactivate();
81 }
82
83 int OCCViewer_ViewSketcher::type() const
84 {
85   return myType;
86 }
87
88 void* OCCViewer_ViewSketcher::data() const
89 {
90   return mypData;
91 }
92
93 int OCCViewer_ViewSketcher::result() const
94 {
95   return myResult;
96 }
97
98 int OCCViewer_ViewSketcher::buttonState() const
99 {
100   return myButtonState;
101 }
102
103 bool OCCViewer_ViewSketcher::isHasShift() const
104 {
105   return myHasShift;
106 }
107
108 void OCCViewer_ViewSketcher::onActivate()
109 {
110 }
111
112 void OCCViewer_ViewSketcher::onDeactivate()
113 {
114 }
115
116 bool OCCViewer_ViewSketcher::isDefault() const
117 {
118   return true;
119 }
120
121 bool OCCViewer_ViewSketcher::eventFilter( QObject* o, QEvent* e )
122 {
123   OCCViewer_ViewPort3d* avp = mypViewWindow->getViewPort();
124
125   QMouseEvent* me = (QMouseEvent*)e;
126   SketchState state = EnTrain;
127   bool ignore = false;
128   if ( o == avp )
129   {
130     switch ( e->type() )
131     {
132       case QEvent::MouseMove:
133       case QEvent::MouseButtonPress:
134       case QEvent::MouseButtonRelease:
135       case QEvent::MouseButtonDblClick:
136       {
137
138         myButtonState = me->buttons();
139         if ( e->type() == QEvent::MouseButtonPress )
140           myButtonState |= me->button();
141
142         if ( myStart.isNull() && ( myButtonState & sketchButton() ) )
143         {
144           state = Debut;
145           myStart = me->pos();
146         }
147
148         myCurr = me->pos();
149
150         onMouse( me );
151
152         if ( myResult != Neutral )
153           state = Fin;
154
155         ignore = true;
156         myHasShift = ( me->modifiers() & Qt::ShiftModifier );
157         break;
158       }
159       case QEvent::Hide:
160       case QEvent::HideToParent:
161         myResult = Reject;
162         onSketch( Fin );
163         break;
164       default:
165         break;
166     }
167   }
168   if ( e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease )
169   {
170     ignore = onKey( (QKeyEvent*)e );
171     if ( myResult != Neutral )
172       state = Fin;
173   }
174
175   if ( ignore )
176   {
177     onSketch( state );
178     return true;
179   }
180   return QObject::eventFilter( o, e );
181 }
182
183 void OCCViewer_ViewSketcher::onDrawViewPort()
184 {
185   onSketch( Debut );
186 }
187
188 bool OCCViewer_ViewSketcher::onKey( QKeyEvent* )
189 {
190   return false;
191 }
192
193 void OCCViewer_ViewSketcher::onMouse( QMouseEvent* )
194 {
195 }
196
197 int OCCViewer_ViewSketcher::sketchButton()
198 {
199   return mySketchButton;
200 }
201
202 void OCCViewer_ViewSketcher::setSketchButton( int b )
203 {
204   mySketchButton = b;
205 }
206
207 /****************************************************************
208 **  Class: OCCViewer_RectSketcher
209 **  Level: Public
210 *****************************************************************/
211
212 OCCViewer_RectSketcher::OCCViewer_RectSketcher( OCCViewer_ViewWindow* vw, int typ )
213   : OCCViewer_ViewSketcher( vw, typ )
214 {
215   if ( vw )
216     {
217       OCCViewer_ViewPort3d* avp = mypViewWindow->getViewPort();
218       mypRectRB = new QtxRectRubberBand( avp );
219     }
220 }
221
222 OCCViewer_RectSketcher::~OCCViewer_RectSketcher()
223 {
224   delete (QRect*)mypData;
225 }
226
227 void OCCViewer_RectSketcher::onActivate()
228 {
229   mypData = new QRect();
230 }
231
232 void OCCViewer_RectSketcher::onDeactivate()
233 {
234   delete (QRect*)mypData;
235   mypData = 0;
236   mypRectRB->clearGeometry();
237   mypRectRB->hide();
238 }
239
240 bool OCCViewer_RectSketcher::onKey( QKeyEvent* e )
241 {
242   if ( e->key() == Qt::Key_Escape )
243     myResult = Reject;
244   else if ( e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return )
245     myResult = Accept;
246
247   return true;
248 }
249
250 void OCCViewer_RectSketcher::onMouse( QMouseEvent* e )
251 {
252   OCCViewer_ViewPort3d* avp = mypViewWindow->getViewPort();
253
254   if ( avp->rect().contains( myCurr ) )
255     avp->setCursor( Qt::PointingHandCursor );
256   else
257     avp->setCursor( Qt::ForbiddenCursor );
258
259   if ( e->type() == QEvent::MouseButtonRelease && (int)e->button() == sketchButton() ) // todo Qt::MouseButton is unsigned int: comparison of int with uint
260   {
261     myResult = Accept;
262     QApplication::postEvent( avp, new QMouseEvent( e->type(), e->pos(),
263                                                    e->globalPos(), e->button(), 
264                                                    e->buttons(), e->modifiers() ) );
265   }
266 }
267
268 void OCCViewer_RectSketcher::onSketch( SketchState state )
269 {
270   //OCCViewer_ViewPort3d* avp = mypViewWindow->getViewPort();
271
272   if ( mypRectRB )
273     {      
274       QRect* sketchRect = (QRect*)data();
275       if ( myButtonState & sketchButton() )
276         {   
277           QRect rect = QRect( myStart, myCurr ).normalized();
278           /*QRect rect( qMin( myStart.x(), myCurr.x() ), qMin( myStart.y(), myCurr.y() ),
279                       qAbs( myStart.x() - myCurr.x() ), qAbs( myStart.y() - myCurr.y() ) );
280           QPainter p( avp );
281           p.setPen( Qt::white );
282           p.setCompositionMode( QPainter::CompositionMode_Xor );
283           */
284           
285           //if ( state != Debut && !sketchRect->isEmpty() )
286           //  p.drawRect( *sketchRect );
287
288           *sketchRect = rect;
289           if ( !rect.isEmpty() && state != Fin )
290             {
291               //p.drawRect( *sketchRect );            
292               mypRectRB->initGeometry( rect );
293               mypRectRB->show();
294             }          
295           else
296             mypRectRB->hide();
297         }
298     }
299
300   if ( state == Fin )
301   {
302     mypViewWindow->activateSketching( OCCViewer_ViewWindow::NoSketching );
303   }
304 }
305
306 /****************************************************************
307 **  Class: OCCViewer_PolygonSketcher
308 **  Level: Public
309 *****************************************************************/
310
311 OCCViewer_PolygonSketcher::OCCViewer_PolygonSketcher( OCCViewer_ViewWindow* vw, int typ )
312 : OCCViewer_ViewSketcher( vw, typ ),
313   myDbl           ( false ),
314   myToler         ( 5, 5 ),
315   //mypPoints        ( 0L ),
316   myAddButton     ( 0 ),
317   myDelButton     ( 0 ),
318   myMode          ( Poligone )
319 {
320   mySketchButton = Qt::LeftButton;
321   if ( vw )
322   {
323     OCCViewer_ViewPort3d* avp = mypViewWindow->getViewPort();
324     mypPolyRB = new QtxPolyRubberBand( avp );
325     mypCircleRB = new QtxCircleRubberBand( avp );
326   }
327   mypData = new QPolygon( 0 );
328 }
329
330 OCCViewer_PolygonSketcher::~OCCViewer_PolygonSketcher()
331 {
332   //delete mypPoints;
333   delete (QPolygon*)mypData;
334 }
335
336 void OCCViewer_PolygonSketcher::onActivate()
337 {
338   myDbl = false;
339   //mypPoints = new QPolygon( 0 );
340
341   switch ( sketchButton() )
342   {
343   case Qt::LeftButton:
344     myAddButton = Qt::RightButton;
345     myDelButton = Qt::MidButton;
346     break;
347   case Qt::MidButton:
348     myAddButton = Qt::LeftButton;
349     myDelButton = Qt::RightButton;
350     break;
351   case Qt::RightButton:
352   default:
353     myAddButton = Qt::LeftButton;
354     myDelButton = Qt::MidButton;
355     break;
356   };
357 }
358
359 void OCCViewer_PolygonSketcher::onDeactivate()
360 {
361   if (mypPolyRB) {
362     mypPolyRB->clearGeometry();
363     mypPolyRB->hide();
364   }
365   if (mypCircleRB) {
366     mypCircleRB->clearGeometry();
367     mypCircleRB->hide();
368   }
369   ((QPolygon*)mypData)->clear();
370 }
371
372 bool OCCViewer_PolygonSketcher::onKey(QKeyEvent* e)
373 {
374   int aKey = e->key();
375   if (aKey == Qt::Key_Escape)
376   {
377     myResult = Reject;
378     return true;
379   }
380   else if (aKey == Qt::Key_Enter || aKey == Qt::Key_Return)
381   {
382     QPolygon* points = (QPolygon*)data();
383     if (points->count())
384     {
385       QPoint last = points->point(points->count() - 1);
386       if (last != myCurr)
387       {
388         points->resize(points->count() + 1);
389         points->setPoint(points->count() - 1, myCurr);
390       }
391     }
392     myResult = Accept;
393     return true;
394   }
395   else if (aKey == Qt::Key_Backspace && e->type() == QEvent::KeyRelease)
396   {
397     QPolygon* points = (QPolygon*)data();
398     if (points->count() > 1)
399       points->resize(points->count() - 1);
400     onMouse(0);
401     return true;
402   }
403   else if (aKey == Qt::Key_Space && e->type() == QEvent::KeyRelease) 
404   {
405     OCCViewer_ViewPort3d* avp = mypViewWindow->getViewPort();
406     bool closed = false;
407     QPolygon* points = (QPolygon*)data();
408     bool valid = avp->rect().contains(myCurr);
409     if (!myStart.isNull())
410     {
411       QRect aRect(myStart.x() - myToler.width(), myStart.y() - myToler.height(),
412         2 * myToler.width(), 2 * myToler.height());
413       closed = aRect.contains(myCurr);
414     }
415     valid = valid && isValid(points, myCurr);
416     if (closed && !valid)
417       closed = false;
418     if (closed)
419       myResult = Accept;
420     else
421     {
422       if (myStart.isNull())
423         myStart = myCurr;
424       else
425       {
426         QPoint last = points->point(points->count() - 1);
427         if (last != myCurr && valid)
428         {
429           points->resize(points->count() + 1);
430           points->setPoint(points->count() - 1, myCurr);
431         }
432         if (valid && myDbl)
433           myResult = Accept;
434       }
435     }
436   }
437
438   return true;
439 }
440
441 void OCCViewer_PolygonSketcher::onMouse( QMouseEvent* e )
442 {
443   OCCViewer_ViewPort3d* avp = mypViewWindow->getViewPort();
444
445   QPolygon* points = (QPolygon*)data();
446   if ( !points->count() && !myStart.isNull() )
447   {
448     points->resize( points->count() + 1 );
449     points->setPoint( points->count() - 1, myStart );
450   }
451
452   bool closed = false;
453   bool valid = avp->rect().contains( myCurr );
454   if ( !myStart.isNull() )
455   {
456     QRect aRect( myStart.x() - myToler.width(), myStart.y() - myToler.height(),
457                  2 * myToler.width(), 2 * myToler.height() );
458     closed = aRect.contains( myCurr );
459   }
460   valid = valid && isValid( points, myCurr );
461   if ( closed && !valid )
462     closed = false;
463
464   if ( closed )
465     avp->setCursor( Qt::CrossCursor );
466   else if ( valid )
467     avp->setCursor( Qt::PointingHandCursor );
468   else
469     avp->setCursor( Qt::ForbiddenCursor );
470
471   if ( !e )
472     return;
473
474   if ( e->type() == QEvent::MouseButtonRelease && ( e->button() & sketchButton() ) )
475   {
476     myResult = (closed && (points->count() > 2)) ? Accept : Reject;
477     QApplication::postEvent( avp, new QMouseEvent( e->type(), e->pos(),
478                                                    e->globalPos(), e->button(), 
479                                                    e->buttons(), e->modifiers() ) );
480   }
481   else if ( ( e->type() == QEvent::MouseButtonRelease && ( e->button() & myDelButton ) ) ||
482             ( e->type() == QEvent::MouseButtonDblClick && ( e->button() & myDelButton ) ) )
483   {
484     if ( points->count() > 1 )
485       points->resize( points->count() - 1 );
486     onMouse( 0 );
487   }
488   myDbl = e->type() == QEvent::MouseButtonDblClick && ( e->button() & myAddButton );
489 }
490
491 void OCCViewer_PolygonSketcher::onSketch( SketchState state )
492 {
493   QPolygon* points = (QPolygon*)data();
494   switch (myMode) {
495   case Poligone:
496     if (mypPolyRB) {
497       if (state == Fin) {
498         mypPolyRB->clearGeometry();
499         mypPolyRB->hide();
500         mypViewWindow->activateSketching(OCCViewer_ViewWindow::NoSketching);
501       }
502       else {
503         mypPolyRB->setUpdatesEnabled(false);
504         if (!mypPolyRB->isVisible())
505           mypPolyRB->show();
506
507         if (state != Fin && points->count())
508           mypPolyRB->initGeometry(QPolygon(*points) << myCurr);
509         mypPolyRB->setUpdatesEnabled(true);
510       }
511     }
512     break;
513   case Circle:
514     if (mypCircleRB) {
515       if (state == Fin) {
516         mypCircleRB->getPoligon(points);
517         mypCircleRB->clearGeometry();
518         mypCircleRB->hide();
519         if (points->size() == CIRCLE_NB_POINTS)
520           myResult = Accept;
521         mypViewWindow->activateSketching(OCCViewer_ViewWindow::NoSketching);
522       }
523       else {
524         mypCircleRB->setUpdatesEnabled(false);
525
526         if (state != Fin) {
527           if (mypCircleRB->isCenterDefined()) {
528             mypCircleRB->setRadius(myCurr);
529             if ((mypCircleRB->radius() > MIN_RADIUS) && (!mypCircleRB->isVisible()))
530               mypCircleRB->show();
531           }
532           else {
533             mypCircleRB->initGeometry(myCurr);
534           }
535         }
536         mypCircleRB->setUpdatesEnabled(true);
537       }
538     }
539     break;
540   }
541 }
542
543 bool OCCViewer_PolygonSketcher::isValid( const QPolygon* aPoints, const QPoint& aCur ) const
544 {
545   if ( !aPoints->count() )
546     return true;
547
548   if ( aPoints->count() == 1 && aPoints->point( 0 ) == aCur )
549     return false;
550
551   const QPoint& aLast = aPoints->point( aPoints->count() - 1 );
552
553   if ( aLast == aCur )
554     return true;
555
556   bool res = true;
557   for ( int i = 0; i < aPoints->count() - 1 && res; i++ )
558   {
559     const QPoint& aStart = aPoints->point( i );
560     const QPoint& anEnd  = aPoints->point( i + 1 );
561     res = !isIntersect( aStart, anEnd, aCur, aLast );
562   }
563
564   return res;
565 }
566
567 bool OCCViewer_PolygonSketcher::isIntersect( const QPoint& aStart1, const QPoint& anEnd1,
568                                              const QPoint& aStart2, const QPoint& anEnd2 ) const
569 {
570   if ( ( aStart1 == aStart2 && anEnd1 == anEnd2 ) ||
571        ( aStart1 == anEnd2 && anEnd1 == aStart2 ) )
572     return true;
573
574   if ( aStart1 == aStart2 || aStart2 == anEnd1 ||
575        aStart1 == anEnd2 || anEnd1 == anEnd2 )
576     return false;
577
578   double x11 = aStart1.x() * 1.0;
579   double x12 = anEnd1.x() * 1.0;
580   double y11 = aStart1.y() * 1.0;
581   double y12 = anEnd1.y() * 1.0;
582
583   double x21 = aStart2.x() * 1.0;
584   double x22 = anEnd2.x() * 1.0;
585   double y21 = aStart2.y() * 1.0;
586   double y22 = anEnd2.y() * 1.0;
587
588   double k1 = x12 == x11 ? 0 : ( y12 - y11 ) / ( x12 - x11 );
589   double k2 = x22 == x21 ? 0 : ( y22 - y21 ) / ( x22 - x21 );
590
591   double b1 = y11 - k1 * x11;
592   double b2 = y21 - k2 * x21;
593
594   if ( k1 == k2 )
595   {
596     if ( b1 != b2 )
597       return false;
598     else
599       return !( ( qMax( x11, x12 ) <= qMin( x21, x22 ) ||
600                   qMin( x11, x12 ) >= qMax( x21, x22 ) ) &&
601                 ( qMax( y11, y12 ) <= qMin( y21, y22 ) ||
602                   qMin( y11, y12 ) >= qMax( y21, y22 ) ) );
603   }
604   else
605   {
606     double x0 = ( b2 - b1 ) / ( k1 - k2 );
607     double y0 = ( k1 * b2 - k2 * b1 ) / ( k1 - k2 );
608
609     if ( qMin( x11, x12 ) < x0 && x0 < qMax( x11, x12 ) &&
610          qMin( y11, y12 ) < y0 && y0 < qMax( y11, y12 ) &&
611          qMin( x21, x22 ) < x0 && x0 < qMax( x21, x22 ) &&
612          qMin( y21, y22 ) < y0 && y0 < qMax( y21, y22 ) )
613       return true;
614   }
615   return false;
616 }
617
618
619 void OCCViewer_PolygonSketcher::setSketcherMode(int theMode)
620 {
621   myMode = (SketchMode)theMode;
622 }