How to have text centered inside each slice of a pie chart? - c++

I would like to get the text labels (percentages) centered within each pie slice. It currently works a bit for two of the quadrants:
What am I doing wrong?
void PieChartWidget::paintEvent(QPaintEvent *) {
QPainter painter(this);
QRectF size;
painter.setPen(QPen(Qt::black, 2));
if (this->height() > this->width()) {
size = QRectF(5, 5, this->width() - 10, this->width() - 10);
} else {
size = QRectF(5, 5, this->height() - 5, this->height() - 10);
}
double sum = 0.0, startAng = 0.0;
double angle, endAng;
double percent;
for (int i = 0; i < qvValues.size(); i++) {
sum += qvValues[i];
}
for (int i = 0; i < qvValues.size(); i++) {
percent = qvValues[i] / sum;
angle = percent * 360.0;
endAng = startAng + angle;
painter.setBrush(qvColors[i]);
painter.drawPie(size, static_cast<int>(startAng * 16),
static_cast<int>(angle * 16));
startAng = endAng;
if (percent != 0) {
double draw_x = width() / 2 +
cos(PI * (endAng / 180.0 - angle / 360.0)) * this->width() / 4.0;
double draw_y = height() / 2 +
sin(PI * (endAng / 180.0 - angle / 360.0)) * this->width() / 4.0;
painter.drawText(draw_x, draw_y, QString::number(percent * 100) + "%");
}
}
}

On this line:
painter.drawText(this->width()/4,this->height(), QString::number(percent*100)+"%");
You seem to draw the percentage in the same place every time. You do successfully draw the percentage for each section, they're just being drawn in the same place every time. Try changing it to this:
painter.drawText(double(i + 1) * this->width()/4,this->height(), QString::number(percent*100)+"%");
And you'll see what I mean. By multiplying the x value by some changing value, the x position of each drawn text will change, and thus you will be able to see the different percentages being drawn.
If you want it to draw in each quadrant, then your code might look something like this:
# define PI 3.14159265358979323846
...
double draw_x = this->width / 2.0 + cos(PI * (end_angle / 180.0 - angle / 360.0)) * this->width / 4.0;
double draw_y = this->height / 2.0 - sin(PI * (end_angle / 180.0 - angle / 360.0)) * this->width / 4.0;
painter.drawText(draw_x, draw_y, QString::number(percent*100)+"%");
Basically, what's happening in the above code is I'm calculating the x and y coords of the middle of each slice. Then, I'm drawing the percentages in those positions.

Related

waves from selected pixel (openGL C++)

