GraphicsScene中的轴承公式计算会产生不稳定的结果 [英] Bearing Formula calculations in GraphicsScene producing erratic results

查看:50
本文介绍了GraphicsScene中的轴承公式计算会产生不稳定的结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用基本的轴承计算得出的结果非常难以预测.我需要做的是,当我根据刻度盘的角度绘制这些线时,这些线需要均匀地绘制到视图的中心.现在,我得到的结果很不稳定,我希望这里的人可以阐明我的观点问题.最好亲自看看结果.

I'm getting very unpredictable results using basic bearing calculations. What I need to happen is that as I draw these lines based on the angle of the dial, the lines need to uniformly get drawn into the center of the view. Right now, I'm just getting erratic results and I'm hoping someone here can elucidate my issues. Best to see the results for yourself.

我对mainwindow.ui表单所做的唯一事情是:

Only thing I did to the mainwindow.ui form was:

  1. 将centralWidget设置为宽度:530,高度:633

  1. set centralWidget to Width: 530, Height: 633

将QGraphicsView显示小部件添加到X:8,Y:8,宽度/高度:256.

add a QGraphicsView display widget to X: 8, Y: 8, Width/Height: 256.

将QDial添加到X:210,Y:530,宽度/高度:100,最大:359,包装:true

add a QDial to X: 210, Y: 530, Width/Height: 100, maximum: 359, wrapping:true

所有其他默认值都可以.

All other default values should be fine.

//mainwindow.h

//mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QLineF>
#include <QDial>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

public slots:
    void slt_updateAngleFromDial(int angle);
private slots:
    void slt_drainTheBowl();

private:
    Ui::MainWindow *ui;
    QGraphicsScene *scene;
    QGraphicsView *view;
    QDial dial;
    QGraphicsLineItem *line;
    QGraphicsLineItem *green_needle;
    QGraphicsEllipseItem *mCircle;
    QGraphicsEllipseItem *cCircle;
    QList<QGraphicsLineItem*> m_line_list;
    QLineF green_line;
    QLineF history_line;
    QPointF sceneCenter;
    QPointF drawing_point;
    QPointF historyPointA;
    QPointF historyPointB;
    int historyPoint_count;
    int draw_Radius;
    int draw_angle;
    int viewSize;
    bool started;

    double getBearing(QPointF point);
    double getPointRange(double Xpos, double Ypos);
    QPointF calculate_Bearing_Range(double screenCenter, double bearing, double range, double offset);
    QPointF setPointPosition(double bearing, double range, double centerPos);
    QPointF getQPointOnDisplay(double bearing, double range);
    void addNewHistoryPoint(int drawBearing);
    void drawpath();
    void drainTheBowl_Timer();
    void addNewHistoryPoint(int drawBearing);
};
#endif //MAINWINDOW_H

//mainwindow.cpp

//mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtMath>
#include <QTimer>

MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    scene = new QGraphicsScene(this);
    view = ui->graphicsView;
    view->setScene(scene);
    view->setSceneRect(0,0,512,512);
    view->setHorizontalScrollBarPolicy(Qt::ScrollbarAlwaysOff);
    view->setVerticalScrollBarPolicy(Qt::ScrollbarAlwaysOff);
    viewSize = view->width() /2;
    sceneCenter = QPointF(viewSize,viewSize);
    draw_Radius = 200;
    connect(ui->dial, &QDial::valueChanged, this, &MainWindow::slt_updateAngleFromDial);

    //add drawing line
    drawing_point = sceneCenter + QPointF(0,draw_Radius);
    green_line = QLineF(drawing_point, sceneCenter + QPointF(0, draw_Radius + 20));
    QPen dirLine(Qt::green, Qt::SolidLine);
    dirLine.setWidth(3);
    green_needle = scene->addLine(green_line, dirLine);
    green_needle->setTransformOriginPoint(sceneCenter);

    //draw static outer circle
    int mSize = draw_Radius *2;
    QRectF rimCircle(QPointF(mSize,mSize),QSize(mSize,mSize));
    int mCenter = rimCircle.center().x();
    mCircle = new QGraphicsEllipseItem(rimCircle);
    QBrush rimTip(Qt::darkCyan, Qt::NoBrush);
    QPen rimPen(Qt::darkCyan, Qt:;SolidLine);
    mCircle->setBrush(rimTip);
    mCircle->setPen(rimPen);
    mCircle->setPos(setPointPosition(0,0, mCenter));
    scene->addItem(mCircle);

    //draw static inner circle
    int cSize = 3;
    QRectF circ(QPointF(cSize,cSize),QSize(cSize,cSize));
    int circCenter = circ.center().x();
    cCircle = new QGraphicsEllipseItem(circ);
    QBrush rimTip2(Qt::black, Qt::SolidPattern);
    QPen rimPen2(Qt::black, Qt:;SolidLine);
    cCircle->setBrush(rimTip2);
    cCircle->setPen(rimPen2);
    cCircle->setPos(setPointPosition(0,0, circCenter + 1));// +1 offset to get to center
    scene->addItem(cCircle);

    started = false;
    historyPoint_count = 0;
    draw_angle = 0;
    drainTheBowl_Timer();
    view->centerOn(sceneCenter);

}

MainWindow::~MainWindow(){delete ui;}

void MainWindow::slt_updateAngleFromDial(int angle){
    draw_angle = angle;
    green_needle->setRotation(draw_angle);
}

QPointF MainWindow::setPointPosition(double bearing, double range, double centerPos){
    double pos = viewSize - centerPos;
    QPointF newPoint = calculate_Bearing_Range(pos,bearing,range,90);
    return newPoint;
}

//using info, get new position in scene, account for offset
QPointF MainWindow::calculate_Bearing_Range(double screenCenter, double bearing, double range, double offset){
    double oldX = screenCenter;
    double oldY = oldX;
    double newX = oldX + qCos(qDegreesToRadians(bearing - offset)) * range;
    double newY = oldY + qSin(qDegreesToRadians(bearing - offset)) * range;
    QPointF pos = QPointF(newX, newY);
    return pos;
 }

double MainWindow::getBearing(QPointF point){
    double cX = viewSize;
    double cY = cX;
    double nX = point.x();
    double nY = point.y();

    /** Inverted Y parameter of atan2
    correct look (no mirroring-no blinking), but upper quadrant dead, spatial relationships horrible*/
    double bearing = qRadiansToDegrees(M_PI_2 - atan2(cY - nY, nX - cX)); 

    /** "Correct" Bearing formula
    left quadrants move at correct speed for the most part, right quad speeds to center, best spatial relationships, but not perfect*/
    //double bearing = qRadiansToDegrees(M_PI_2 - atan2(nY - cY, nX - cX));

    /** Invert both parameters of atan2
    no dead quadrants, but mirrored and blinking, spatial relationships horrible*/
    //double bearing = qRadiansToDegrees(M_PI_2 - atan2(cY - nY, cX - nX));

    if(bearing < 0)
        bearing += 360;

    return bearing;
} 

double Mainwindow::getPointRange(double xPos, double yPos){
    double centerX = viewSize;
    double center = centerX;
    double newX = centerX - Xpos;
    double newY = center - Ypos;
    //pythagoros
    double distance = qPow(newX,2) + qPow(newY,2);
    double range = qSqrt(distance);
    return range;
 }

//gather 2 points from angle of dial to draw a line
void MainWindow::addNewHistoryPoint(int drawBearing){
    double pos = viewSize;
    double range = draw_Radius;
    QPointF pt = calculate_Bearing_Range(pos, drawBearing, range, -90);//align to draw point
    historyPoint_count++;

    switch(historyPoint_count){
        case 1:
            historyPointA = pt;
            break;
        case 2:
            historyPointB = pt;
            historyPoint_count = 0;
            break;
     }
}

void MainWindow::drainTheBowl_Timer(){
    QTimer* drainTimer = new QTimer(this);
    connect(drainTimer, SIGNAL(timeout()), this, SLOT(slt_drainTheBowl()));
    drainTimer->start(100);
}

