#include <QPainter>
#include <QMouseEvent>
-#include <KDebug>
BezierSplineEditor::BezierSplineEditor(QWidget* parent) :
QWidget(parent),
- m_zoomLevel(0),
m_mode(ModeNormal),
+ m_zoomLevel(0),
+ m_gridLines(3),
+ m_pixmapCache(NULL),
+ m_pixmapIsDirty(true),
m_currentPointIndex(-1)
{
setMouseTracking(true);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
+BezierSplineEditor::~BezierSplineEditor()
+{
+ if (m_pixmapCache)
+ delete m_pixmapCache;
+}
+
CubicBezierSpline BezierSplineEditor::spline()
{
return m_spline;
void BezierSplineEditor::setSpline(const CubicBezierSpline& spline)
{
- // TODO: cleanup
- m_spline.fromString(spline.toString());
+ int precision = m_spline.getPrecision();
+ m_spline = spline;
+ m_spline.setPrecision(precision);
+ m_currentPointIndex = -1;
+ m_mode = ModeNormal;
+ emit modified();
+ update();
}
BPoint BezierSplineEditor::getCurrentPoint()
m_spline.setPoint(m_currentPointIndex, p);
// during validation the point might have changed
emit currentPoint(m_spline.points()[m_currentPointIndex]);
+ emit modified();
update();
}
}
+void BezierSplineEditor::setPixmap(const QPixmap& pixmap)
+{
+ m_pixmap = pixmap;
+ m_pixmapIsDirty = true;
+ update();
+}
+
void BezierSplineEditor::slotZoomIn()
{
m_zoomLevel = qMax(m_zoomLevel-1, 0);
+ m_pixmapIsDirty = true;
update();
}
void BezierSplineEditor::slotZoomOut()
{
m_zoomLevel = qMin(m_zoomLevel+1, 3);
+ m_pixmapIsDirty = true;
+ update();
+}
+
+int BezierSplineEditor::gridLines()
+{
+ return m_gridLines;
+}
+
+void BezierSplineEditor::setGridLines(int lines)
+{
+ m_gridLines = qBound(0, lines, 8);
update();
}
QPainter p(this);
- p.fillRect(rect(), palette().background());
-
int wWidth = width() - 1;
int wHeight = height() - 1;
int offset = 1/8. * m_zoomLevel * (wWidth > wHeight ? wWidth : wHeight);
wWidth -= 2 * offset;
wHeight -= 2 * offset;
+ p.translate(offset, offset);
+
+ /*
+ * Background
+ */
+ p.fillRect(rect().translated(-offset, -offset), palette().background());
+ if (!m_pixmap.isNull()) {
+ if (m_pixmapIsDirty || !m_pixmapCache) {
+ if (m_pixmapCache)
+ delete m_pixmapCache;
+ m_pixmapCache = new QPixmap(wWidth + 1, wHeight + 1);
+ QPainter cachePainter(m_pixmapCache);
+
+ cachePainter.scale(1.0*(wWidth+1) / m_pixmap.width(), 1.0*(wHeight+1) / m_pixmap.height());
+ cachePainter.drawPixmap(0, 0, m_pixmap);
+ m_pixmapIsDirty = false;
+ }
+ p.drawPixmap(0, 0, *m_pixmapCache);
+ }
+
+
+ p.setPen(QPen(Qt::gray, 1, Qt::SolidLine));
+
+ /*
+ * Borders
+ */
+ if (m_zoomLevel != 0) {
+ p.drawRect(0, 0, wWidth, wHeight);
+ }
+
+ /*
+ * Grid
+ */
+ if (m_gridLines != 0) {
+ double stepH = wWidth / (double)(m_gridLines + 1);
+ double stepV = wHeight / (double)(m_gridLines + 1);
+ for (int i = 1; i <= m_gridLines; ++i) {
+ p.drawLine(QLineF(i * stepH, 0, i * stepH, wHeight));
+ p.drawLine(QLineF(0, i * stepV, wWidth, i * stepV));
+ }
+ }
+
+ p.setRenderHint(QPainter::Antialiasing);
+
/*
* Standard line
*/
- /*p.setPen(QPen(Qt::gray, 1, Qt::SolidLine));
- p.drawLine(QLineF(0, wHeight, wWidth, 0));*/
+ p.drawLine(QLineF(0, wHeight, wWidth, 0));
/*
* Spline
* to ints mathematically, not just rounds down
* like in C
*/
- p.drawLine(QLineF(prevX, prevY,
- x, curY).translated(offset, offset));
+ p.drawLine(QLineF(prevX, prevY, x, curY));
prevX = x;
prevY = curY;
}
p.drawLine(QLineF(prevX, prevY ,
- x, wHeight - m_spline.value(1.0, true) * wHeight).translated(offset, offset));
+ x, wHeight - m_spline.value(1.0, true) * wHeight));
/*
* Points + Handles
*/
+ int max = m_spline.points().count() - 1;
p.setPen(QPen(Qt::red, 1, Qt::SolidLine));
BPoint point;
QPolygon handle(4);
4, 1,
1, 4,
-2, 1);
- for (int i = 0; i < m_spline.points().count(); ++i) {
+#if QT_VERSION < 0x040600
+ QPolygon tmp;
+#endif
+ for (int i = 0; i <= max; ++i) {
point = m_spline.points().at(i);
if (i == m_currentPointIndex) {
p.setBrush(QBrush(QColor(Qt::red), Qt::SolidPattern));
- p.drawLine(QLineF(point.h1.x() * wWidth, wHeight - point.h1.y() * wHeight, point.p.x() * wWidth, wHeight - point.p.y() * wHeight).translated(offset, offset));
- p.drawLine(QLineF(point.p.x() * wWidth, wHeight - point.p.y() * wHeight, point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight).translated(offset, offset));
+ if (i != 0)
+ p.drawLine(QLineF(point.h1.x() * wWidth, wHeight - point.h1.y() * wHeight, point.p.x() * wWidth, wHeight - point.p.y() * wHeight));
+ if (i != max)
+ p.drawLine(QLineF(point.p.x() * wWidth, wHeight - point.p.y() * wHeight, point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight));
}
p.drawEllipse(QRectF(point.p.x() * wWidth - 3,
- wHeight - 3 - point.p.y() * wHeight, 6, 6).translated(offset, offset));
- p.drawConvexPolygon(handle.translated(point.h1.x() * wWidth, wHeight - point.h1.y() * wHeight).translated(offset, offset));
- p.drawConvexPolygon(handle.translated(point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight).translated(offset, offset));
+ wHeight - 3 - point.p.y() * wHeight, 6, 6));
+ if (i != 0) {
+#if QT_VERSION >= 0x040600
+ p.drawConvexPolygon(handle.translated(point.h1.x() * wWidth, wHeight - point.h1.y() * wHeight));
+#else
+ tmp = handle;
+ tmp.translate(point.h1.x() * wWidth, wHeight - point.h1.y() * wHeight);
+ p.drawConvexPolygon(tmp);
+#endif
+ }
+ if (i != max) {
+#if QT_VERSION >= 0x040600
+ p.drawConvexPolygon(handle.translated(point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight));
+#else
+ tmp = handle;
+ tmp.translate(point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight);
+ p.drawConvexPolygon(tmp);
+#endif
+ }
if ( i == m_currentPointIndex)
p.setBrush(QBrush(Qt::NoBrush));
void BezierSplineEditor::resizeEvent(QResizeEvent* event)
{
- m_spline.setPrecision(width());
+ m_spline.setPrecision(width() > height() ? width() : height());
+ m_pixmapIsDirty = true;
QWidget::resizeEvent(event);
}
po.h2 = QPointF(x+0.05, y+0.05);
m_currentPointIndex = m_spline.addPoint(po);
m_currentPointType = PTypeP;
- /*if (!d->jumpOverExistingPoints(newPoint, -1)) return;*/
} else {
m_currentPointIndex = closestPointIndex;
m_currentPointType = selectedPoint;
p = point.h2;
}
- m_grabOriginalX = p.x();
- m_grabOriginalY = p.y();
+ m_grabPOriginal = point;
+ if (m_currentPointIndex > 0)
+ m_grabPPrevious = m_spline.points()[m_currentPointIndex - 1];
+ if (m_currentPointIndex < m_spline.points().count() - 1)
+ m_grabPNext = m_spline.points()[m_currentPointIndex + 1];
m_grabOffsetX = p.x() - x;
m_grabOffsetY = p.y() - y;
}
m_spline.setPoint(m_currentPointIndex, point);
- //d->m_draggedAwayPointIndex = -1;
-
m_mode = ModeDrag;
emit currentPoint(point);
double x = (event->pos().x() - offset) / (double)(wWidth);
double y = 1.0 - (event->pos().y() - offset) / (double)(wHeight);
- if (m_mode == ModeNormal) { // If no point is selected set the the cursor shape if on top
+ if (m_mode == ModeNormal) {
+ // If no point is selected set the the cursor shape if on top
point_types type;
int nearestPointIndex = nearestPointInRange(QPointF(x, y), wWidth, wHeight, &type);
-
+
if (nearestPointIndex < 0)
setCursor(Qt::ArrowCursor);
else
setCursor(Qt::CrossCursor);
- } else { // Else, drag the selected point
- /*bool crossedHoriz = event->pos().x() - width() > MOUSE_AWAY_THRES ||
- event->pos().x() < -MOUSE_AWAY_THRES;
- bool crossedVert = event->pos().y() - height() > MOUSE_AWAY_THRES ||
- event->pos().y() < -MOUSE_AWAY_THRES;
-
- bool removePoint = (crossedHoriz || crossedVert);
-
- if (!removePoint && d->m_draggedAwayPointIndex >= 0) {
- // point is no longer dragged away so reinsert it
- QPointF newPoint(d->m_draggedAwayPoint);
- d->m_grab_point_index = d->m_curve.addPoint(newPoint);
- d->m_draggedAwayPointIndex = -1;
- }
-
- if (removePoint &&
- (d->m_draggedAwayPointIndex >= 0))
- return;
- */
-
+ } else {
+ // Else, drag the selected point
setCursor(Qt::CrossCursor);
x += m_grabOffsetX;
y += m_grabOffsetY;
-
- double leftX, rightX;
+
+ double leftX = 0.;
+ double rightX = 1.;
BPoint point = m_spline.points()[m_currentPointIndex];
switch (m_currentPointType) {
case PTypeH1:
rightX = point.p.x();
if (m_currentPointIndex == 0)
- leftX = -1000;
+ leftX = -4;
else
leftX = m_spline.points()[m_currentPointIndex - 1].p.x();
+
x = qBound(leftX, x, rightX);
- point.h1 = QPointF(x, y);
+ point.setH1(QPointF(x, y));
break;
+
case PTypeP:
- if (m_currentPointIndex == 0) {
- leftX = 0.0;
+ if (m_currentPointIndex == 0)
rightX = 0.0;
- /*if (d->m_curve.points().count() > 1)
- * rightX = d->m_curve.points()[d->m_grab_point_index + 1].x() - POINT_AREA;
- * else
- * rightX = 1.0;*/
- } else if (m_currentPointIndex == m_spline.points().count() - 1) {
- leftX = 1.0;//m_spline.points()[m_currentPointIndex - 1].p.x();
- rightX = 1.0;
- } else {
- //// the 1E-4 addition so we can grab the dot later.
- leftX = m_spline.points()[m_currentPointIndex - 1].p.x();// + POINT_AREA;
- rightX = m_spline.points()[m_currentPointIndex + 1].p.x();// - POINT_AREA;
- }
+ else if (m_currentPointIndex == m_spline.points().count() - 1)
+ leftX = 1.0;
+
x = qBound(leftX, x, rightX);
y = qBound(0., y, 1.);
- // move handles by same offset
- point.h1 += QPointF(x, y) - point.p;
- point.h2 += QPointF(x, y) - point.p;
+ // handles might have changed because we neared another point
+ // try to restore
+ point.h1 = m_grabPOriginal.h1;
+ point.h2 = m_grabPOriginal.h2;
+ // and move by same offset
+ // (using update handle in point.setP won't work because the offset between new and old point is very small)
+ point.h1 += QPointF(x, y) - m_grabPOriginal.p;
+ point.h2 += QPointF(x, y) - m_grabPOriginal.p;
- point.p = QPointF(x, y);
+ point.setP(QPointF(x, y), false);
break;
+
case PTypeH2:
leftX = point.p.x();
if (m_currentPointIndex == m_spline.points().count() - 1)
- rightX = 1001;
+ rightX = 5;
else
rightX = m_spline.points()[m_currentPointIndex + 1].p.x();
+
x = qBound(leftX, x, rightX);
- point.h2 = QPointF(x, y);
+ point.setH2(QPointF(x, y));
};
- m_spline.setPoint(m_currentPointIndex, point);
-
- /*if (removePoint && d->m_curve.points().count() > 2) {
- d->m_draggedAwayPoint = d->m_curve.points()[d->m_grab_point_index];
- d->m_draggedAwayPointIndex = d->m_grab_point_index;
- d->m_curve.removePoint(d->m_grab_point_index);
- d->m_grab_point_index = bounds(d->m_grab_point_index, 0, d->m_curve.points().count() - 1);
+ int index = m_currentPointIndex;
+ m_currentPointIndex = m_spline.setPoint(m_currentPointIndex, point);
+
+ if (m_currentPointType == PTypeP) {
+ // we might have changed the handles of other points
+ // try to restore
+ if (index == m_currentPointIndex) {
+ if (m_currentPointIndex > 0)
+ m_spline.setPoint(m_currentPointIndex - 1, m_grabPPrevious);
+ if (m_currentPointIndex < m_spline.points().count() -1)
+ m_spline.setPoint(m_currentPointIndex + 1, m_grabPNext);
+ } else {
+ if (m_currentPointIndex < index) {
+ m_spline.setPoint(index, m_grabPPrevious);
+ m_grabPNext = m_grabPPrevious;
+ if (m_currentPointIndex > 0)
+ m_grabPPrevious = m_spline.points()[m_currentPointIndex - 1];
+ } else {
+ m_spline.setPoint(index, m_grabPNext);
+ m_grabPPrevious = m_grabPNext;
+ if (m_currentPointIndex < m_spline.points().count() - 1)
+ m_grabPNext = m_spline.points()[m_currentPointIndex + 1];
+ }
+ }
}
-
- d->setCurveModified();*/
+
emit currentPoint(point);
update();
}
int i = 0;
double distanceSquared;
+ // find out distance using the Pythagorean theorem
foreach(const BPoint & point, m_spline.points()) {
distanceSquared = pow(point.h1.x() - p.x(), 2) + pow(point.h1.y() - p.y(), 2);
if (distanceSquared < nearestDistanceSquared) {