I've created a program that draws a waving flag and I want to add a functionality that will create new wave on selected pixel, but I can't make it start where I want it to start an that even make the flag stop waving (prob. because of synced sin).
Here's my display func.
const int W = 800;
const int H = 600;
// simulates Frame Buffer
unsigned char pixels[H][W][3] = { 0 }; // 3 is for RGB
void display()
{
glClear(GL_COLOR_BUFFER_BIT); // clean frame buffer
createFlag();
int i, j;
double dist;
offset += 0.25;
for (i = 0; i < H; i++)
for (j = 0; j < W; j++)
{
dist = sqrt(pow(i + H / 2.0, 2) + pow(j + W / 2.0, 2));
pixels[i][j][0] += 135 + 55 * (1 + 1 * sin(dist / 25 - offset)) / 2; // red
pixels[i][j][1] += 135 + 85 * (1 + 1 * sin(dist / 25 - offset)) / 2; // green
pixels[i][j][2] += 135 + 105 * (1 + 1 * sin(dist / 25 - offset)) / 2; // blue
}
// draws the matrix pixels
glDrawPixels(W, H, GL_RGB, GL_UNSIGNED_BYTE, pixels);
glutSwapBuffers(); // show all
}
And here is my mouse func.
void mouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
double dist;
offset += 0.1;
for (y = 0; y < H; y++)
for (x = 0; x < W; x++)
{
dist = sqrt(pow(H/2.0 -(H - y), 2) + pow(W/2.0 -x, 2)); //problem is prob. here
pixels[y][x][0] += 135+ 55 * (1 + 1 * sin(dist / 50.0 - offset)) / 2; // red
pixels[y][x][1] += 135+ 85 * (1 + 1 * sin(dist / 50.0 - offset)) / 2; // green
pixels[y][x][2] += 135+105 * (1 + 1 * sin(dist / 50.0 - offset)) / 2; // blue
if (offset < 0.3)
offset += 0.05;
}
}
}
Here are some points that I see:
in your mouse function you don't use your arguments x and y which are the position of your mouse click, instead you create local variables x and y. If you want to start a wave from the pixel you clicked on, you have to center your dist to this point, so an idea of code could be (inspired by GLUT mouse button down):
void mouse(int button, int state, int x, int y)
{
// save the left button state
if (button == GLUT_LEFT_BUTTON)
{
leftMouseButtonDown = (state == GLUT_DOWN);
}
// save the mouse position
mouseXPos = x;
mouseYPos = y;
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT); // clean frame buffer
createFlag();
double dist;
offset += 0.25;
for (i = 0; i < H; i++)
for (j = 0; j < W; j++)
{
dist = sqrt(pow(i + H / 2.0, 2) + pow(j + W / 2.0, 2));
pixels[i][j][0] += 135 + 55 * (1 + 1 * sin(dist / 25 - offset)) / 2; // red
pixels[i][j][1] += 135 + 85 * (1 + 1 * sin(dist / 25 - offset)) / 2; // green
pixels[i][j][2] += 135 + 105 * (1 + 1 * sin(dist / 25 - offset)) / 2; // blue
}
if (leftMouseButtonDown) // the way you can get leftMouseButtonDown depends on your
// configuration of your different files
// (main.cpp, scene.cpp, ...)
new_wave() ; //see below
// draws the matrix pixels
glDrawPixels(W, H, GL_RGB, GL_UNSIGNED_BYTE, pixels);
glutSwapBuffers(); // show all
}
void new_wave()
{
x = mouseXPos ; // same remark as leftMouseButtonDown
y = mouseYPos ;
offset = 0.1;
for (i = 0; i < H; i++) // i and not y
for (j = 0; j < W; j++)
{
dist = sqrt(pow(i - y, 2) + pow(j - x, 2)); // change here
pixels[i][j][0] += 135+ 55 * (1 + 1 * sin(dist / 50.0 - offset)) / 2; // red
pixels[i][j][1] += 135+ 85 * (1 + 1 * sin(dist / 50.0 - offset)) / 2; // green
pixels[i][j][2] += 135+105 * (1 + 1 * sin(dist / 50.0 - offset)) / 2; // blue
if (offset < 0.3)
offset += 0.05;
// I don't really get what you do here with the offset, but another parameter to
// play with could be the amplitude of this wave that starts from the point
// you clicked, like when a drop falls on water
}
}
if you want a real-time waving flag you have to take time as a parameter of your pixels color value
I haven't tested it but I'm not sure on how openGL reacts to RGB coloring with values over 255, which happens in your case, maybe keep that in mind if you still have bugs
dist = sqrt(pow(i + H / 2.0, 2) + pow(j + W / 2.0, 2)); this is centered in (-W/2.0, -H/2.0), is that what you want? (maybe yes just want to make sure, if you want to simulate some wind you could set wind's origin where you want, which is what you do here)
int i, j; in your display isn't useful (just to clear some code)
So these are some remarks I would have made myself, this piece of code probably won't be your final one.
Let me know if I misunderstood what you are aiming to do, or if something I wrote is unclear.

Function to calculate angle to a point in unusual 2D space

I'm looking for a robust function to calculate the difference(delta) between an object and a point.
For example, it there was an object at point A with an orientation of 1.2 Rad, what would be the required angle for the object to turn in order to face Point B.
Furthermore, I'm working in a odd coordinate system where north(0 Rad) faces towards +X, the image below shows this.
I understand the fundamentals, but I'm struggling to produce something robust.
My c++ function template look like this,
float Robot::getDeltaHeading(float _x1, float _y1, float _x2, float _y2, float _currentHeading) {
//TODO:
return xxxxxxx;
}
Any help would be appreciated.
Cheers in Advance.
Here's the answer.
float Robot::getDeltaHeading(float _x1, float _y1, float _x2, float _y2, float _currentHeading) {
_currentHeading -= 90;
double Ux = 0.0, Uy = 0.0, Vx = 0.0, Vy = 0.0, d = 0.0;
d = sqrtf(powf(abs(_x1 - _x2), 2) + powf(abs(_y1 - _x2), 2));
Ux = (_x2 - _x1) / d;
Uy = (_y2 - _y1) / d;
Vx = cos(_currentHeading * (3.14159f / 180.0));
Vy = sin(_currentHeading * (3.14159f / 180.0));
auto ans = 90 + (atan2(((Ux * Vy) - (Uy * Vx)), ((Ux * Vx) + (Uy * Vy))) * (180.0 / 3.14159f));
while (ans > 180) ans -= 360;
while (ans < -180) ans += 360;
return ans;
}

Is there any simple way to multiply cv::Rect size and coordinates?

Let's say I have 2 rectangle. I want the second rectangle to be twice bigger than the first rectangle and the position of x,y also twice bigger.
cv::Rect r1=Rect(10,20,40,60);
cv::Rect r2 = r1 * 2; //this won't work
Setting the rectangle 2 parameter 1 by 1 will work
r2.height = r1.height * 2;
r2.width = r1.height * 2;
r2.x = r1.x * 2;
r2.y = r2.y * 2;
It works, but is there any simpler way to do it (like single line code)?
If you want to do this, this might be the shortest way:
cv::Rect r1=Rect(10,20,40,60);
cv::Rect r2(r1.tl() * 2, r1.br() * 2);
We can overload the * operator:
cv::Rect operator*(cv::Rect r, double scale) {
r.height *= scale;
r.width *= scale;
r.x *= scale;
r.y *= scale;
return r;
}
And then you can multiply rectangles directly:
Rect r2 = Rect(10, 20, 40, 60) * 2;

Calculating iso tile co-ordinates for a TMX map when zoomed on a CCLayerPanZoom control

I'm working on some code to place isometric CCTMXTiledMap onto a CCLayerPanZoom control and then convert a touch location into ISO tilemap co-ordinates. This all works perfectly well for me, so long as the scale of the CClayerPanZoom is 1 (i.e. if I don't zoom in or zoom out). I can pan the map around and still calculate the correct iso tile co-oridinates. However, as soon as I zoom the tiled map in or out the iso cordinates returned by my code are completely wrong. Please see below for my code to calculate the iso co-ordinates from the touch location.
-(CGPoint) tilePosFromLocation:(CGPoint)location tileMap:(CCTMXTiledMap*)thisTileMap panZoom:(CCLayerPanZoom*)thisPanZoom
{
float midScale = (thisPanZoom.minScale + thisPanZoom.maxScale) / 2.0;
float newScale = (thisPanZoom.scale <= midScale) ? thisPanZoom.maxScale : thisPanZoom.minScale;
if (thisPanZoom.scale < 1)
{
newScale = newScale + thisPanZoom.scale;
}
else
{
newScale = newScale - thisPanZoom.scale;
}
CGFloat deltaX = (location.x - thisPanZoom.anchorPoint.x * (thisPanZoom.contentSize.width / CC_CONTENT_SCALE_FACTOR()) ) * (newScale);
CGFloat deltaY = (location.y - thisPanZoom.anchorPoint.y * (thisPanZoom.contentSize.height / CC_CONTENT_SCALE_FACTOR()) ) * (newScale);
CGPoint position = ccp((thisPanZoom.position.x - deltaX) , (thisPanZoom.position.y - deltaY) );
float halfMapWidth = thisTileMap.mapSize.width * 0.5f;
float mapHeight = thisTileMap.mapSize.height;
float tileWidth = thisTileMap.tileSize.width / CC_CONTENT_SCALE_FACTOR() * newScale;
float tileHeight = thisTileMap.tileSize.height / CC_CONTENT_SCALE_FACTOR() * newScale;
CGPoint tilePosDiv = CGPointMake(position.x / tileWidth, position.y / tileHeight );
float inverseTileY = tilePosDiv.y - (mapHeight * CC_CONTENT_SCALE_FACTOR()) * newScale; //mapHeight + tilePosDiv.y;
float posX = (int)(tilePosDiv.y - tilePosDiv.x + halfMapWidth);
float posY = (int)(inverseTileY + tilePosDiv.x - halfMapWidth + mapHeight);
// make sure coordinates are within isomap bounds
posX = MAX(0, posX);
posX = MIN(thisTileMap.mapSize.width - 1, posX);
posY = MAX(0, posY);
posY = MIN(thisTileMap.mapSize.height - 1, posY);
return CGPointMake(posX, posY);
}
Can anyone offer any insight into where I'm going wrong with this?
Thanks,
Alan

Confusing source code in TrollTech's Qt Tutorial Ch11

I am learning Qt from TrollTech's Qt Tutorial these days, and I'am confused about the source code of calculating the position of bullet in this page:
QRect CannonField::shotRect() const
{
const double gravity = 4;
double time = timerCount / 20.0;
double velocity = shootForce;
double radians = shootAngle * 3.14159265 / 180;
double velx = velocity * cos(radians);
double vely = velocity * sin(radians);
double x0 = (barrelRect.right() + 5) * cos(radians);
double y0 = (barrelRect.right() + 5) * sin(radians);
double x = x0 + velx * time;
double y = y0 + vely * time - 0.5 * gravity * time * time;
QRect result(0, 0, 6, 6);
result.moveCenter(QPoint(qRound(x), height() - 1 - qRound(y)));
return result;
}
In the third-last line:
result.moveCenter(QPoint(qRound(x), height() - 1 - qRound(y)));
I think that - 1 is nonsense, isn't it?
You have a widget:
If the height of widget is height, then y == 0 line is on the top of the widget and bottom line has y == height - 1 coordinate. So, if you want to show a point on the bottom line of the widget, you should set it y coordinate to height - 1.
Apparently, they use bottom of the widget as a ground level, so the bullet can be only above or on this level.