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.
Only thing I did to the mainwindow.ui form was:
set centralWidget to Width: 530, Height: 633
add a QGraphicsView display widget to X: 8, Y: 8, Width/Height: 256.
add a QDial to X: 210, Y: 530, Width/Height: 100, maximum: 359, wrapping:true
All other default values should be fine.
//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);
}
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.
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.
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.
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.