diff --git a/resources/forms/brushProperties.ui b/resources/forms/brushProperties.ui index 19883e90..eea50fba 100644 --- a/resources/forms/brushProperties.ui +++ b/resources/forms/brushProperties.ui @@ -1,7 +1,8 @@ - + + brushProperties - - + + 0 0 @@ -9,25 +10,25 @@ 808 - + - + QFrame::NoFrame - + QFrame::Plain - - - - + + + + Qt::Vertical - + QSizePolicy::Fixed - + 20 40 @@ -35,53 +36,40 @@ - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - + + + QFrame::NoFrame - + QFrame::Raised - - + + 0 - - + + QFrame::NoFrame - + QFrame::Raised - + - - + + On Light Background - - + + Qt::Horizontal - + 40 20 @@ -90,17 +78,17 @@ - - + + 32 32 - + QFrame::StyledPanel - + QFrame::Raised @@ -109,27 +97,27 @@ - - + + QFrame::NoFrame - + QFrame::Raised - + - - + + On Dark Background - - + + Qt::Horizontal - + 40 20 @@ -138,17 +126,17 @@ - - + + 32 32 - + QFrame::StyledPanel - + QFrame::Raised @@ -159,34 +147,21 @@ - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - + + + QFrame::NoFrame - + QFrame::Raised - + - - + + Qt::Horizontal - + 154 20 @@ -195,40 +170,40 @@ - - + + Opacity - - + + 20 - + 100 - + 50 - + Qt::Horizontal - + QSlider::TicksAbove - + 20 - - + + Qt::Horizontal - + 156 20 @@ -239,40 +214,27 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - + + + + 0 0 - + Line Width - - - - + + + + Qt::Horizontal - + QSizePolicy::Fixed - + 40 20 @@ -280,15 +242,15 @@ - - - + + + Qt::Horizontal - + QSizePolicy::Fixed - + 40 20 @@ -296,149 +258,149 @@ - - - + + + 5 - + 500 - + 5 - + Qt::Horizontal - + QSlider::TicksAbove - + 100 - - - + + + Medium - - - + + + 5 - + 500 - + Qt::Horizontal - + QSlider::TicksAbove - + 100 - - - + + + Strong - - - + + + 5 - + 500 - + Qt::Horizontal - + QSlider::TicksAbove - + 100 - - - - + + + + 60 60 - + 60 60 - + QFrame::StyledPanel - + QFrame::Raised - - - - + + + + 60 60 - + 60 60 - + QFrame::StyledPanel - + QFrame::Raised - - - - + + + + 60 60 - + 60 60 - + QFrame::StyledPanel - + QFrame::Raised - - - + + + Fine @@ -446,50 +408,85 @@ - - - + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + QFrame::NoFrame - + QFrame::Raised - - + + + 0 + + + 0 + + + 0 + + 0 - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - + + + 0 0 - + Pen is Pressure Sensitive - - + + Qt::Horizontal - + 198 20 @@ -500,6 +497,30 @@ + + + + + + Smooth strokes (experimental) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/src/core/UBPreferencesController.cpp b/src/core/UBPreferencesController.cpp index 9fef16a1..7f87cdda 100644 --- a/src/core/UBPreferencesController.cpp +++ b/src/core/UBPreferencesController.cpp @@ -165,6 +165,7 @@ void UBPreferencesController::wire() connect(mPenProperties->mediumSlider, SIGNAL(valueChanged(int)), this, SLOT(widthSliderChanged(int))); connect(mPenProperties->strongSlider, SIGNAL(valueChanged(int)), this, SLOT(widthSliderChanged(int))); connect(mPenProperties->pressureSensitiveCheckBox, SIGNAL(clicked(bool)), settings, SLOT(setPenPressureSensitive(bool))); + connect(mPenProperties->interpolateStrokesCheckBox, SIGNAL(clicked(bool)), settings->boardInterpolatePenStrokes, SLOT(setBool(bool))); // marker QList markerLightBackgroundColors = settings->boardMarkerLightBackgroundColors->colors(); @@ -231,6 +232,7 @@ void UBPreferencesController::init() mPenProperties->mediumSlider->setValue(settings->boardPenMediumWidth->get().toDouble() * sSliderRatio); mPenProperties->strongSlider->setValue(settings->boardPenStrongWidth->get().toDouble() * sSliderRatio); mPenProperties->pressureSensitiveCheckBox->setChecked(settings->boardPenPressureSensitive->get().toBool()); + mPenProperties->interpolateStrokesCheckBox->setChecked(settings->boardInterpolatePenStrokes->get().toBool()); // marker tab mMarkerProperties->fineSlider->setValue(settings->boardMarkerFineWidth->get().toDouble() * sSliderRatio); diff --git a/src/core/UBSettings.cpp b/src/core/UBSettings.cpp index 47177e89..ad9bc861 100644 --- a/src/core/UBSettings.cpp +++ b/src/core/UBSettings.cpp @@ -269,6 +269,8 @@ void UBSettings::init() boardUseHighResTabletEvent = new UBSetting(this, "Board", "UseHighResTabletEvent", true); + boardInterpolatePenStrokes = new UBSetting(this, "Board", "InterpolatePenStrokes", true); + boardKeyboardPaletteKeyBtnSize = new UBSetting(this, "Board", "KeyboardPaletteKeyBtnSize", "16x16"); ValidateKeyboardPaletteKeyBtnSize(); diff --git a/src/core/UBSettings.h b/src/core/UBSettings.h index 0d874471..b0bf73ca 100644 --- a/src/core/UBSettings.h +++ b/src/core/UBSettings.h @@ -273,6 +273,8 @@ class UBSettings : public QObject UBSetting* boardUseHighResTabletEvent; + UBSetting* boardInterpolatePenStrokes; + UBSetting* boardKeyboardPaletteKeyBtnSize; UBSetting* appStartMode; diff --git a/src/domain/UBGraphicsScene.cpp b/src/domain/UBGraphicsScene.cpp index 01918dc3..acd54ee7 100644 --- a/src/domain/UBGraphicsScene.cpp +++ b/src/domain/UBGraphicsScene.cpp @@ -25,6 +25,7 @@ */ +#include #include "UBGraphicsScene.h" @@ -37,6 +38,7 @@ #include "frameworks/UBGeometryUtils.h" #include "frameworks/UBPlatformUtils.h" +#include "frameworks/UBInterpolator.h" #include "core/UBApplication.h" #include "core/UBSettings.h" @@ -335,6 +337,7 @@ UBGraphicsScene::UBGraphicsScene(UBDocumentProxy* parent, bool enableUndoRedoSta , mZLayerController(new UBZLayerController(this)) , mpLastPolygon(NULL) , mCurrentPolygon(0) + , mTempPolygon(NULL) , mSelectionFrame(0) { UBCoreGraphicsScene::setObjectName("BoardScene"); @@ -434,6 +437,8 @@ bool UBGraphicsScene::inputDevicePress(const QPointF& scenePos, const qreal& pre else { moveTo(scenePos); drawLineTo(scenePos, width, UBDrawingController::drawingController()->stylusTool() == UBStylusTool::Line); + + mCurrentStroke->addPoint(scenePos); } accepted = true; } @@ -537,11 +542,47 @@ bool UBGraphicsScene::inputDeviceMove(const QPointF& scenePos, const qreal& pres position = newPosition; } + if (!mCurrentStroke) + mCurrentStroke = new UBGraphicsStroke(); + if(dc->mActiveRuler){ dc->mActiveRuler->DrawLine(position, width); } else{ - drawLineTo(position, width, UBDrawingController::drawingController()->stylusTool() == UBStylusTool::Line); + UBInterpolator::InterpolationMethod interpolator = UBInterpolator::NoInterpolation; + + /* + if (currentTool == UBStylusTool::Marker) { + // The marker is already super slow due to the transparency, we can't also do interpolation + interpolator = UBInterpolator::NoInterpolation; + } + */ + + if (UBSettings::settings()->boardInterpolatePenStrokes->get().toBool()) { + interpolator = UBInterpolator::Bezier; + } + + QList newPoints = mCurrentStroke->addPoint(scenePos, interpolator); + if (newPoints.length() > 1) { + drawCurve(newPoints, mPreviousWidth, width); + } + + if (interpolator == UBInterpolator::Bezier) { + // Bezier curves aren't drawn all the way to the scenePos (they stop halfway between the previous and + // current scenePos), so we add a line from the last drawn position in the stroke and the + // scenePos, to make the drawing feel more responsive. This line is then deleted if a new segment is + // added to the stroke. (Or it is added to the stroke when we stop drawing) + + if (mTempPolygon) { + removeItem(mTempPolygon); + mTempPolygon = NULL; + } + + QPointF lastDrawnPoint = newPoints.last(); + + mTempPolygon = lineToPolygonItem(QLineF(lastDrawnPoint, scenePos), mPreviousWidth, width); + addItem(mTempPolygon); + } } } else if (currentTool == UBStylusTool::Eraser) @@ -610,6 +651,13 @@ bool UBGraphicsScene::inputDeviceRelease() mDrawWithCompass = false; } else if (mCurrentStroke){ + if (mTempPolygon) { + UBGraphicsPolygonItem * poly = dynamic_cast(mTempPolygon->deepCopy()); + removeItem(mTempPolygon); + mTempPolygon = NULL; + addPolygonItemToCurrentStroke(poly); + } + UBGraphicsStrokesGroup* pStrokes = new UBGraphicsStrokesGroup(); // Remove the strokes that were just drawn here and replace them by a stroke item @@ -775,16 +823,51 @@ void UBGraphicsScene::moveTo(const QPointF &pPoint) mArcPolygonItem = 0; mDrawWithCompass = false; } - void UBGraphicsScene::drawLineTo(const QPointF &pEndPoint, const qreal &pWidth, bool bLineStyle) +{ + drawLineTo(pEndPoint, pWidth, pWidth, bLineStyle); + +} + +void UBGraphicsScene::drawLineTo(const QPointF &pEndPoint, const qreal &startWidth, const qreal &endWidth, bool bLineStyle) { if (mPreviousWidth == -1.0) - mPreviousWidth = pWidth; + mPreviousWidth = startWidth; + + qreal initialWidth = startWidth; + if (initialWidth == endWidth) + initialWidth = mPreviousWidth; + + if (bLineStyle) { + QSetIterator itItems(mAddedItems); + + while (itItems.hasNext()) { + QGraphicsItem* item = itItems.next(); + removeItem(item); + } + mAddedItems.clear(); + } - // UBGraphicsPolygonItem *polygonItem = lineToPolygonItem(QLineF(mPreviousPoint, pEndPoint), pWidth); + UBGraphicsPolygonItem *polygonItem = lineToPolygonItem(QLineF(mPreviousPoint, pEndPoint), initialWidth, endWidth); + addPolygonItemToCurrentStroke(polygonItem); - UBGraphicsPolygonItem *polygonItem = lineToPolygonItem(QLineF(mPreviousPoint, pEndPoint), mPreviousWidth,pWidth); + if (!bLineStyle) { + mPreviousPoint = pEndPoint; + mPreviousWidth = endWidth; + } +} + +void UBGraphicsScene::drawCurve(const QList& points, qreal startWidth, qreal endWidth) +{ + UBGraphicsPolygonItem* polygonItem = curveToPolygonItem(points, startWidth, endWidth); + addPolygonItemToCurrentStroke(polygonItem); + + mPreviousWidth = endWidth; + mPreviousPoint = points.last(); +} +void UBGraphicsScene::addPolygonItemToCurrentStroke(UBGraphicsPolygonItem* polygonItem) +{ if (!polygonItem->brush().isOpaque()) { // ------------------------------------------------------------------------------------- @@ -797,19 +880,6 @@ void UBGraphicsScene::drawLineTo(const QPointF &pEndPoint, const qreal &pWidth, } } - - if (bLineStyle) - { - QSetIterator itItems(mAddedItems); - - while (itItems.hasNext()) - { - QGraphicsItem* item = itItems.next(); - removeItem(item); - } - mAddedItems.clear(); - } - mpLastPolygon = polygonItem; mAddedItems.insert(polygonItem); @@ -822,12 +892,6 @@ void UBGraphicsScene::drawLineTo(const QPointF &pEndPoint, const qreal &pWidth, mPreviousPolygonItems.append(polygonItem); - - if (!bLineStyle) - { - mPreviousPoint = pEndPoint; - mPreviousWidth = pWidth; - } } void UBGraphicsScene::eraseLineTo(const QPointF &pEndPoint, const qreal &pWidth) @@ -1087,6 +1151,14 @@ UBGraphicsPolygonItem* UBGraphicsScene::arcToPolygonItem(const QLineF& pStartRad return polygonToPolygonItem(polygon); } +UBGraphicsPolygonItem* UBGraphicsScene::curveToPolygonItem(const QList& points, qreal startWidth, qreal endWidth) +{ + QPolygonF polygon = UBGeometryUtils::curveToPolygon(points, startWidth, endWidth); + + return polygonToPolygonItem(polygon); + +} + void UBGraphicsScene::clearSelectionFrame() { if (mSelectionFrame) { diff --git a/src/domain/UBGraphicsScene.h b/src/domain/UBGraphicsScene.h index 9d6d9500..05674873 100644 --- a/src/domain/UBGraphicsScene.h +++ b/src/domain/UBGraphicsScene.h @@ -193,8 +193,10 @@ class UBGraphicsScene: public UBCoreGraphicsScene, public UBItem void moveTo(const QPointF& pPoint); void drawLineTo(const QPointF& pEndPoint, const qreal& pWidth, bool bLineStyle); + void drawLineTo(const QPointF& pEndPoint, const qreal& pStartWidth, const qreal& endWidth, bool bLineStyle); void eraseLineTo(const QPointF& pEndPoint, const qreal& pWidth); void drawArcTo(const QPointF& pCenterPoint, qreal pSpanAngle); + void drawCurve(const QList& points, qreal startWidth, qreal endWidth); bool isEmpty() const; @@ -366,6 +368,8 @@ public slots: UBGraphicsPolygonItem* lineToPolygonItem(const QLineF &pLine, const qreal &pStartWidth, const qreal &pEndWidth); UBGraphicsPolygonItem* arcToPolygonItem(const QLineF& pStartRadius, qreal pSpanAngle, qreal pWidth); + UBGraphicsPolygonItem* curveToPolygonItem(const QList& points, qreal startWidth, qreal endWidth); + void addPolygonItemToCurrentStroke(UBGraphicsPolygonItem* polygonItem); void initPolygonItem(UBGraphicsPolygonItem*); @@ -450,6 +454,7 @@ public slots: UBZLayerController *mZLayerController; UBGraphicsPolygonItem* mpLastPolygon; + UBGraphicsPolygonItem* mTempPolygon; bool mDrawWithCompass; UBGraphicsPolygonItem *mCurrentPolygon; diff --git a/src/domain/UBGraphicsStroke.cpp b/src/domain/UBGraphicsStroke.cpp index 7e124a11..f20b680b 100644 --- a/src/domain/UBGraphicsStroke.cpp +++ b/src/domain/UBGraphicsStroke.cpp @@ -31,11 +31,14 @@ #include "UBGraphicsPolygonItem.h" +#include "board/UBBoardController.h" +#include "core/UBApplication.h" #include "core/memcheck.h" + UBGraphicsStroke::UBGraphicsStroke() { - // NOOP + mAntiScaleRatio = 1./(UBApplication::boardController->systemScaleFactor() * UBApplication::boardController->currentZoom()); } @@ -63,6 +66,76 @@ QList UBGraphicsStroke::polygons() const return mPolygons; } +/** + * @brief Add a point to the curve, interpolating extra points if required + * @return The points (or point, if none were interpolated) that were added + */ +QList UBGraphicsStroke::addPoint(const QPointF& point, UBInterpolator::InterpolationMethod interpolationMethod) +{ + int n = mReceivedPoints.size(); + + if (n == 0) { + mReceivedPoints << point; + mDrawnPoints << point; + return QList(); + } + + if (interpolationMethod == UBInterpolator::NoInterpolation) { + QPointF lastPoint = mReceivedPoints.last(); + mReceivedPoints << point; + mDrawnPoints << point; + return QList() << lastPoint << point; + } + + else if (interpolationMethod == UBInterpolator::Bezier) { + // This is a bit special, as the curve we are interpolating is not between two drawn points; + // it is between the midway points of the second-to-last and last point, and last and current point. + + // Don't draw segments smaller than a certain length. This can help with performance + // (less polygons in a stroke) but mostly with keeping the curve smooth. + qreal MIN_DISTANCE = 5*mAntiScaleRatio; + qreal distance = QLineF(mReceivedPoints.last(), point).length(); + + if (distance < MIN_DISTANCE) { + return QList() << mDrawnPoints.last(); + } + + // The first segment is just a straight line to the first midway point + if (n == 1) { + QPointF lastPoint = mReceivedPoints[0]; + mReceivedPoints << point; + mDrawnPoints << QPointF((lastPoint + point)/2.0); + + return QList() << lastPoint << ((lastPoint + point)/2.0); + } + + QPointF p0 = mReceivedPoints[mReceivedPoints.size() - 2]; + QPointF p1 = mReceivedPoints[mReceivedPoints.size() - 1]; + QPointF p2 = point; + + UBQuadraticBezier bz; + + QPointF startPoint = (p1+p0)/2.0; + QPointF endPoint = (p2+p1)/2.0; + + bz.setPoints(startPoint, p1, endPoint); + + QList newPoints = bz.getPoints(10); + + // avoid adding duplicates + if (newPoints.first() == mDrawnPoints.last()) + mDrawnPoints.removeLast(); + + foreach(QPointF p, newPoints) { + mDrawnPoints << p; + } + + mReceivedPoints << point; + return newPoints; + } + + return QList(); +} bool UBGraphicsStroke::hasPressure() { @@ -106,4 +179,3 @@ void UBGraphicsStroke::clear() mPolygons.clear(); } } - diff --git a/src/domain/UBGraphicsStroke.h b/src/domain/UBGraphicsStroke.h index 4b6759d6..8c332498 100644 --- a/src/domain/UBGraphicsStroke.h +++ b/src/domain/UBGraphicsStroke.h @@ -33,6 +33,8 @@ #include #include "core/UB.h" +#include "frameworks/UBInterpolator.h" + class UBGraphicsPolygonItem; @@ -57,6 +59,10 @@ class UBGraphicsStroke void clear(); + QList addPoint(const QPointF& point, UBInterpolator::InterpolationMethod interpolationMethod = UBInterpolator::NoInterpolation); + + const QList& points() { return mDrawnPoints; } + protected: void addPolygon(UBGraphicsPolygonItem* pol); @@ -64,6 +70,13 @@ class UBGraphicsStroke QList mPolygons; + /// Points that were drawn by the user (i.e, actually received through input device) + QList mReceivedPoints; + + /// All the points (including interpolated) that are used to draw the stroke + QList mDrawnPoints; + + qreal mAntiScaleRatio; }; #endif /* UBGRAPHICSSTROKE_H_ */ diff --git a/src/frameworks/UBGeometryUtils.cpp b/src/frameworks/UBGeometryUtils.cpp index a453e9b8..f42f6684 100644 --- a/src/frameworks/UBGeometryUtils.cpp +++ b/src/frameworks/UBGeometryUtils.cpp @@ -244,6 +244,111 @@ QPolygonF UBGeometryUtils::arcToPolygon(const QLineF& startRadius, qreal spanAng return painterPath.toFillPolygon(); } +/** + * @brief Build and return a polygon from a list of points (at least 2), and start and end widths. + * + * The resulting polygon will pass by all points in the curve; its thickness is calculated at each point + * of the curve (linearly interpolated between start and end widths) and the segments are joined by + * (approximately) curved joints. + * + * Like with lineToPolygon, the ends are semi-circular. + */ +QPolygonF UBGeometryUtils::curveToPolygon(const QList& points, qreal startWidth, qreal endWidth) +{ + typedef QPair pointPair; + + int n_points = points.size(); + + if (n_points < 2) + return QPolygonF(); + + if (n_points == 2) + return lineToPolygon(points[0], points[1], startWidth, endWidth); + + /* The vertices (x's) are calculated based on the stroke's width and angle, and the position of the + supplied points (o's): + + x----------x--------x + + o o o + + x----------x -------x + + The vertices above and below each 'o' point are temporarily stored together, + as a pair of points. + + */ + QList newPoints; + + + QLineF firstSegment = QLineF(points[0], points[1]); + QLineF normal = firstSegment.normalVector(); + normal.setLength(startWidth/2.0); + newPoints << pointPair(normal.p2(), points[0] - QPointF(normal.dx(), normal.dy())); + + /* + Calculating the vertices (d1 and d2, below) is a little less trivial for the + next points: their positions depend on the angle between one segment and the next. + + d1 + ------------x + \ + .a b . \ + \ + --------x \ + d2 \ \ + \ .c \ + + Here, points a, b and c are supplied in the `points` list. + + N.B: The drawing isn't quite accurate; we don't do a miter joint but a kind + of rounded-off joint (the distance between b and d1 is half the width of the stroke) + */ + + for (int i(1); i < n_points-1; ++i) { + qreal width = startWidth + (qreal(i)/qreal(n_points-1)) * (endWidth - startWidth); + + QLineF normal = (QLineF(points[i-1], points[i+1])).normalVector(); + normal.setLength(width/2.0); + QPointF d1 = points[i] + QPointF(normal.dx(), normal.dy()); + QPointF d2 = points[i] - QPointF(normal.dx(), normal.dy()); + + newPoints << pointPair(d1, d2); + } + + // The last point is similar to the first + QLineF lastSegment = QLineF(points[n_points-2], points[n_points-1]); + normal = lastSegment.normalVector(); + normal.setLength(endWidth/2.0); + + QPointF d1 = points.last() + QPointF(normal.dx(), normal.dy()); + QPointF d2 = points.last() - QPointF(normal.dx(), normal.dy()); + + newPoints << pointPair(d1, d2); + + QPainterPath path; + path.moveTo(newPoints[0].first); + + for (int i(1); i < n_points; ++i) { + path.lineTo(newPoints[i].first); + } + + path.arcTo(points.last().x() - endWidth/2.0, points.last().y() - endWidth/2.0, endWidth, endWidth, (90.0 + lastSegment.angle()), -180.0); + //path.lineTo(newPoints.last().second); + + for (int i(n_points-1); i >= 0; --i) { + path.lineTo(newPoints[i].second); + } + + path.arcTo(points[0].x() - startWidth/2.0, points[0].y() - startWidth/2.0, startWidth, startWidth, (firstSegment.angle() - 90.0), -180.0); + //path.lineTo(newPoints[0].second); + + + path.closeSubpath(); + + return path.toFillPolygon(); +} + QPointF UBGeometryUtils::pointConstrainedInRect(QPointF point, QRectF rect) { return QPointF(qMax(rect.x(), qMin(rect.x() + rect.width(), point.x())), qMax(rect.y(), qMin(rect.y() + rect.height(), point.y()))); diff --git a/src/frameworks/UBGeometryUtils.h b/src/frameworks/UBGeometryUtils.h index 83832401..6d0aeff1 100644 --- a/src/frameworks/UBGeometryUtils.h +++ b/src/frameworks/UBGeometryUtils.h @@ -47,6 +47,7 @@ class UBGeometryUtils static QPolygonF lineToPolygon(const QPointF& pStart, const QPointF& pEnd, const qreal& pStartWidth, const qreal& pEndWidth); + static QPolygonF curveToPolygon(const QList& points, qreal startWidth, qreal endWidth); static QPointF pointConstrainedInRect(QPointF point, QRectF rect); static QPoint pointConstrainedInRect(QPoint point, QRect rect); diff --git a/src/frameworks/UBInterpolator.cpp b/src/frameworks/UBInterpolator.cpp new file mode 100644 index 00000000..b5777bcd --- /dev/null +++ b/src/frameworks/UBInterpolator.cpp @@ -0,0 +1,53 @@ +#include "UBInterpolator.h" + +UBInterpolator::UBInterpolator() +{ +} + +UBInterpolator::~UBInterpolator() +{ +} + +UBQuadraticBezier::UBQuadraticBezier() +{ + mPath = 0; +} + +UBQuadraticBezier::~UBQuadraticBezier() +{ + if (mPath) + delete mPath; +} + +void UBQuadraticBezier::setPoints(QList points) +{ + setPoints(points[0], points[1], points[2]); + +} + +void UBQuadraticBezier::setPoints(QPointF start, QPointF control, QPointF end) +{ + mPath = new QPainterPath(start); + mPath->quadTo(control, end); +} + +/** + * @brief Return n points along the curve, including start and end points (thus n should be larger than or equal to 2). + * + * The higher n, the more accurate the result + */ +QList UBQuadraticBezier::getPoints(int n) +{ + QList points; + + if (n <= 1) + return points; + + for (int i(0); i <= n; ++i) { + qreal percent = qreal(i)/qreal(n); + + points << mPath->pointAtPercent(percent); + } + + return points; +} diff --git a/src/frameworks/UBInterpolator.h b/src/frameworks/UBInterpolator.h new file mode 100644 index 00000000..9a714488 --- /dev/null +++ b/src/frameworks/UBInterpolator.h @@ -0,0 +1,45 @@ +#ifndef UBINTERPOLATOR_H +#define UBINTERPOLATOR_H + +#include + +class UBInterpolator +{ + /* Abstract class representing an interpolator */ + +public: + enum InterpolationMethod { + NoInterpolation, + //SimpleSpline, + //CatmullRom, + Bezier + }; + + UBInterpolator(); + virtual ~UBInterpolator(); + + virtual void setPoints(QList points) = 0; + //virtual double y(double x) {} + +}; + +class UBQuadraticBezier : public UBInterpolator +{ + +public: + UBQuadraticBezier(); + virtual ~UBQuadraticBezier(); + + virtual void setPoints(QList points); + void setPoints(QPointF start, QPointF control, QPointF end); + + //virtual double y(double x); + + QList getPoints(int n); + +private: + + QPainterPath* mPath; +}; + +#endif // UBINTERPOLATOR_H diff --git a/src/frameworks/frameworks.pri b/src/frameworks/frameworks.pri index 74d6edbb..8d6d435d 100644 --- a/src/frameworks/frameworks.pri +++ b/src/frameworks/frameworks.pri @@ -6,7 +6,8 @@ HEADERS += src/frameworks/UBGeometryUtils.h \ src/frameworks/UBVersion.h \ src/frameworks/UBCoreGraphicsScene.h \ src/frameworks/UBCryptoUtils.h \ - src/frameworks/UBBase32.h + src/frameworks/UBBase32.h \ + $$PWD/UBInterpolator.h SOURCES += src/frameworks/UBGeometryUtils.cpp \ src/frameworks/UBPlatformUtils.cpp \ @@ -15,7 +16,8 @@ SOURCES += src/frameworks/UBGeometryUtils.cpp \ src/frameworks/UBVersion.cpp \ src/frameworks/UBCoreGraphicsScene.cpp \ src/frameworks/UBCryptoUtils.cpp \ - src/frameworks/UBBase32.cpp + src/frameworks/UBBase32.cpp \ + $$PWD/UBInterpolator.cpp win32 {