DoodlePage.h 5.47 KB
#ifndef DOODLEPAGE_H
#define DOODLEPAGE_H
#include <QPoint>
#include <QSize>
#include <math.h>
#include <QColor>
#include <QTime>
#include <QWidget>

#include <stdlib.h>
namespace JusDoodle {

// Doodle Point
// X, Y is in logical coordinate system, value from -1.0 to +1.0;
// Origin point is the midpoint of page.
class Point
{
public:
    Point(double x, double y, int intvalMs = 0)
        : _x(x)
        , _y(y)
        , _intvalMs(intvalMs)
    {
    }

    // Construct from physical coordinates
    Point(const QSize &size, const QPoint &point, int intvalMs = 0)
    {
        _x = (point.x() * 2.0 - size.width()) / size.width();
        _y = (point.y() * 2.0 - size.height()) / size.height();
        _intvalMs = intvalMs;
    }

    // Construct from physical coordinates
    Point(const QSize &size, const QPointF &point, int intvalMs = 0)
    {
        _x = (point.x() * 2.0 - size.width()) / size.width();
        _y = (point.y() * 2.0 - size.height()) / size.height();
        _intvalMs = intvalMs;
    }

    // Convert to physical coordinates
    void toQPoint(const QSize &size, QPoint &point) const
    {
        point.setX(floor((_x * size.width() + size.width()) / 2));
        point.setY(floor((_y * size.height() + size.height()) / 2));
    }

    // Convert to physical coordinates
    void toQPointF(const QSize &size, QPointF &point) const
    {
        point.setX((_x * size.width() + size.width()) / 2);
        point.setY((_y * size.height() + size.height()) / 2);
    }

public:
    double _x;
    double _y;
    int _intvalMs;
};

// Path
// List of points;
typedef std::list<Point> Path;

// Line
// A line contains path, color, width
// Width is ratio of line width and page width.
class Line
{
public:
    Line(const QColor &color, double width, bool erase, const QSize &size, bool mine)
        : _path()
        , _color(color)
        , _width(width)
        , _erase(erase)
        , _size(size)
        , _lastTs()
        , _mine(mine)
    {
    }

    Line(const QColor &color, double width, bool erase, const Path &path)
        : _path(path)
        , _color(color)
        , _width(width)
        , _erase(erase)
        , _size(QSize())
        , _lastTs(QTime())
        , _mine(false)
    {
    }

    void addPoint(const QPoint &point)
    {
        QTime nowTs = QTime::currentTime();
        if (_lastTs.isNull())
            _path.push_back(Point(_size, point));
        else
            _path.push_back(Point(_size, point, _lastTs.msecsTo(nowTs)));
        _lastTs = nowTs;
    }

    void addPoint(const QPointF &point)
    {
        QTime nowTs = QTime::currentTime();
        if (_lastTs.isNull())
            _path.push_back(Point(_size, point));
        else
            _path.push_back(Point(_size, point, _lastTs.msecsTo(nowTs)));
        _lastTs = nowTs;
    }

public:
    Path _path;
    QColor _color;
    double _width;
    bool _erase;
    QSize _size;
    QTime _lastTs;
    bool _mine;
};

class DoodlePage : public QWidget
{
    Q_OBJECT

public:
    explicit DoodlePage(int pageId, QWidget *parent = 0);
    ~DoodlePage();

    // Set background image.
    void setBackground(const QString &imagePath);

    // Set next drawing line color.
    void setColor(const QColor &color);

    // Set next drawing line width.
    void setWidth(double width);

    // Set next action mode.
    void setMode(bool erase);

    // Draw a line.
    void draw(const QPoint &from, const QPoint &to, const QColor &color, 
        double width);

    // Draw a path.
    void draw(const Path &path, const QColor &color, 
        double width);

    // Erase a line.
    void erase(const QPoint &from, const QPoint &to, double width);

    // Erase a path.
    void erase(const Path &path, double width);

    // Clear the page.
    void clear();

public Q_SLOTS:
    // Process of color changed.
    void onColorChanged(const QColor &color);

    // Process of width changed.
    void onWidthChanged(double width);

    // Process of mode changed.
    void onModeChanged(bool erase);

    void setDrawFlag(bool flag);

Q_SIGNALS:
    // Emit after draw a line.
    void notifyDrawPath(int pageId, const Path &path,
        const QColor &color, double width);

    // Emit after erase a line.
    void notifyErasePath(int pageId, const Path &path,
        double width);

protected:
    // Override resize event. Redraw the content.
    void resizeEvent(QResizeEvent *event);

    // Override move event. Redraw the content.
    void moveEvent(QMoveEvent *event);

    // Override show event. Redraw the content.
    void showEvent(QShowEvent *event);

    // Paint the content.
    void paintEvent(QPaintEvent *event);

    // Override move down event. Start track.
    void mousePressEvent(QMouseEvent *event);

    // Override move move event. Track points.
    void mouseMoveEvent(QMouseEvent *event);

    // Override move up event. Stop track.
    void mouseReleaseEvent(QMouseEvent *event);

private:
    // Paint the background image.
    void paintBackground(QPainter *painter);

    // Paint lines.
    void paintLines(QPainter *painter);

    // Paint one line.
    void paintLine(QPainter *painter, const Line &line);

    // Start track
    void trackStart(const QPointF &point);

    // Record point
    void trackPoint(const QPointF &point);

    // Stop track
    void trackStop(const QPointF &point);

private:
    int _pageId;
    QColor _color;
    double _width;
    bool _erase;
    QImage *_background;
    std::list<Line> _lines;
    Line *_track;
    bool _clear;
    bool _redraw;
    bool drawFlag;
};

}

#endif // DOODLEPAGE_H