How to draw an arc with Qt? - qt

Consider the following diagram:
I have information about the center point of both the lines, the angle in between, and the length of both the lines.
The issue is to draw an arc starting at the end of the bottom line and touching the above slanting line (the way shown below):
/
/
/
/.
/ .
/___.
I saw these arc drawing functions of Qt:
http://qt-project.org/doc/qt-5.1/qtgui/qpainter.html#drawArc
These functions need a rectangle as an argument where as I don't have any.
How should I use these functions to draw the arc as shown above?

QPointF O; // intersection of lines
QPointF B; // end point of horizontal line
QPointF A; // end point of other line
float halfSide = B.x-O.x;
QRectF rectangle(O.x - halfSide,
O.y - halfSide,
O.x + halfSide,
O.y + halfSide);
int startAngle = 0;
int spanAngle = (atan2(A.y-O.y,A.x-O.x) * 180 / M_PI) * 16;
QPainter painter(this);
painter.drawArc(rectangle, startAngle, spanAngle);
You have to calculate the boundary rectangle, than the angle between the lines using atan.

Related

LibGDX Own 3D effect set visible lines after getting the distance

Hey sorry for the title
I wanted to create my own 3D effect and it worked well but now i want so visualize my gotten distance but there was too much math for me...
I will put the funktion here that should get the distance and draw the lines.
Thanks for help 🙂
Ask if you need some code please!
// fov is how much is in the vision
// count is how many test lines are drawn in fov
// speed is how far the tester should move on one frame
public void test(float fov, float count, int speed) {
int size = Gdx.graphics.getWidth();
int height = Gdx.graphics.getHeight();
for (float i = 1; i <= count; i++) {
// Distance.test2D() will send a sensor from the roration of "rotation" and if it touches something is returns how many pixels were between that objects. The maxValue will return if it doesnt touch anything for 500 pixels
float distance = Distance.test2D(position, rotation - (fov) + i * (fov / (count / 2)), 500, speed);
// int drawAt = where to draw the line
int drawAt = // where the line should be drawn on the x axes
// will draw a strate line from the buttom to a specefic height
shapeRenderer.line(drawAt, 0, drawAt, (200 - distance * 2));
}
}

QWT moving canvas

