Frapper  1.0a
PainterGraphicsItems.h
Go to the documentation of this file.
1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of FRAPPER
4 research.animationsinstitut.de
5 sourceforge.net/projects/frapper
6 
7 Copyright (c) 2008-2012 Filmakademie Baden-Wuerttemberg, Institute of Animation
8 
9 This program is free software; you can redistribute it and/or modify it under
10 the terms of the GNU Lesser General Public License as published by the Free Software
11 Foundation; version 2.1 of the License.
12 
13 This program is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public License along with
18 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 Place - Suite 330, Boston, MA 02111-1307, USA, or go to
20 http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
21 -----------------------------------------------------------------------------
22 */
23 
32 
33 #ifndef PAINTERGRAPHICSITEMS_H
34 #define PAINTERGRAPHICSITEMS_H
35 
36 #include "FrapperPrerequisites.h"
37 
38 #include <QtGui/QPainter>
39 #include <QtGui/QPainterPath>
40 #include <QtGui/QGraphicsItem>
41 #include <QtGui/QGraphicsRectItem>
42 #include <QtGui/QGraphicsPathItem>
43 #include <QtGui/QAbstractGraphicsShapeItem>
44 
45 namespace PainterPanel {
46 using namespace Frapper;
47 
57 
58  class BaseShapeItem: public QAbstractGraphicsShapeItem
59  {
60  protected:
64  class PointHandle : public QGraphicsRectItem
65  {
66  public:
68  PointHandle( QPointF position, BaseShapeItem* parent, QColor penColor = Qt::gray) : QGraphicsRectItem(-4, -4, 8, 8, parent), m_parent(parent)
69  {
70  setBrush( Qt::NoBrush );
71  setPen( QPen(penColor));
72  setFlags( ItemIsMovable | ItemSendsGeometryChanges );
73  setPos(position);
74  setVisible(false);
75  setCursor(Qt::SizeAllCursor);
76  };
77 
78  protected:
79 
82  virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
83  {
84  // set new position of handle and call updateShape on parent
85  setPos( mapToParent(event->pos()) );
86  m_parent->updateShape();
87  };
88 
89  private:
91  BaseShapeItem* m_parent;
92 
93  }; // PointHandle
94 
95  protected:
96 
100  class TranslationHandle : public QGraphicsRectItem
101  {
102  public:
104  TranslationHandle( BaseShapeItem* parent ) : QGraphicsRectItem(-6, -6, 12, 12, parent), m_parent(parent)
105  {
106  setBrush( Qt::gray );
107  setFlags( ItemIsMovable | ItemSendsGeometryChanges );
108  setVisible(false);
109  setCursor(Qt::SizeAllCursor);
110  };
111 
112  protected:
115  virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
116  {
117  // set new position of parent
118  QPointF off = event->scenePos() - event->lastScenePos();
119  m_parent->setPos( m_parent->pos() + off);
120  };
121 
122  private:
124  BaseShapeItem* m_parent;
125  }; // TranslationHandle
126 
127  protected:
128 
132  class RotationHandle : public QGraphicsEllipseItem
133  {
134  public:
136  RotationHandle( BaseShapeItem* parent) : QGraphicsEllipseItem(-6, -6, 12, 12, parent),
137  m_parent(parent),
138  m_angle(0.0f)
139  {
140  setBrush(QBrush(Qt::green));
141  setFlags( ItemIsMovable | ItemSendsGeometryChanges);
142  setVisible(false);
143  setCursor(Qt::SizeHorCursor);
144  };
145 
146  protected:
149  virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
150  {
151  // update angle by cursor movement in scene coords
152  m_angle += (event->scenePos() - event->lastScenePos()).x();
153 
154  // set transformation of parent to apply rotation
155  QPointF p = pos();
156  m_parent->setTransform( QTransform().translate(p.x(), p.y()).rotate(m_angle).translate(-p.x(),-p.y()));
157  };
158 
159  private:
161  BaseShapeItem* m_parent;
162 
164  float m_angle;
165 
166  }; //RotationHandle
167 
168  public: // constructors and destructor
169 
176  BaseShapeItem ( QPen pen = QPen(), QBrush brush = QBrush()) : QAbstractGraphicsShapeItem()
177  {
178  setFlags( ItemIsSelectable );
179  setPen( pen);
180  setBrush(brush);
181  m_rotateHandle = new RotationHandle(this);
182  m_translateHandle = new TranslationHandle(this);
183  };
184 
192  virtual ~BaseShapeItem()
193  {
194  // delete extra handles of this item
195  foreach( PointHandle* handle, m_points)
196  delete handle;
197  delete m_translateHandle;
198  delete m_rotateHandle;
199  }
200 
201  public: // enumerations
202  enum ItemType {
203  BASE=0,
204  PAINT, LINE, RECT, CIRCLE, ELLIPSE, POLYGON, POLYLINE, CURVE, CLOSEDCURVE,
205  NUM_ITEMTYPES
206  };
207 
208  enum { Type = UserType + 1 };
209 
210  public: // virtual functions
211 
213  virtual int type() const
214  {return Type;}
215 
217  virtual ItemType GetItemType()
218  { return BASE; }
219 
221  virtual void addPosition( QPointF pos ) = 0;
222 
224  virtual void updatePosition( QPointF pos ) = 0;
225 
228  virtual void skipLastPosition( bool& emptyItem )
229  {emptyItem = false;};
230 
232  virtual QRectF boundingRect() const
233  {
234  QRectF bb = m_shape.boundingRect();
235  int pw = pen().width();
236  return QRectF( bb.left() - pw/2, bb.top() - pw/2,
237  bb.width() + pw/2, bb.height() + pw/2);
238 
239  };
240 
243  virtual void paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
244  {
245  QPen painterPen = pen();
246  if (isSelected())
247  painterPen.setColor(Qt::yellow);
248 
249  painter->setPen(painterPen);
250  painter->setBrush( brush());
251  painter->drawPath( m_shape );
252  };
253 
254  QString getDescriptionText()
255  {
256  ItemType itemType = GetItemType() ;
257  switch( itemType )
258  {
259  case BASE: return "Basic";
260  case PAINT: return "Paint";
261  case LINE: return "Line";
262  case RECT: return "Rectangle";
263  case CIRCLE: return "Circle";
264  case ELLIPSE: return "Ellipse";
265  case POLYGON: return "Polygon";
266  case POLYLINE: return "Polyline";
267  case CURVE: return "Curve";
268  case CLOSEDCURVE: return "Closed Curve";
269  }
270  return "Unknown";
271  }
272 
273  protected: // protected functions
274 
277  virtual void updateShape()
278  {
279  QPointF center = m_shape.boundingRect().center();
280  m_rotateHandle->setPos( center );
281  m_translateHandle->setPos(center.x(), center.y()-20);
282  };
283 
285  virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value)
286  {
287  if( change == ItemSelectedHasChanged)
288  showHandles( isSelected());
289  return QGraphicsItem::itemChange( change, value);
290  }
291 
292  private: // functions
293 
295  void showHandles( bool show = true )
296  {
297  foreach( PointHandle *handle, m_points )
298  handle->setVisible(show);
299  m_rotateHandle->setVisible(show);
300  m_translateHandle->setVisible(show);
301  }
302 
303  protected: // data
304 
306  QPainterPath m_shape;
307 
309  QList<PointHandle*> m_points;
310 
313 
316 
317  }; //BaseShapeItem
318 
319 
320 
321 
322 /************************************************************************/
323 /* PaintItem */
324 /************************************************************************/
325 
331 
332  class PaintItem : public BaseShapeItem
333  {
334  public:
336  PaintItem( QPointF startPos, QPen pen, QBrush brush ) : BaseShapeItem( pen, brush)
337  {
338  m_shape.moveTo( startPos );
339  };
340 
343  void updatePosition( QPointF pos )
344  {
345  prepareGeometryChange();
346  m_shape.lineTo( pos );
347  };
348 
351  void addPosition( QPointF pos )
352  {
353  prepareGeometryChange();
354  m_shape.lineTo( pos );
355  };
356 
358  virtual ItemType GetItemType() { return PAINT; }
359 
360  }; //PaintItem
361 
362 
363 
364 
365 /************************************************************************/
366 /* LineItem */
367 /************************************************************************/
368 
373  class LineItem : public BaseShapeItem
374  {
375  public:
377  LineItem( QPointF startPos, QPen pen, QBrush brush ) : BaseShapeItem( pen, brush)
378  {
379  m_points.push_back( new PointHandle( startPos, this));
380  m_points.push_back( new PointHandle( startPos, this));
381  };
382 
385  void updatePosition( QPointF pos )
386  {
387  m_points.at(1)->setPos(pos);
388  updateShape();
389  }
390 
393  void addPosition( QPointF pos )
394  {
395  updatePosition(pos);
396  }
397 
399  void updateShape()
400  {
401  prepareGeometryChange();
402  m_shape = QPainterPath(); // clear
403  m_shape.moveTo( ( m_points.at(0)->pos()));
404  m_shape.lineTo( ( m_points.at(1)->pos()));
406  };
407 
409  virtual ItemType GetItemType() { return LINE; }
410 
411  }; //RectItem
412 
413 
414 
415 
416 /************************************************************************/
417 /* RectItem */
418 /************************************************************************/
419 
424  class RectItem : public BaseShapeItem
425  {
426  public:
428  RectItem( QPointF startPos, QPen pen, QBrush brush ) : BaseShapeItem( pen, brush)
429  {
430  m_points.push_back( new PointHandle( startPos, this));
431  m_points.push_back( new PointHandle( startPos, this));
432  };
433 
436  void updatePosition( QPointF pos )
437  {
438  m_points.at(1)->setPos(pos);
439  updateShape();
440  }
441 
444  void addPosition( QPointF pos )
445  {
446  updatePosition(pos);
447  }
448 
450  void updateShape()
451  {
452  prepareGeometryChange();
453  m_shape = QPainterPath(); // clear
454  m_shape.addRect( QRectF(( m_points.at(0)->pos()), ( m_points.at(1)->pos())).normalized()); // use normalized to get a valid (non-negative) rect
456  };
457 
459  virtual ItemType GetItemType() { return RECT; }
460 
461  }; //RectItem
462 
463 
464 
465 
466 /************************************************************************/
467 /* CircleItem */
468 /************************************************************************/
469 
474  class CircleItem : public BaseShapeItem
475  {
476  public:
478  CircleItem( QPointF startPos, QPen pen, QBrush brush ) : BaseShapeItem( pen, brush)
479  {
480  m_points.push_back( new PointHandle( startPos, this));
481  m_points.push_back( new PointHandle( startPos, this));
482  };
483 
486  void updatePosition( QPointF pos )
487  {
488  m_points.at(1)->setPos(pos);
489  updateShape();
490  }
491 
494  void addPosition( QPointF pos )
495  {
496  updatePosition(pos);
497  }
498 
500  void updateShape()
501  {
502  prepareGeometryChange();
503  QRectF rect = QRectF(( m_points.at(0)->pos()), ( m_points.at(1)->pos())).normalized();
504  // calculate rect of circle
505  float d = std::min( rect.width(), rect.height());
506  QRectF circleRect( rect.left(), rect.top(), d, d);
507  m_shape = QPainterPath(); // clear
508  m_shape.addEllipse( circleRect );
510  };
511 
513  virtual ItemType GetItemType() { return CIRCLE; }
514 
515  }; //CircleItem
516 
517 
518 
519 
520 /************************************************************************/
521 /* EllipseItem */
522 /************************************************************************/
523 
528  class EllipseItem : public BaseShapeItem
529  {
530  public:
532  EllipseItem( QPointF startPos, QPen pen, QBrush brush ) : BaseShapeItem( pen, brush)
533  {
534  m_points.push_back( new PointHandle( startPos, this));
535  m_points.push_back( new PointHandle( startPos, this));
536  };
537 
540  void updatePosition( QPointF pos )
541  {
542  m_points.at(1)->setPos(pos);
543  updateShape();
544  }
545 
548  void addPosition( QPointF pos )
549  {
550  updatePosition(pos);
551  }
552 
554  void updateShape()
555  {
556  prepareGeometryChange();
557  QRectF rect = QRectF(( m_points.at(0)->pos()), ( m_points.at(1)->pos())).normalized();
558  m_shape = QPainterPath(); // clear
559  m_shape.addEllipse( rect );
561  };
562 
564  virtual ItemType GetItemType() { return ELLIPSE; }
565 
566  }; //EllipseItem
567 
568 
569 
570 /************************************************************************/
571 /* PolyLineItem */
572 /************************************************************************/
573 
579  {
580  public:
582  PolyLineItem( QPointF startPos, QPen pen, QBrush brush ) : BaseShapeItem( pen, brush)
583  {
584  m_points.push_back( new PointHandle( startPos, this));
585  };
586 
589  void addPosition( QPointF pos )
590  {
591  m_points.push_back( new PointHandle( pos, this));
592  updateShape();
593  }
594 
597  void updatePosition( QPointF pos )
598  {
599  bool emptyItem;
600  skipLastPosition( emptyItem);
601  addPosition(pos);
602  }
603 
606  void skipLastPosition( bool& emptyItem )
607  {
608  emptyItem = m_points.size() < 2;
609  if( !emptyItem ) {
610  delete m_points.back();
611  m_points.pop_back();
612  }
613  updateShape();
614  }
615 
617  virtual void updateShape()
618  {
619  // calculate new geometry
620  prepareGeometryChange();
621  QPointF start = ( m_points.at(0)->pos());
622  m_shape = QPainterPath(); // clear
623  m_shape.moveTo( start);
624  for( int i=1; i<m_points.size(); i++)
625  {
626  QPointF end = ( m_points.at(i)->pos());
627  m_shape.lineTo(end);
628  }
630  };
631 
633  virtual ItemType GetItemType() { return POLYLINE; }
634 
635  }; //PolyLineItem
636 
637 
638 /************************************************************************/
639 /* PolygonItem */
640 /************************************************************************/
641 
646  class PolygonItem : public PolyLineItem
647  {
648  public:
650  PolygonItem( QPointF startPos, QPen pen, QBrush brush ) : PolyLineItem( startPos, pen, brush)
651  {};
652 
654  void updateShape()
655  {
657  m_shape.lineTo(m_points.front()->pos());
658  };
659 
661  virtual ItemType GetItemType() { return POLYGON; }
662 
663  }; // PolygonItem
664 
665 
666 /************************************************************************/
667 /* CurveItem */
668 /************************************************************************/
669 
675  class CurveItem : public BaseShapeItem
676  {
677  public:
679  CurveItem( QPointF startPos, QPen pen, QBrush brush ) : BaseShapeItem( pen, brush)
680  {
681  m_points.push_back( new PointHandle(startPos, this));
682  };
683 
685  virtual ~CurveItem()
686  {
687  foreach( PointHandle* handle, m_cpoints)
688  delete handle;
689  }
690 
693  virtual void updatePosition( QPointF pos )
694  {
695  bool emptyItem;
696  skipLastPosition( emptyItem);
697  addPosition(pos);
698  }
699 
702  virtual void skipLastPosition( bool& emptyItem )
703  {
704  emptyItem = m_points.size() < 2;
705  if( m_cpoints.size() > 1)
706  {
707  delete m_cpoints.back();
708  m_cpoints.pop_back();
709  delete m_cpoints.back();
710  m_cpoints.pop_back();
711  }
712  if( !emptyItem )
713  {
714  delete m_points.back();
715  m_points.pop_back();
716  }
717  updateShape();
718  }
719 
722  virtual void addPosition( QPointF pos )
723  {
724  QPointF start = ( m_points.back()->pos());
725  QPointF end = pos;
726  QPointF vec = end - start;
727 
728  // calculate and add control points
729  m_cpoints.push_back( new PointHandle( start + 1.0f/3.0f * vec, this, Qt::red));
730  m_cpoints.push_back( new PointHandle( start + 2.0f/3.0f * vec, this, Qt::red));
731 
732  // store current position as endpoint of curve
733  m_points.push_back(new PointHandle(end, this));
734 
735  updateShape();
736  }
737 
739  virtual void updateShape()
740  {
741  prepareGeometryChange();
742 
743  QPointF startPos = m_points.at(0)->pos();
744 
745  m_shape = QPainterPath(); // clear
746  m_shape.moveTo( startPos );
747  m_controlShape = QPainterPath();
748  m_controlShape.moveTo( startPos );
749 
750  for( int i=1; i<m_points.size(); i++)
751  {
752  QPointF end = ( m_points.at(i)->pos());
753  QPointF cp1 = ( m_cpoints.at( 2*( i-1) )->pos());
754  QPointF cp2 = ( m_cpoints.at( 2*( i-1) +1)->pos());
755 
756  m_shape.cubicTo( cp1, cp2, end);
757  m_controlShape.lineTo(cp1);
758  m_controlShape.lineTo(cp2);
759  m_controlShape.lineTo(end);
760  }
761 
763  };
764 
766  virtual ItemType GetItemType() { return CURVE; }
767 
768  // react on changes of the item
769  virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value)
770  {
771  if( change == ItemSelectedHasChanged )
772  showControlHandles( isSelected() );
773 
774  return BaseShapeItem::itemChange(change, value);
775  }
776 
779  virtual void paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
780  {
781  if( isSelected() )
782  {
783  painter->setPen(QPen(Qt::lightGray, 1));
784  painter->drawPath( m_controlShape );
785  }
786  BaseShapeItem::paint(painter, option, widget);
787  };
788 
789  private:
791  void showControlHandles( bool show = true )
792  {
793  foreach( PointHandle *handle, m_cpoints )
794  handle->setVisible(show);
795  }
796 
797  protected:
799  QList<PointHandle*> m_cpoints;
800 
802  QPainterPath m_controlShape;
803 
804  }; //CurveItem
805 
806 
807 
808 /************************************************************************/
809 /* ClosedCurveItem */
810 /************************************************************************/
811 
817  class ClosedCurveItem : public CurveItem
818  {
819  public:
821  ClosedCurveItem( QPointF startPos, QPen pen, QBrush brush ) : CurveItem( startPos, pen, brush)
822  {};
823 
826  virtual void updatePosition( QPointF pos )
827  {
828  bool emptyItem;
829  CurveItem::skipLastPosition( emptyItem);
830  addPosition(pos);
831  }
832 
835  virtual void addPosition( QPointF pos )
836  {
837  if( m_cpoints.size() > 1)
838  {
839  delete m_cpoints.back();
840  m_cpoints.pop_back();
841  delete m_cpoints.back();
842  m_cpoints.pop_back();
843  }
844 
846 
847  QPointF start = m_points.back()->pos();
848  QPointF end = m_points.front()->pos();
849  QPointF vec = end - start;
850 
851  // calculate and add control points
852  m_cpoints.push_back( new PointHandle( start + 1.0f/3.0f * vec, this, Qt::red));
853  m_cpoints.push_back( new PointHandle( start + 2.0f/3.0f * vec, this, Qt::red));
854 
855  updateShape();
856  }
857 
860  virtual void skipLastPosition( bool& emptyItem)
861  {
862  CurveItem::skipLastPosition( emptyItem);
863 
864  int numPoints = m_points.size();
865  if( m_cpoints.size() == 2*numPoints)
866  {
867  QPointF start = m_points.back()->pos();
868  QPointF end = m_points.front()->pos();
869  QPointF vec = end - start;
870 
871  m_cpoints.at(2*numPoints-2)->setPos(start + 1.0f/3.0f * vec);
872  m_cpoints.at(2*numPoints-1)->setPos(start + 2.0f/3.0f * vec);
873  }
874 
875  updateShape();
876  }
877 
879  virtual void updateShape()
880  {
882 
883  int numPoints = m_points.size();
884  if( numPoints > 1 && m_cpoints.size() == 2*numPoints)
885  {
886  QPointF cp1 = m_cpoints.at(2*numPoints-2)->pos();
887  QPointF cp2 = m_cpoints.at(2*numPoints-1)->pos();
888  QPointF end = m_points.front()->pos();
889 
890  m_shape.cubicTo( cp1, cp2, end);
891  m_controlShape.lineTo(cp1);
892  m_controlShape.lineTo(cp2);
893  m_controlShape.lineTo(end);
894  }
895  };
896 
898  virtual ItemType GetItemType() { return CLOSEDCURVE; }
899 
900  }; //ClosedCurveItem
901 
902 } // end namespace Frapper
903 
904 #endif