Arc in QGraphicsScene

心已入冬 提交于 2019-11-27 15:53:09

A Subclass of QGraphicsItem, that takes 3 points, and intersects the three with an arc of a circle. The second point is always in the middle. (Selectablity and other properties haven't been fully implemented or tested).

Note: Qt Creator includes more advanced examples of subclassed QGraphicsItems such as Colliding Mice, and 40,000 chips examples.

http://qt-project.org/doc/qt-5/examples-graphicsview.html

Also to enable QObject signals and slots and properties from a QGraphicsItem, you should use QGraphicsObject.

Note: added onto github here.

arcgraphicsitem.h

#ifndef ARCGRAPHICSITEM_H
#define ARCGRAPHICSITEM_H

#include <QGraphicsItem>
#include <QLineF>
#include <QPointF>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QWidget>

class ArcGraphicsItem : public QGraphicsItem
{

public:
    ArcGraphicsItem();
    ArcGraphicsItem(int i, QPointF point0, QPointF point1, QPointF point2);
    ~ArcGraphicsItem();

    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

    int type() const { return Type;}
    int id() {return m_id;}

    QPainterPath shape() const;
protected:

private:
    void init();

    enum { Type = UserType + 6 };
    int m_id;

    QPointF startP, midP, endP, p1, p2, p3, center;
    QLineF lineBC;
    QLineF lineAC;
    QLineF lineBA;
    QLineF lineOA;
    QLineF lineOB;
    QLineF lineOC;
    QLineF bisectorBC;
    QLineF bisectorBA;
    qreal startAngle;
    qreal spanAngle;

    QRectF circle;
    QRectF boundingRectTemp;
    qreal rad;
};

#endif // ARCGRAPHICSITEM_H

arcgraphicsitem.cpp

#include "arcgraphicsitem.h"
#include "qmath.h"
#include <QPen>
#include <QDebug>
#include <QPainterPath>

ArcGraphicsItem::ArcGraphicsItem(int i,
                                 QPointF point1,
                                 QPointF point2,
                                 QPointF point3)
    : m_id(i), p1(point1), p2(point2), p3(point3)

{
    init();
}


ArcGraphicsItem::ArcGraphicsItem()
{
    p1 = QPointF(0,0);
    p2 = QPointF(0,1);
    p3 = QPointF(1,1);
    m_id = -1;
    init();
}

ArcGraphicsItem::~ArcGraphicsItem()
{

}

void ArcGraphicsItem::init()
{
    lineBC = QLineF(p2, p3);
    lineAC = QLineF(p1, p3);
    lineBA = QLineF(p2, p1);

    rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));

    bisectorBC = QLineF(lineBC.pointAt(0.5), lineBC.p2());
    bisectorBC.setAngle(lineBC.normalVector().angle());

    bisectorBA = QLineF(lineBA.pointAt(0.5), lineBA.p2());
    bisectorBA.setAngle(lineBA.normalVector().angle());
    bisectorBA.intersect(bisectorBC, &center);

    circle = QRectF(center.x() - rad, center.y() - rad, rad*2, rad*2);

    lineOA = QLineF(center, p1);
    lineOB = QLineF(center, p2);
    lineOC = QLineF(center, p3);


    startAngle = lineOA.angle();
    spanAngle = lineOA.angleTo(lineOC);
    // Make sure that the span angle covers all three points with the second point in the middle
    if(qAbs(spanAngle) < qAbs(lineOA.angleTo(lineOB)) || qAbs(spanAngle) < qAbs(lineOB.angleTo(lineOC)))
    {
        // swap the end point and invert the spanAngle
        startAngle = lineOC.angle();
        spanAngle = 360 - spanAngle;
    }

    int w = 10;
    boundingRectTemp = circle.adjusted(-w, -w, w, w);
}

QRectF ArcGraphicsItem::boundingRect() const
{
    // outer most edges
    return boundingRectTemp;
}

void ArcGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QPen paintpen;
    painter->setRenderHint(QPainter::Antialiasing);
    paintpen.setWidth(1);
    // Draw arc

    if(isSelected())
    {
        painter->setBrush(Qt::SolidPattern);
        paintpen.setColor(Qt::black);
        painter->setPen(paintpen);
    }
    else
    {
        paintpen.setStyle(Qt::DashLine);
        paintpen.setColor(Qt::black);
        painter->setPen(paintpen);
    }

    QPainterPath path;
    path.arcMoveTo(circle,startAngle);
    path.arcTo(circle, startAngle, spanAngle);

    // Draw points

    if (isSelected())
    {
        // sets brush for end points
        painter->setBrush(Qt::SolidPattern);
        paintpen.setColor(Qt::red);
        painter->setPen(paintpen);

        qreal ptRad = 10;
        painter->drawEllipse(p1, ptRad, ptRad);
        painter->drawEllipse(p2, ptRad, ptRad);
        painter->drawEllipse(p3, ptRad, ptRad);
    }

    painter->setBrush(Qt::NoBrush);
    painter->drawPath(path);
}

QPainterPath ArcGraphicsItem::shape() const
{
    QPainterPath path;
    path.arcMoveTo(circle,startAngle);
    path.arcTo(circle, startAngle, spanAngle);
    return path;
}

