GraphicsScene中的轴承公式计算产生不稳定的结果

问题描述:

我使用基本轴承计算得到非常不可预测的结果。我需要发生的是,当我根据刻度盘的角度绘制这些线条时,线条需要均匀地绘制到视图的中心。现在,我只是得到不稳定的结果,我希望有人在这里可以澄清我的 问题。最好亲自看看结果。GraphicsScene中的轴承公式计算产生不稳定的结果

我做的mainwindow.ui形式的唯一的事情就是:

  1. 集centralWidget为width:530,高度:633

  2. 一个的QGraphicsView显示小工具添加到X:8,Y: 8,宽度/高度:256

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

所有其他默认值应该没问题。

//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

#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个主要公式,尝试了许多其他公式,但是这些是唯一能够生成连贯线条的公式。我添加了一些评论,这些评论应该有助于概括这些公式正在做什么。第一个公式是没有注释的公式,与我希望达到的最接近。

它有两个主要的问题:1)圆的左上象​​限是死的,没有点/线移动2)移动到中心点不遵循稳定的进展。有些分数比赛中心,而其他人拖后。当我提到空间关系时,这就是我在评论中提到的。绘制的每条线应该移动到其之前绘制的线的后面,并且在之后绘制的线之前。

另外两个注释掉的公式会产生一个更接近于没有死象限的行为,但它们都有一条镜像线被绘制,并且每条线都由于某种原因而闪烁。

我最好的猜测是发生了什么事情是有一些协调混乱继续下去。我的另一个想法是,也许我没有正确地确定场景的中心点和向内绘制点的中心。

你会注意到在内部静态圆的图上,我有一个“circCenter + 1”偏移量。如果没有这个小的偏移量,这个圆不在中心。

我已经在这里已经3个星期了,并且希望得到一些帮助了解这一点。

*请原谅任何拼写不一致,因为我必须手动将这些代码从一台机器传输到另一台机器。

我认为有几种方法可以简化这将有所帮助。

  1. 我会在GraphicsScene中关于0,0的所有内容 - 这将使数学更清晰。

  2. 而不是使用一组QLines的,真的我觉得你是一个集所有移向原点之后。因此,而不是使用多个QGraphicsLineItems,使用一个QGraphicsPathItem并做更新的路径。而不是存储大量的图形项目,存储一组点 - QList<QPointF> m_points在我的例子。

  3. 如果可能的话,使用内置的Qt的几何图元像QLineF和QPointF做几何的工作,而不是滚动您自己。我在https://gist.github.com/docsteer/64483cc8f44ca53565912c50d11cf4a9解决方案

的完整代码,但关键功能:

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); 
} 
+0

哇,清除了很多东西,谢谢。但是,由于某些原因,centerOn(0,0)不起作用。整个圆圈保持在左边角落,我无法在视图中居中。如果我使用fitInView,我至少可以看到整个圆圈,但是这提出了其他问题。再次感谢你,我觉得自己浪费了很多时间。我还是很想知道为什么轴承的计算是靠不住的,但我很高兴看到它最后的工作应该的方式。 – bauervision

+0

是的,我注意到,centerOn()也没工作,不明白为什么,但如果你把它放在构造函数外,像在resizeEvent(),它的工作原理。不是100%确定为什么你原来的计算不起作用,而是尽可能简化事情通常帮助我:) – docsteer