GraphicsScene中的轴承公式计算产生不稳定的结果
我使用基本轴承计算得到非常不可预测的结果。我需要发生的是,当我根据刻度盘的角度绘制这些线条时,线条需要均匀地绘制到视图的中心。现在,我只是得到不稳定的结果,我希望有人在这里可以澄清我的 问题。最好亲自看看结果。GraphicsScene中的轴承公式计算产生不稳定的结果
我做的mainwindow.ui形式的唯一的事情就是:
集centralWidget为width:530,高度:633
一个的QGraphicsView显示小工具添加到X:8,Y: 8,宽度/高度:256
添加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个星期了,并且希望得到一些帮助了解这一点。
*请原谅任何拼写不一致,因为我必须手动将这些代码从一台机器传输到另一台机器。
我认为有几种方法可以简化这将有所帮助。
我会在GraphicsScene中关于0,0的所有内容 - 这将使数学更清晰。
而不是使用一组QLines的,真的我觉得你是一个集所有移向原点之后。因此,而不是使用多个QGraphicsLineItems,使用一个QGraphicsPathItem并做更新的路径。而不是存储大量的图形项目,存储一组点 -
QList<QPointF> m_points
在我的例子。如果可能的话,使用内置的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);
}
哇,清除了很多东西,谢谢。但是,由于某些原因,centerOn(0,0)不起作用。整个圆圈保持在左边角落,我无法在视图中居中。如果我使用fitInView,我至少可以看到整个圆圈,但是这提出了其他问题。再次感谢你,我觉得自己浪费了很多时间。我还是很想知道为什么轴承的计算是靠不住的,但我很高兴看到它最后的工作应该的方式。 – bauervision
是的,我注意到,centerOn()也没工作,不明白为什么,但如果你把它放在构造函数外,像在resizeEvent(),它的工作原理。不是100%确定为什么你原来的计算不起作用,而是尽可能简化事情通常帮助我:) – docsteer