//perform all updates
void MainWindow::slt_drainTheBowl(){
   //always add new points for continuous line
   addNewHistoryPoint(draw_angle);

   //handle moving lines to center
   foreach(QGraphicsLineItem* line, m_line_list){

        //get coordinates of the 2 points for this line
        QLineF adjLine = line->line();
        int adjLine_pt1_x = adjLine.p1().x();
        int adjLine_pt1_y = adjLine.p1().y();
        int adjLine_pt2_x = adjLine.p2().x();
        int adjLine_pt2_y = adjLine.p2().y();

        //find range of the points
        double pt1_range = getPointRange( adjLine_pt1_x, adjLine_pt1_y);
        double pt2_range = getPointRange( adjLine_pt2_x, adjLine_pt2_y);

        //reduce the range towards center
        pt1_range = (pt1_range - 1);
        pt2_range = (pt2_range - 1);

        //determine bearing of the points
        double pt1Bearing = qRound(getBearing(QPointF(adjLine_pt1_x, adjLine_pt1_y)));
        double pt2Bearing = qRound(getBearing(QPointF(adjLine_pt2_x, adjLine_pt2_y)));

        QPointF newOffset1;
        QPointF newOffset2;

        //handle how points get to center
        if(pt1_range > 1.0)
            newOffset1 = QPointF(getQPointOnDisplay(pt1Bearing, pt1_range));
        else{
            pt1_range = 0.0;
            newOffset1 = QPointF(getQPointOnDisplay(pt1Bearing, pt1_range));
        }

        if(pt2_range > 1.0)
            newOffset2 = QPointF(getQPointOnDisplay(pt2Bearing, pt2_range));
        else{
            pt2_range = 0.0;
            newOffset2 = QPointF(getQPointOnDisplay(pt2Bearing, pt2_range));
            //! scene->removeItem(line); //remove line generates errors
            //! m_line_list.removeFirst();// because points don't get to center in order, everything breaks
        }

        //apply new adjustments to this line
        adjLine.setP1(newOffset1);
        adjLine.setPt1(newOffset2);
        line->setline(adjLine);
   } 

   drawPath();//connect the dots
}

//track the tip of the needle for drawing
QPointF MainWindow::getQPointOnDisplay(double bearing, double range){
    int offset = 90; 
    double pos = viewSize;
    QPointF newPoint = calculate_Bearing_Range(pos, bearing, range, offset);
    return newPoint;
}

//draw the new line segment base on history points gathered above
void MainWindow:drawPath(){
    history_line = QLineF(historyPointA, historyPointB);
    QPen mainline(Qt::blue, Qt::SolidLine);
    mainline.setWidth(2);
    line = scene->addLine(history_line, mainline);

    //remove the initial line drawn at 0,0
    if(!started){
         scene->removeItem(line);
         started = true;
    }
    m_line_list.append(line);
}

所以这里要注意几件事.首先在getBearing方法内部,您将看到我成功使用过的3个主要公式,并尝试了许多其他公式,但这是唯一产生一致线条的公式.我添加了一些注释,这些注释应有助于概括这些公式的作用.第一个公式,没有被注释掉,最接近我希望达到的公式.

So a couple of things to note here. First inside of the getBearing method, you'll see the 3 main formulas I have had success with, tried many others, but these are the only ones that produced coherent lines. I added some comments that should help generalize what those formulas are doing. The first formula, the one not commented out, is the closest to what I'm hoping to achieve.

两个主要问题是:1)圆的左上象​​限已死,根本没有点/线移动2)确实移到中心的点没有遵循稳定的顺序.有些积分竞相居中,而另一些则落后.这是我提到空间关系时在评论中所指的内容.绘制的每条线应移至其前一条线的中心,然后移至其后的线的中心.