I'm using QWT library for my widget, there are some curves on the canvas, like this:
void Plot::addCurve1( double x, double y, const char *CurveName,
const char *CurveColor,const char *CurveType )
{
...
*points1 << QPointF(x, y);
curve1->setSamples( *points1 );
curve1->attach( this );
...
}
So, all my curves have the same coordinate system. I'm trying to build navigation interface, so I could put step into TextEdit (for example) and moving by using this step, or I could go the end/start of my defined curve.
I've found method in QwtPlotPanner class, that gives me such opportunity:
double QWT_widget::move_XLeft()
{
//getting step from TextEdit
QString xValStr = _XNavDiscrepancies->toPlainText();
double xVal = xVal.toDouble();
// moveCanvas(int dx, int dy) - the method of QwtPlotPanner
plot->panner->moveCanvas(xVal,0);
x_storage = x_storage - xVal;
return x_storage;
}
So it works ok, but displacement in pixels and I need to stick it to my defined curve and it's coordinate system.
Qwt User's Guide tells, that:
Adjust the enabled axes according to dx/dy
Parameters
dx Pixel offset in x direction
dy Pixel offset in y direction
And this is the only information I've found. How can I convert pixels step into my coordinat system step? I need to go to the end of my curve, so I should return the last QPointF(x,y) of my curve and convert it to pixel-step? Or maybe I'm using wrong class/method?
Thank you very much :)
Thanks to #Pavel Gridin:
(https://ru.stackoverflow.com/a/876184/251026)
"For conversion from pixels to coordinates and back there are two
methods: QwtPlot::transform and QwtPlot::invTransform"

Rotating a QGraphicsItem and finding the new position of it

I am working on a tower defence game and trying to shoot a projectile from a turret. The sprite of the turret I have looks like this
Also, the bullet looks like this
What I want to do is make the turret shot the bullet to a certain point (for example attackDestination = QPointF(1000, 500);
for this, I have a class Bullet, with the slot move:
void Bullet::move()
{
int stepSize = 20; // how fast the bullet moves
double angle = rotation();
double dy = stepSize * qSin(qDegreesToRadians(angle)); // The X that needs to be moved
double dx = stepSize * qCos(qDegreesToRadians(angle)); // The Y that needs to be moved
setPos(x() + dx, y() + dy);
}
which is triggered by a QTimer.
I also have a slot in the Tower class (which stands for the turret)
void Tower::attackTarget()
{
Bullet *bullet = new Bullet();
//getWidthMap() returns the width of the tower
//getHeightMap() returns the height of the tower
bullet->setPos(x() + getWidthMap() /2, y());
QLineF line(QPointF(x() + getWidthMap() /2, y()), attackDestination);
double angle =(-1) * line.angle();
bullet->setRotation(angle);
this->setRotation(90 + angle);
game->scene->addItem(bullet);
}
I have rotated the turret by +90 degrees because its initial position is vertical and it needs to form an angle (that of the line with oX) just like the bullet. The rotation happens clockwise.
The problem is with the position of the bullet relative to the turret when attacking.
Without the line this->setRotation(90 + angle); (first picture), with it (second picture)
As you can see, the bullets are starting from the initial position of the turret (when it was not rotated), because the pos() function keeps the initial X and Y. How can I fix that ?

Hexagonal tilling of hemi-sphere

I need to have hexagonal grid on a spherical surface. like shown here.
Right now I am doing a hexagonal flattened grid.
and the projecting it onto the surface of a hemisphere. Like here,
But as you can see, the funny artifact is hexagons on the edge are disproportionately large. There should be a better way to do this so that all the hexagons are near equal in their size.
I had tried the solution like #spektre had suggested but my code was producing following plot.
i was using the a=sqrt(x*x+y*y)/r * (pi/2) because i wanted to scale a that goes from [0,r] to z [0,r] so angle a has bounds of [0,pi/2].
But with just a=sqrt(x*x+y*y)/r it works well.
New Development with the task, New problem
I have the problem that now, the hexagons are not equal through out the shapes. I want a uniform shape (area wise) for them across the dome and cylinder. I am confused on how to manage this?
Here is what I have in mind:
create planar hex grid on XY plane
center of your grid must be the center of your sphere I chose (0,0,0) and size of the grid should be at least the 2*radius of your sphere big.
convert planar coordinates to spherical
so distance from (0,0,0) to point coordinate in XY plane is arclength traveling on surface of your sphere so if processed point is (x,y,z) and sphere radius is r then latitude position on sphere is:
a=sqrt(x*x+y*y)/r;
so we can directly compute z coordinate:
z=r*cos(a);
and scale x,y to surface of sphere:
a=r*sin(a)/sqrt(x*x+y*y);
x*=a; y*=a;
If the z coordinate is negative then you have crossed half sphere and should handle differently (skip hex or convert to cylinder or whatever)
Here Small OpenGL/C++ example for this:
//---------------------------------------------------------------------------
const int _gx=15; // hex grid size
const int _gy=15;
const int _hy=(_gy+1)<<1; // hex points size
const int _hx=(_gx+1);
double hex[_hy][_hx][3]; // hex grid points
//---------------------------------------------------------------------------
void hexgrid_init(double r) // set hex[][][] to planar hex grid points at xy plane
{
double x0,y0,x,y,z,dx,dy,dz;
double sx,sy,sz;
int i,j;
// hex sizes
sz=sqrt(8.0)*r/double(_hy);
sx=sz*cos(60.0*deg);
sy=sz*sin(60.0*deg);
// center points arrounf (0,0)
x0=(0.5*sz)-double(_hy/4)*(sz+sx);
y0=-double(_hx)*(sy);
if (int(_gx&1)==0) x0-=sz+sx;
if (int(_gy&1)==0) y0-=sy; else y0+=sy;
for (y=y0,i=0;i<_hy;i+=2,y+=sy+sy)
for (x=x0,j=0;j<_hx;j++,x+=sz)
{
hex[i][j][0]=x;
hex[i][j][1]=y;
hex[i][j][2]=0.0;
x+=sz+sx+sx; j++; if (j>=_hx) break;
hex[i][j][0]=x;
hex[i][j][1]=y;
hex[i][j][2]=0.0;
}
for (y=y0+sy,i=1;i<_hy;i+=2,y+=sy+sy)
for (x=x0+sx,j=0;j<_hx;j++,x+=sx+sx+sz)
{
hex[i][j][0]=x;
hex[i][j][1]=y;
hex[i][j][2]=0.0;
x+=sz; j++; if (j>=_hx) break;
hex[i][j][0]=x;
hex[i][j][1]=y;
hex[i][j][2]=0.0;
}
}
//---------------------------------------------------------------------------
void hexgrid_half_sphere(double r0) // convert planar hex grid to half sphere at (0,0,0) with radius r0
{
int i,j;
double x,y,z,a,l;
for (i=0;i<_hy;i++)
for (j=0;j<_hx;j++)
{
x=hex[i][j][0];
y=hex[i][j][1];
z=hex[i][j][2];
l=sqrt(x*x+y*y); // distance from center on xy plane (arclength)
a=l/r0; // convert arclength to angle
z=r0*cos(a); // compute z coordinate (sphere)
if (z>=0.0) // half sphere
{
a=r0*sin(a)/l;
}
else{ // turn hexes above half sphere to cylinder
z=0.5*pi*r0-l;
a=r0/l;
}
x*=a;
y*=a;
hex[i][j][0]=x;
hex[i][j][1]=y;
hex[i][j][2]=z;
}
}
//---------------------------------------------------------------------------
void hex_draw(int x,int y,GLuint style) // draw hex x = <0,_gx) , y = <0,_gy)
{
y<<=1;
if ((x&1)==0) y++;
if ((x<0)||(x+1>=_hx)) return;
if ((y<0)||(y+2>=_hy)) return;
glBegin(style);
glVertex3dv(hex[y+1][x ]);
glVertex3dv(hex[y ][x ]);
glVertex3dv(hex[y ][x+1]);
glVertex3dv(hex[y+1][x+1]);
glVertex3dv(hex[y+2][x+1]);
glVertex3dv(hex[y+2][x ]);
glEnd();
}
//---------------------------------------------------------------------------
And usage:
hexgrid_init(1.5);
hexgrid_half_sphere(1.0);
int x,y;
glColor3f(0.0,0.2,0.3);
for (y=0;y<_gy;y++)
for (x=0;x<_gx;x++)
hex_draw(x,y,GL_POLYGON);
glLineWidth(2);
glColor3f(1.0,1.0,1.0);
for (y=0;y<_gy;y++)
for (x=0;x<_gx;x++)
hex_draw(x,y,GL_LINE_LOOP);
glLineWidth(1);
And preview:
For more info and ideas see related:
Make a sphere with equidistant vertices
Turning a cylinder into a sphere without pinching at the poles

How to draw and fill a triangle with QPainter?

This is what I tried, it gave me no output. Where am I going wrong?
// Start point of bottom line
qreal startPointX1 = 600.0;
qreal startPointY1 = 600.0;
// End point of bottom line
qreal endPointX1 = 600.0;
qreal endPointY1 = 1200.0;
// Start point of top line
qreal startPointX2 = 600.0;
qreal startPointY2 = 600.0;
// End point of top line
qreal endPointX2 = 800.0;
qreal endPointY2 = 1200.0;
QPainterPath path;
// Set pen to this point.
path.moveTo (startPointX1, startPointY1);
// Draw line from pen point to this point.
path.lineTo (endPointX1, endPointY1);
path.moveTo (endPointX1, endPointY1);
path.lineTo (endPointX2, endPointY2);
path.moveTo (endPointX2, endPointY2);
path.lineTo (startPointX1, startPointY1);
painter.setPen (Qt :: NoPen);
painter.fillPath (path, QBrush (QColor ("blue")));
I have just tried to create a path between these 3 points and fill the area, but there is no output shown.
I think you do not need to call moveTo() function after you call lineTo() because the current position already updated to the the end point of the line you draw. Here is the code that draws a rectangle for me:
// Start point of bottom line
qreal startPointX1 = 600.0;
qreal startPointY1 = 600.0;
// End point of bottom line
qreal endPointX1 = 600.0;
qreal endPointY1 = 1200.0;
// Start point of top line
qreal startPointX2 = 600.0;
qreal startPointY2 = 600.0;
// End point of top line
qreal endPointX2 = 800.0;
qreal endPointY2 = 1200.0;
QPainterPath path;
// Set pen to this point.
path.moveTo (startPointX1, startPointY1);
// Draw line from pen point to this point.
path.lineTo (endPointX1, endPointY1);
//path.moveTo (endPointX1, endPointY1); // <- no need to move
path.lineTo (endPointX2, endPointY2);
//path.moveTo (endPointX2, endPointY2); // <- no need to move
path.lineTo (startPointX1, startPointY1);
painter.setPen (Qt :: NoPen);
painter.fillPath (path, QBrush (QColor ("blue")));
If you want use QRectF
QRectF rect = QRectF(0, 0, 100, 100);
QPainterPath path;
path.moveTo(rect.left() + (rect.width() / 2), rect.top());
path.lineTo(rect.bottomLeft());
path.lineTo(rect.bottomRight());
path.lineTo(rect.left() + (rect.width() / 2), rect.top());
painter.fillPath(path, QBrush(QColor ("blue")));
The documentation says: "Moving the current point will also start a new subpath (implicitly closing the previously current path when the new one is started)".
This means you should once move to the origin of the path, then use only lineTo in order to draw the shape to be filled.
I added this answer because the answer "I think you do not need to call moveTo() function after you call lineTo() because the current position already updated to the the end point of the line you draw." is quite misleading. The moveTo is not unnecessary, it's actually causing the problem.

Resources