使用基本轴承计算,我得到了非常不可预测的结果。我需要做的是,当我根据表盘的角度绘制这些线条时,线条需要均匀地绘制到视图的中心。现在,我只是得到了不稳定的结果,我希望这里有人可以阐明我的 的问题。最好自己看看结果。
我对mainwindow.ui表单所做的只是:
将centralWidget设置为Width:530,Height:633
将QGraphicsView显示小部件添加到X:8,Y:8,宽度/高度:256。
向X:210添加QDial,Y:530,宽度/高度:100,最大值:359,包装:true
所有其他默认值应该没问题。
// 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个星期,并希望得到一些帮助来解决这个问题。
*请原谅任何拼写不一致,因为我必须手动将这些代码从一台机器传输到另一台机器。
答案 0 :(得分:1)
我认为有几种方法可以简化这项工作,这会有所帮助。
我会在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);
}