Two main things wrong with it: 1) the upper left quadrant of the circle is dead, no points/lines move at all 2) the points that do move to center do not follow a steady progression. Some points race to center while others drag behind. This is what I'm referring to in my comments when I mention the spatial relationships. Each line drawn should move to center behind the line drawn before it, and ahead of the line drawn afterwards.

被注释掉的其他两个公式在没有死象限的情况下产生更接近的行为,但是它们都绘制了一条镜像线,并且每条线由于某种原因而闪烁.

The other 2 formulas that are commented out produce a closer behavior in regards to there being no dead quadrants, but they both have a mirrored line being drawn, and every line blinks for some reason.

关于发生的事情,我最好的猜测是正在发生一些坐标混乱.我的另一个想法是,也许我没有正确确定场景的中心和向内绘制的点的中心.

My best guess as to what is happening is there is some coordinate confusion going on. Another thought I had is that maybe I'm not correctly determining the center of the scene and the center of the points being drawn inwards.

您会注意到在内部静态圆的图形上,我有一个"circCenter + 1"偏移量.没有这个小偏移量,圆就不在中心.

You'll notice on the drawing of the inner static circle, I have a " circCenter + 1" offset. Without this small offset, the circle is not quite in the center.

我已经参加了3周了,很乐意为您解决这个问题提供帮助.

I've been on this for 3 weeks now and would love some assistance figuring this out.

*请原谅任何拼写不一致的地方,因为我不得不手工将这段代码从一台机器转移到另一台机器上.

*Please forgive any spelling inconsistencies as I had to transfer this code by hand from one machine to the other.

推荐答案

我认为有几种方法可以简化此过程,这将有所帮助.

I think there are a few ways to simplify this which would help.

  1. 我会在GraphicsScene中将所有大约0,0居中放置-这样会使数学更清晰.

  1. I would center everything about 0,0 in the GraphicsScene - that will make the mathematics more clear.

我真的想而不是使用一组QLines,而是您所追求的是一组全部移向原点的点.因此,与其使用多个QGraphicsLineItem,不如使用一个QGraphicsPathItem并更新路径.而不是存储大量图形项目,而是存储一组点- QList< QPointF>在我的示例中为m_points .

Rather than using a set of QLines, really I think what you after is a set of points which all move towards the origin. So rather than using multiple QGraphicsLineItems, use one QGraphicsPathItem and do the updates to the path. Instead of storing lots of graphics items, store a set of points - QList<QPointF> m_points in my example.

在可能的情况下,使用内置的Qt几何基元(例如QLineF和QPointF)来完成几何工作,而不用自己滚动.

Where possible, use inbuilt Qt geometry primitives like QLineF and QPointF to do the geometry work rather than rolling your own.

我的解决方案的完整代码,位于 https://gist.github.com/docsteer/64483cc8f44ca53565912c50d11cf4a9

Full code for my solution at https://gist.github.com/docsteer/64483cc8f44ca53565912c50d11cf4a9, but the key function:

void MainWindow::slt_drainTheBowl()
{
    // Move the points towards center
    QMutableListIterator<QPointF> i(m_points);
    while(i.hasNext())
    {
        i.next();
        QLineF line(QPointF(0,0), i.value());
        // We move a point by decreasing the length from the origin to the point by 1
        qreal length = line.length();
        length -=1;
        line.setLength(length);
        // If the point is now at (or past) the origin, remove from the list
        if(length<=0)
        {
            i.remove();
        }
        else
        {
            // Update the point in the list
            i.setValue(line.p2());
        }
    }


    // Add a new point to the list based on the current angle
    QPointF newPoint;
    newPoint.setY( qSin(qDegreesToRadians((double)draw_angle)) * 200 );
    newPoint.setX( qCos(qDegreesToRadians((double)draw_angle)) * 200 );


    // Set the points into the path item
    QPainterPath path;
    path.moveTo(newPoint);
    for(int i=0; i<m_points.count(); i++)
        path.lineTo(m_points[i]);

    m_points << newPoint;

    m_pathItem->setPath(path);
}

这篇关于GraphicsScene中的轴承公式计算会产生不稳定的结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