#include "splineitem.h"
#include "bpointitem.h"
+#include "nearestpoint.h"
#include <QGraphicsScene>
+#include <QCursor>
+#include <QGraphicsSceneMouseEvent>
+
+
+inline QPointF closestPointInRect(QPointF point, QRectF rect)
+{
+ QPointF closest;
+ rect = rect.normalized();
+ closest.setX(qBound<qreal>(rect.left(), point.x(), rect.right()));
+ closest.setY(qBound<qreal>(rect.top(), point.y(), rect.bottom()));
+ return closest;
+}
+
+void deCasteljau(BPoint *p1, BPoint *p2, BPoint *res, double t)
+{
+ QPointF ab, bc, cd;
+
+ ab = QLineF(p1->p, p1->h2).pointAt(t);
+ bc = QLineF(p1->h2, p2->h1).pointAt(t);
+ cd = QLineF(p2->h1, p2->p).pointAt(t);
+
+ res->h1 = QLineF(ab, bc).pointAt(t);
+ res->h2 = QLineF(bc, cd).pointAt(t);
+ res->p = QLineF(res->h1, res->h2).pointAt(t);
+
+ p1->h2 = ab;
+ p2->h1 = cd;
+}
+
SplineItem::SplineItem(const QList< BPoint >& points, QGraphicsItem* parent, QGraphicsScene *scene) :
QGraphicsPathItem(parent, scene)
framepen.setColor(Qt::yellow);
setPen(framepen);
setBrush(Qt::NoBrush);
+ setAcceptHoverEvents(true);
if (points.isEmpty())
return;
return points;
}
+void SplineItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
+{
+ QGraphicsItem::mousePressEvent(event);
+
+ if (!event->isAccepted()) {
+ QRectF r(event->scenePos() - QPointF(6, 6), QSizeF(12, 12));
+ if (path().intersects(r) && !path().contains(r)) {
+ double t = 0;
+ BPointItem *i, *i1, *i2;
+ BPoint p, p1, p2;
+ int ix = getClosestPointOnCurve(event->scenePos(), &t);
+ i1 = qgraphicsitem_cast<BPointItem *>(childItems().at(ix));
+ i2 = qgraphicsitem_cast<BPointItem *>(childItems().at((ix + 1) % childItems().count()));
+ p1 = i1->getPoint();
+ p2 = i2->getPoint();
+
+ deCasteljau(&p1, &p2, &p, t);
+
+ i1->setPoint(p1);
+ i2->setPoint(p2);
+
+ i = new BPointItem(p, this);
+ // TODO: make it work with Qt < 4.6
+#if QT_VERSION >= 0x040600
+ i->stackBefore(i2);
+#endif
+ }
+ }
+}
+
+void SplineItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
+{
+ QGraphicsItem::mouseMoveEvent(event);
+}
+
+void SplineItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
+{
+ QGraphicsItem::mouseReleaseEvent(event);
+}
+
+void SplineItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
+{
+ QGraphicsItem::hoverMoveEvent(event);
+
+ QRectF r(event->scenePos() - QPointF(6, 6), QSizeF(12, 12));
+ if (path().intersects(r) && !path().contains(r))
+ setCursor(QCursor(Qt::PointingHandCursor));
+ else
+ unsetCursor();
+}
+
+int SplineItem::getClosestPointOnCurve(QPointF point, double *tFinal)
+{
+ // TODO: proper minDiff
+ qreal diff = 10000, param = 0;
+ BPoint p1, p2;
+ int curveSegment = 0, j;
+ for (int i = 0; i < childItems().count(); ++i) {
+ j = (i + 1) % childItems().count();
+ p1 = qgraphicsitem_cast<BPointItem *>(childItems().at(i))->getPoint();
+ p2 = qgraphicsitem_cast<BPointItem *>(childItems().at(j))->getPoint();
+ QPolygonF bounding = QPolygonF() << p1.p << p1.h2 << p2.h1 << p2.p;
+ QPointF cl = closestPointInRect(point, bounding.boundingRect());
+ qreal d = (point - cl).manhattanLength();
+
+ if (d > diff)
+ continue;
+
+ Point2 b[4], p;
+ double t;
+
+ b[0].x = p1.p.x();
+ b[0].y = p1.p.y();
+ b[1].x = p1.h2.x();
+ b[1].y = p1.h2.y();
+ b[2].x = p2.h1.x();
+ b[2].y = p2.h1.y();
+ b[3].x = p2.p.x();
+ b[3].y = p2.p.y();
+
+ p.x = point.x();
+ p.y = point.y();
+
+ Point2 n = NearestPointOnCurve(p, b, &t);
+ cl.setX(n.x);
+ cl.setY(n.y);
+
+ d = (point - cl).manhattanLength();
+ if (d < diff) {
+ diff = d;
+ param = t;
+ curveSegment = i;
+ }
+ }
+
+ *tFinal = param;
+ return curveSegment;
+}
+
#include "splineitem.moc"