Salome HOME
Copyrights update 2015.
[modules/yacs.git] / src / genericgui / SceneLinkItem.cxx
1 // Copyright (C) 2006-2015  CEA/DEN, EDF R&D
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 #define CHRONODEF
21 #include "chrono.hxx"
22
23 #include "SceneLinkItem.hxx"
24 #include "SceneDataPortItem.hxx"
25 #include "SceneCtrlPortItem.hxx"
26 #include "SceneElementaryNodeItem.hxx"
27 #include "SceneHeaderNodeItem.hxx"
28 #include "Scene.hxx"
29 #include "Resource.hxx"
30
31 #include "Menus.hxx"
32 #include <QPointF>
33 #include <cmath>
34
35 //#define _DEVDEBUG_
36 #include "YacsTrace.hxx"
37
38 using namespace std;
39 using namespace YACS::ENGINE;
40 using namespace YACS::HMI;
41
42
43 SceneLinkItem::SceneLinkItem(QGraphicsScene *scene, SceneItem *parent,
44                              ScenePortItem* from, ScenePortItem* to,
45                              QString label, Subject *subject)
46   : SceneObserverItem(scene, parent, label, subject)
47 {
48   _from = from;
49   _to = to;
50   _penColor     = Resource::link_draw_color.darker(Resource::link_pen_darkness);
51   _hiPenColor   = Resource::link_select_color.darker(Resource::link_pen_darkness);
52   _brushColor   = Resource::link_draw_color;
53   _hiBrushColor = Resource::link_select_color;
54   _level += 100;
55   setZValue(_level);
56   DEBTRACE("ZValue=" << zValue());
57   _lp.clear();
58   _directions.clear();
59   _nbPoints = 0;
60   _path = QPainterPath();
61 }
62
63 SceneLinkItem::~SceneLinkItem()
64 {
65 }
66
67 void SceneLinkItem::select(bool isSelected)
68 {
69   if (isSelected)
70     setZValue(_level+100);
71   else
72     setZValue(_level);
73   DEBTRACE("ZValue=" << zValue());
74   SceneObserverItem::select(isSelected);
75 }
76
77 QRectF SceneLinkItem::boundingRect() const
78 {
79   return _path.boundingRect();
80 }
81
82 QPainterPath SceneLinkItem::shape() const
83 {
84   //DEBTRACE("SceneLinkItem::shape");
85   return _path;
86 }
87
88 void SceneLinkItem::setShape(int thickness)
89 {
90   DEBTRACE("---");
91   _path = QPainterPath();
92   _path.setFillRule(Qt::WindingFill);
93   QPointF pfrom = start();
94   QPointF pto   = goal();
95   DEBTRACE(Scene::_straightLinks);
96   if (_nbPoints && !Scene::_straightLinks)
97     {
98       DEBTRACE("---");
99       addArrow(pfrom, _lp[0], _RIGHT, thickness);
100       for (int k=0; k<_nbPoints-1; k++)
101         addArrow(_lp[k], _lp[k+1], _directions[k+1], thickness);
102       addArrow(_lp[_nbPoints-1], pto, _RIGHT, thickness);
103     }
104   else
105     {
106       DEBTRACE("---");
107       double d = std::sqrt((pto.x() - pfrom.x())*(pto.x() - pfrom.x()) + (pto.y() - pfrom.y())*(pto.y() - pfrom.y()));
108       double sina = (pto.y() - pfrom.y())/d;
109       double cosa = (pto.x() - pfrom.x())/d;
110       double ep=3.0*thickness/2.0 * Resource::link_thickness;
111       _path.moveTo(pfrom.x() -ep*sina, pfrom.y() +ep*cosa);
112       _path.lineTo(pto.x()   -ep*sina, pto.y()   +ep*cosa);
113       _path.lineTo(pto.x()   +ep*sina, pto.y()   -ep*cosa);
114       _path.lineTo(pfrom.x() +ep*sina, pfrom.y() -ep*cosa);
115       _path.lineTo(pfrom.x() -ep*sina, pfrom.y() -ep*cosa);
116       //arrow
117       double x=(pto.x() + pfrom.x())/2.;
118       double y=(pto.y() + pfrom.y())/2.;
119       double l=8*ep;
120       double e=4*ep;
121       _path.moveTo(x+l*cosa,y+l*sina);
122       _path.lineTo(x+e*sina,y-e*cosa);
123       _path.lineTo(x-e*sina,y+e*cosa);
124       _path.lineTo(x+l*cosa,y+l*sina);
125     }
126 }
127
128 void SceneLinkItem::addArrow(QPointF pfrom,
129                              QPointF pto,
130                              YACS::HMI::Direction dir,
131                              int thickness)
132 {
133   qreal x, y, width, height, length;
134   double ep=thickness * Resource::link_thickness;
135   switch (dir)
136     {
137     case _UP:
138       x = pfrom.x() -ep;
139       y = pfrom.y() -ep;
140       width = 3*ep;
141       height = 2*ep + pto.y() -pfrom.y();
142       length = height;
143       break;
144     case _RIGHT:
145       x = pfrom.x() -ep;
146       y = pfrom.y() -ep;
147       width = 2*ep + pto.x() -pfrom.x();
148       height = 3*ep;
149       length = width;
150       break;
151     case _DOWN:
152       x = pto.x() -ep;
153       y = pto.y() -ep;
154       width = 3*ep;
155       height = 2*ep + pfrom.y() -pto.y();
156       length = height;
157       break;
158     case _LEFT:
159       x = pto.x() -ep;
160       y = pto.y() -ep;
161       width = 2*ep + pfrom.x() -pto.x();
162       height = 3*ep;
163       length = width;
164       break;
165     }
166   _path.addRect(x, y, width, height);
167
168   if (length > 20)
169     {
170       int e=5*ep, h1=4*ep, h2=8*ep;
171       switch (dir)
172         {
173         case _UP:
174           x = pfrom.x();
175           y = (pto.y() + pfrom.y())/2 +h1;
176           _path.moveTo(x, y);
177           _path.lineTo(x+e, y-h2);
178           _path.lineTo(x,   y-h1);
179           _path.lineTo(x-e, y-h2);
180           _path.lineTo(x,   y   );
181           break;
182         case _RIGHT:
183           x = (pto.x() + pfrom.x())/2 +h1;
184           y = pfrom.y();
185           _path.moveTo(x, y);
186           _path.lineTo(x-h2, y+e);
187           _path.lineTo(x-h1, y  );
188           _path.lineTo(x-h2, y-e);
189           _path.lineTo(x,    y  );
190           break;
191         case _DOWN:
192           x = pfrom.x();
193           y = (pto.y() + pfrom.y())/2 -h1;
194           _path.moveTo(x, y);
195           _path.lineTo(x+e, y+h2);
196           _path.lineTo(x,   y+h1);
197           _path.lineTo(x-e, y+h2);
198           _path.lineTo(x,   y   );
199           break;
200         case _LEFT:
201           x = (pto.x() + pfrom.x())/2 -h1;
202           y = pfrom.y();
203           _path.moveTo(x, y);
204           _path.lineTo(x+h2, y+e);
205           _path.lineTo(x+h1, y  );
206           _path.lineTo(x+h2, y-e);
207           _path.lineTo(x,    y  );
208           break;
209         }
210     }
211 }
212
213 void SceneLinkItem::paint(QPainter *painter,
214                           const QStyleOptionGraphicsItem *option,
215                           QWidget *widget)
216 {
217   //DEBTRACE("SceneLinkItem::paint " << _label.toStdString());
218   if (_path.isEmpty()) setShape(_emphasized+1);
219   painter->save();
220   QPen pen;
221   pen.setColor(getPenColor());
222   painter->setPen(pen);
223   painter->setBrush(getBrushColor());
224   painter->drawPath(_path);
225   painter->restore();
226 }
227
228 void SceneLinkItem::update(GuiEvent event, int type, Subject* son)
229 {
230   DEBTRACE("SceneLinkItem::update "<< eventName(event)<<" "<<type<<" "<<son);
231   switch (event)
232     {
233     case YACS::HMI::EMPHASIZE:
234       DEBTRACE("SceneObserverItem::update EMPHASIZE " << type);
235       if (type)
236         {
237           _emphasized = true;
238           setZValue(_level+100);
239           DEBTRACE("ZValue=" << zValue());
240           setShape(2);
241         }
242       else
243         {
244           _emphasized = false;
245           setZValue(_level);
246           DEBTRACE("ZValue=" << zValue());
247           setShape();
248         }
249       QGraphicsItem::update();
250       break;
251     case YACS::HMI::SWITCHSHAPE:
252       DEBTRACE("SceneObserverItem::update SWITCHSHAPE");
253       setShape(_emphasized+1);
254       QGraphicsItem::update();
255       break;
256     default:
257       ;
258     }
259 }
260
261 void SceneLinkItem::popupMenu(QWidget *caller, const QPoint &globalPos)
262 {
263   LinkMenu m;
264   m.popupMenu(caller, globalPos);
265 }
266
267 void SceneLinkItem::setPath(LinkPath lp)
268 {
269   DEBTRACE("SceneLinkItem::setPath " << lp.size());
270   CHRONO(10);
271   prepareGeometryChange();
272   _nbPoints = lp.size();
273   _lp.resize(_nbPoints+1);
274   _directions.resize(_nbPoints +2);
275   std::list<linkPoint>::const_iterator it = lp.begin();
276   int k=0;
277   qreal prevx = 0;
278   qreal prevy = 0;
279   bool dirx = true;
280   bool diry = false;
281   for (; it != lp.end(); ++it)
282     {
283       QPointF p = mapFromScene(QPointF((*it).x, (*it).y));
284       DEBTRACE("p(" << p.x() << "," << p.y() << ")");
285       if (k == 0)
286         {
287          _lp[k] = p;
288          _lp[k].setY(start().y());
289          _directions[k] = _RIGHT; // --- direction from start to point k=0
290          k++;
291         }
292       else
293         {
294           bool changeDir = true;
295           if (dirx && (p.y() == prevy))
296             {
297               _lp[k-1].setX(p.x());
298               changeDir = false;
299             }
300           if (diry && (p.x() == prevx))
301             {
302               _lp[k-1].setY(p.y());
303               changeDir = false;
304             }
305           if (changeDir)
306             {
307               dirx = !dirx;
308               diry = !diry;
309               _lp[k] = p;
310               if      (p.x() > prevx) _directions[k] = _RIGHT; // --- direction from k-1 to k
311               else if (p.x() < prevx) _directions[k] = _LEFT;
312               else if (p.y() > prevy) _directions[k] = _UP;
313               else if (p.y() < prevy) _directions[k] = _DOWN;
314               k++;
315             }
316         }
317       prevx = p.x();
318       prevy = p.y();
319     }
320
321   if(k==1)
322     {
323       //link should be direct (k=0) or orthogonal k>=2
324       k=2;
325       _lp[1]=_lp[0];
326       _lp[1].setY(goal().y());
327       if (goal().y() > start().y()) _directions[1] = _UP;
328       else _directions[1] = _DOWN;
329     }
330
331   if(k>2 && _directions[k-1]==_RIGHT)
332     {
333       //remove last point
334       k=k-1;
335     }
336
337   _nbPoints = k;
338   _lp[k-1].setY(goal().y());
339   _directions[k] = _RIGHT; // --- direction from point k-1 to goal
340   dirx = true;
341   diry = false;
342   for (k = _nbPoints -2; k >= 0; k--)
343     {
344       if(_lp[k].y() == prevy)
345         _lp[k].setY(goal().y());
346       else
347         break;
348     }
349   CHRONOSTOP(10);
350   CHRONO(11);
351   if (Scene::_simplifyLinks) minimizeDirectionChanges();
352   CHRONOSTOP(11);
353   CHRONO(12);
354   if (Scene::_force2NodesLink) force2points();
355   for (k=0; k<_nbPoints; k++)
356     DEBTRACE("_lp[" << k << "](" << _lp[k].x() << "," << _lp[k].y() << ")");
357   setShape(_emphasized+1);
358   SceneItem::update();
359 }
360
361 /*!
362  *  replace patterns like (right, down, right, down) with (right, right, down, down)
363  *  if new path is OK (no obstacle in the modified rectangle), then combine the segments.
364  */
365 void SceneLinkItem::minimizeDirectionChanges()
366 {
367   vector<QPointF> newlp;
368   vector<Direction> newdir;
369   newlp.resize(_nbPoints);
370   newdir.resize(_nbPoints +1);
371
372   bool modified = true;
373   while (modified)
374     {
375       modified = false;
376       Direction prevdir = _RIGHT;
377       newlp[0] = _lp[0];
378       newdir[0] = _directions[0];
379       int newk = 0;
380       for (int k = 0; k < _nbPoints; k++)
381         {
382           if (k< _nbPoints-2) // --- _nbPoints + 1 directions, look to k+3
383             if ((_directions[k] == _directions[k+2]) && (_directions[k+1] == _directions[k+3]))
384               {
385                 bool kmodif = true;
386                 qreal tryx;
387                 qreal tryy;
388                 if ((_directions[k] == _RIGHT) || (_directions[k] == _LEFT))
389                   {
390                     tryy = _lp[k].y();
391                     tryx = _lp[k+2].x();
392                   }
393                 else
394                   {
395                     tryy = _lp[k+2].y();
396                     tryx = _lp[k].x();
397                 }
398
399                 qreal xtop, ytop, width, height;
400                 if (_lp[k].x() < _lp[k+2].x())
401                   {
402                     xtop = _lp[k].x();
403                     width = _lp[k+2].x() - _lp[k].x();
404                   }
405                 else
406                   {
407                     xtop = _lp[k+2].x();
408                     width = _lp[k].x() - _lp[k+2].x();
409                   }
410                 if (_lp[k].y() < _lp[k+2].y())
411                   {
412                     ytop = _lp[k].y();
413                     height = _lp[k+2].y() - _lp[k].y();
414                   }
415                 else
416                   {
417                     ytop = _lp[k+2].y();
418                     height = _lp[k].y() - _lp[k+2].y();
419                   }
420                 QRectF aRect(mapToScene(QPointF(xtop, ytop)),
421                              mapToScene(QPointF(xtop + width, ytop + height)));
422                 QList<QGraphicsItem*> itemList =  scene()->items(aRect);
423                 for (int kk=0; kk<itemList.size(); kk++)
424                   {
425                     QGraphicsItem *item = itemList[kk];
426                     if ((dynamic_cast<SceneElementaryNodeItem*>(item)) ||
427                         (dynamic_cast<SceneHeaderNodeItem*>(item)))
428                       {
429                         kmodif = false;
430                         break;
431                       }}
432
433                 if (kmodif)
434                   {
435                     modified = true;
436                     _lp[k+1] = QPointF(tryx, tryy);
437                     _directions[k+1] = _directions[k];
438                     _directions[k+2] = _directions[k+3];
439                   }
440               }
441           if (k>0)
442             {
443               if (prevdir == _directions[k])
444                 {
445                   if ((prevdir == _RIGHT) || (prevdir == _LEFT))
446                     newlp[newk].setX(_lp[k].x());
447                   else
448                     newlp[newk].setY(_lp[k].y());
449                 }
450               else
451                 {
452                   newk++;
453                   newlp[newk] = _lp[k];
454                   newdir[newk] = _directions[k];         
455                 }
456
457             }
458           prevdir = _directions[k];          
459         }
460       DEBTRACE("modified : _nbPoints=" << _nbPoints << " newk=" << newk+1);
461       if (modified)
462         {
463 //           for (int i=0; i<=newk; i++)
464 //             {
465 //               DEBTRACE("("<< _lp[i].x() << "," << _lp[i].y() << ") ("
466 //                        << newlp[i].x() << "," << newlp[i].y() << ") "
467 //                        << _directions[i] << "-" << newdir[i]);
468 //             }
469 //           for (int i=newk+1; i<_nbPoints; i++)
470 //             {
471 //               DEBTRACE("("<< _lp[i].x() << "," << _lp[i].y() << ") "
472 //                        << _directions[i]);
473 //             }
474
475           if(newdir[newk]==_RIGHT)
476             {
477               //remove last point
478               newk=newk-1;
479             }
480           _nbPoints = newk+1;
481           for (int i=0; i<_nbPoints; i++)
482             {
483               _lp[i] = newlp[i];
484               _directions[i] = newdir[i];
485             }
486           _directions[_nbPoints] = _RIGHT;
487           DEBTRACE("_nbPoints " << _nbPoints);
488         }
489     }
490 }
491
492 void SceneLinkItem::force2points()
493 {
494   if (_nbPoints == 1)
495     {
496       QPointF a = start();
497       QPointF b = goal();
498       if (a.y() != b.y())
499         {
500           vector<QPointF> newlp;
501           vector<Direction> newdir;
502           newlp.resize(_nbPoints+1);
503           newdir.resize(_nbPoints +2);
504           //_lp.resize(_nbPoints +1);         // --- not OK : data may be lost! pre reserve enough before!
505           //_directions.resize(_nbPoints +2);
506           newlp[0] = _lp[0];
507           newlp[1] = _lp[0];
508           newlp[0].setY(a.y());
509           newlp[1].setY(b.y());
510           newdir[0] = _RIGHT;
511           newdir[2] = _RIGHT;
512           if (a.y() > b.y())
513             newdir[1] = _DOWN;
514           else
515             newdir[1] = _UP;
516           _nbPoints = 2;
517           for (int i=0; i<_nbPoints; i++)
518             {
519               _lp[i] = newlp[i];
520               _directions[i] = newdir[i];
521             }
522           _directions[_nbPoints] = _RIGHT;
523         }
524     }
525 }
526
527
528 QPointF SceneLinkItem::start()
529 {
530   SceneDataPortItem* dpif = dynamic_cast<SceneDataPortItem*>(_from);
531   QPointF localFrom(dpif->getWidth()*(9.5/10), dpif->getHeight()/2);
532   DEBTRACE("localFrom(" << localFrom.x() << "," << localFrom.y() << ")");
533   return mapFromItem(dpif, localFrom);
534 }
535
536 QPointF SceneLinkItem::goal()
537 {
538   SceneDataPortItem* dpit = dynamic_cast<SceneDataPortItem*>(_to);
539   QPointF localTo(dpit->getWidth()/20, dpit->getHeight()/2);
540   DEBTRACE("localTo(" << localTo.x() << "," << localTo.y() << ")");
541   return mapFromItem(dpit, localTo);
542 }
543
544 void SceneLinkItem::updateShape()
545 {
546   prepareGeometryChange();
547   if (_nbPoints)
548     {
549       // a path has been calculated, update it
550       QPointF pfrom = start();
551       QPointF pto   = goal();
552       _lp[0].setY(pfrom.y());
553       if(_lp[1].y() > _lp[0].y())_directions[1]=_UP;
554       else _directions[1]=_DOWN;
555       _lp[_nbPoints-1].setY(pto.y());
556       if(_lp[_nbPoints-1].y() > _lp[_nbPoints-2].y())_directions[_nbPoints-1]=_UP;
557       else _directions[_nbPoints-1]=_DOWN;
558     }
559   setShape(_emphasized+1);
560 }
561
562 QColor SceneLinkItem::getPenColor()
563 {
564   _penColor     = Resource::link_draw_color.darker(Resource::link_pen_darkness);
565   _hiPenColor   = Resource::link_select_color.darker(Resource::link_pen_darkness);
566   return SceneObserverItem::getPenColor();
567 }
568
569 QColor SceneLinkItem::getBrushColor()
570 {
571   _brushColor   = Resource::link_draw_color;
572   _hiBrushColor = Resource::link_select_color;
573   return SceneObserverItem::getBrushColor();
574 }
575
576 // Get the node at the "from" place of the link
577 SceneNodeItem* SceneLinkItem::getFromNode() {
578   SceneCtrlPortItem* p = dynamic_cast<SceneCtrlPortItem*>(_from);
579   if (p) {
580     return(p->getParentNode());
581   } else {
582     return(NULL);
583   };
584 }
585
586 // Get the node at the "to" place of the link
587 SceneNodeItem* SceneLinkItem::getToNode() {
588   SceneCtrlPortItem* p = dynamic_cast<SceneCtrlPortItem*>(_to);
589   if (p) {
590     return(p->getParentNode());
591   } else {
592     return(NULL);
593   };
594 }