#ifdef _WIN32
//See http://www.richelbilderbeek.nl/CppCompileErrorSwprintfHasNotBeenDeclared.htm
#undef __STRICT_ANSI__
#endif
//#include own header file as first substantive line of code, from:
// * John Lakos. Large-Scale C++ Software Design. 1996. ISBN: 0-201-63362-0. Section 3.2, page 110
#include "qtpathitem.h"
#include <cassert>
#include <cmath>
#include <QCursor>
#include <QGraphicsSceneMouseEvent>
#include <QKeyEvent>
#include <QPainter>
#include "qtrectitem.h"
const double QtPathItem::m_click_easy_width = 10.0;
QtPathItem::QtPathItem(
const QtRectItem * const from,
const bool tail,
const QtRectItem * const mid,
const bool head,
const QtRectItem * const to,
QGraphicsItem *parent, QGraphicsScene *scene)
: QGraphicsItem(parent,scene),
m_from(from),
m_head(head),
m_mid(mid),
m_tail(tail),
m_to(to)
{
this->setFlags(QGraphicsItem::ItemIsSelectable);
assert(!(flags() & QGraphicsItem::ItemIsMovable) );
assert( (flags() & QGraphicsItem::ItemIsSelectable) );
//Accept enterHoverEvents
this->setAcceptHoverEvents(true);
//Put this arrow item under the rect
this->setZValue(mid->zValue() - 1.0);
}
QRectF QtPathItem::boundingRect() const
{
return shape().boundingRect();
}
double QtPathItem::GetAngle(const double dx, const double dy)
{
return M_PI - (std::atan(dx/dy));
}
void QtPathItem::hoverEnterEvent(QGraphicsSceneHoverEvent *)
{
this->setCursor(QCursor(Qt::PointingHandCursor));
}
void QtPathItem::keyPressEvent(QKeyEvent *event)
{
switch (event->key())
{
case Qt::Key_F1:
case Qt::Key_1:
case Qt::Key_T:
case Qt::Key_Minus:
m_tail = !m_tail;
this->update();
break;
case Qt::Key_F2:
case Qt::Key_2:
case Qt::Key_H:
case Qt::Key_Plus:
m_head = !m_head;
this->update();
break;
default:
break;
}
QGraphicsItem::keyPressEvent(event);
}
void QtPathItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (event->modifiers() & Qt::ShiftModifier)
{
if ((event->pos() - this->m_from->pos()).manhattanLength() < 10.0)
{
m_tail = !m_tail;
this->update();
}
else if ((event->pos() - this->m_to->pos()).manhattanLength() < 10.0)
{
m_head = !m_head;
this->update();
}
}
QGraphicsItem::mousePressEvent(event);
}
//void QtPathItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
void QtPathItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
painter->setRenderHint(QPainter::Antialiasing);
if (this->isSelected())
{
const QColor color(255,0,0);
QPen pen;
pen.setColor(color);
pen.setWidth(3);
painter->setPen(pen);
}
else
{
const QColor color(0,0,0);
QPen pen;
pen.setColor(color);
pen.setWidth(1);
painter->setPen(pen);
}
QPainterPath curve;
curve.moveTo(m_from->pos());
//Line must go _though_ mid pos, instead of using it as a virtual hinge point
//Solution:
// - define point 'center' as the middle between from and to
// - define point 'beyond' as the mirror point of 'center', using mid_pos as a mirror
const QPointF center((m_from->pos() + m_to->pos()) / 2.0);
const double dx_mid_center = m_mid->pos().x() - center.x();
const double dy_mid_center = m_mid->pos().y() - center.y();
const QPointF beyond(center.x() + dx_mid_center + dx_mid_center, center.y() + dy_mid_center + dy_mid_center);
curve.quadTo(beyond,m_to->pos());
painter->drawPath(curve);
{
const double sz = 10.0; //pixels
if (m_tail)
{
//The angle from midpoint to tail
//Thanks goes out to Toine van den Bogaart and Theo van den Bogaart for being happy to help with the math
const double dx = beyond.x() - m_from->pos().x();
const double dy = beyond.y() - m_from->pos().y();
double angle = GetAngle(dx,dy);
if (dy >= 0.0) angle = (1.0 * M_PI) + angle;
const QPointF p0 = m_from->pos();
const QPointF p1
= p0 + QPointF(
std::sin(angle + M_PI + (M_PI * 0.1)) * sz,
-std::cos(angle + M_PI + (M_PI * 0.1)) * sz);
const QPointF p2
= p0 + QPointF(
std::sin(angle + M_PI - (M_PI * 0.1)) * sz,
-std::cos(angle + M_PI - (M_PI * 0.1)) * sz);
painter->drawPolygon(QPolygonF() << p0 << p1 << p2);
}
if (m_head)
{
//The angle from midpoint to head
const double dx = m_to->pos().x() - beyond.x();
const double dy = m_to->pos().y() - beyond.y();
double angle = GetAngle(dx,dy);
if (dy >= 0.0) angle = (1.0 * M_PI) + angle;
const QPointF p0 = m_to->pos();
const QPointF p1
= p0 + QPointF(
std::sin(angle + 0.0 + (M_PI * 0.1)) * sz,
-std::cos(angle + 0.0 + (M_PI * 0.1)) * sz);
const QPointF p2
= p0 + QPointF(
std::sin(angle + 0.0 - (M_PI * 0.1)) * sz,
-std::cos(angle + 0.0 - (M_PI * 0.1)) * sz);
painter->drawPolygon(QPolygonF() << p0 << p1 << p2);
}
}
//QGraphicsItem::paint(painter,option,widget);
//this->update();
}
QPainterPath QtPathItem::shape() const
{
QPainterPath path;
//Line must go _though_ mid pos, instead of using it as a virtual hinge point
//Solution:
// - define point p as the middle between from and to
// - define point q as the mirror point of q, using mid_pos as a mirror
path.moveTo(m_from->pos());
const QPointF p((m_from->pos() + m_to->pos()) / 2.0);
const double dx = m_mid->pos().x() - p.x();
const double dy = m_mid->pos().y() - p.y();
const QPointF q(p.x() + dx + dx, p.y() + dy + dy);
path.quadTo(q,m_to->pos());
QPainterPathStroker stroker;
stroker.setWidth(m_click_easy_width);
return stroker.createStroke(path);
}
|