Hope that helps

phyatt

This sounds like it could be solved with some relatively simple math:

https://www.google.com/search?q=define%20circle%20three%20points

https://math.stackexchange.com/a/213678

https://www.khanacademy.org/math/geometry/triangle-properties/perpendicular_bisectors/v/three-points-defining-a-circle

Here is my translation of the math into Qt goodness

// m_points is a QList<QPointF>
// use math to define the circle
QLineF lineBC(m_points.at(1), m_points.at(2));
QLineF lineAC(m_points.at(0), m_points.at(2));
QLineF lineBA(m_points.at(1), m_points.at(0));
qreal rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));

QLineF bisectorBC(lineBC.pointAt(0.5), lineBC.p2());
bisectorBC.setAngle(lineBC.normalVector().angle());

QLineF bisectorBA(lineBA.pointAt(0.5), lineBA.p2());
bisectorBA.setAngle(lineBA.normalVector().angle());

QPointF center;
bisectorBA.intersect(bisectorBC, &center);

qDebug() << rad << center;

QT QGraphicsScene Drawing Arc

QPainterPath* path = new QPainterPath();
path->arcMoveTo(0,0,50,50,20);
path->arcTo(0,0,50,50,20, 90);
scene.addPath(*path);

Putting all of this together into a nice little project turns into this:

https://github.com/peteristhegreat/ThreePointsCircle

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include <QGraphicsView>
#include <QGraphicsScene>
#include "graphicsscene.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QGraphicsView * view = new QGraphicsView;

    GraphicsScene * scene = new GraphicsScene();
    view->setScene(scene);

    view->setSceneRect(-300,-300, 300, 300);
    this->resize(600, 600);

    this->setCentralWidget(view);
}

MainWindow::~MainWindow()
{

}

graphicsscene.h

#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H

#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPointF>
#include <QList>

class GraphicsScene : public QGraphicsScene
{
    Q_OBJECT
public:
    explicit GraphicsScene(QObject *parent = 0);
    virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * mouseEvent);
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent * mouseEvent);
    virtual void mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent);
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent * mouseEvent);
signals:

public slots:

private:
    QList <QPointF> m_points;
};

#endif // GRAPHICSSCENE_H

graphicsscene.cpp

#include "graphicsscene.h"
#include <QDebug>
#include <QGraphicsEllipseItem>
#include <QGraphicsPathItem>
#include <QPainterPath>
#include "qmath.h"

GraphicsScene::GraphicsScene(QObject *parent) :
    QGraphicsScene(parent)
{
    this->setBackgroundBrush(Qt::gray);
}

void GraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * mouseEvent)
{
    qDebug() << Q_FUNC_INFO << mouseEvent->scenePos();
    QGraphicsScene::mouseDoubleClickEvent(mouseEvent);
}

void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent * mouseEvent)
{
    qDebug() << Q_FUNC_INFO << mouseEvent->scenePos();
    QGraphicsScene::mouseMoveEvent(mouseEvent);
}

void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent)
{
    qDebug() << Q_FUNC_INFO << mouseEvent->scenePos();
    QGraphicsScene::mousePressEvent(mouseEvent);
}

void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent * me)
{
    qDebug() << Q_FUNC_INFO << me->scenePos();
    int radius = 20;
    QGraphicsEllipseItem * ellipse = this->addEllipse(me->scenePos().x() - radius, me->scenePos().y() - radius, radius*2, radius*2);

    ellipse->setBrush(Qt::white);
    m_points.append(me->scenePos());
    if(m_points.size() == 3)
    {
        // use math to define the circle
        QLineF lineBC(m_points.at(1), m_points.at(2));
        QLineF lineAC(m_points.at(0), m_points.at(2));
        QLineF lineBA(m_points.at(1), m_points.at(0));
        qreal rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));

        QLineF bisectorBC(lineBC.pointAt(0.5), lineBC.p2());
        bisectorBC.setAngle(lineBC.normalVector().angle());

        QLineF bisectorBA(lineBA.pointAt(0.5), lineBA.p2());
        bisectorBA.setAngle(lineBA.normalVector().angle());

        QPointF center;
        bisectorBA.intersect(bisectorBC, &center);

        qDebug() << rad << center;

        bool drawCircle = true;

        QGraphicsEllipseItem * ellipse = new QGraphicsEllipseItem(center.x() - rad, center.y() - rad, rad*2, rad*2);
        if(drawCircle)
            this->addItem(ellipse);

        // add arc
        // this->addItem(path);
        QPainterPath path;
        QLineF lineOA(center, m_points.at(0));
        QLineF lineOC(center, m_points.at(2));
        path.arcMoveTo(ellipse->boundingRect(),lineOA.angle());
        path.arcTo(ellipse->boundingRect(), lineOA.angle(), lineOC.angle() - lineOA.angle());
        QGraphicsPathItem * pathItem = new QGraphicsPathItem(path);
        pathItem->setPen(QPen(Qt::red,10));
        this->addItem(pathItem);

        if(!drawCircle)
            delete ellipse;
        m_points.clear();
    }

    QGraphicsScene::mouseReleaseEvent(me);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!