Qt: Painting a square and setting its bounds - c++

I'm a beginner in Qt. I want to draw roads and a moving circle. The circle can only move on the road. And the road is made of a simple rectangle.
In painting the road I'm initializing a QPolygonF then appending the 4 corners of the desired rectangle.
And the circle is also drawn a boundingRect which returns QRectF (-10,-10,20,20).
I'm using collidesWithItem to determine where the circle can move.
The problem is that the circle is only moving along the center of the road, and it seems with my research that boundingRect is not the suitable function.
What is a simple method to draw a road and a circle that can move along the whole road?
Code for painting the road (Edge):
void Edge::paint( QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget )
{
painter->setBrush(Qt::gray);
int x1= startPoint.x();
int y1= startPoint.y();
int x2= endPoint.x();
int y2= endPoint.y();
QPolygonF Rectangle;
if (y1 != y2)
{
Rectangle.append(QPointF(x1-width/2,y1));
Rectangle.append(QPointF(x1+width/2,y1));
Rectangle.append(QPointF(x2+width/2,y2));
Rectangle.append(QPointF(x2-width/2,y2));
}
if (x1 != x2)
{
Rectangle.append(QPointF(x1,y1+width/2));
Rectangle.append(QPointF(x1,y1-width/2));
Rectangle.append(QPointF(x2,y2-width/2));
Rectangle.append(QPointF(x2,y2+width/2));
}
painter->drawPolygon(Rectangle);
}
And this is the code for the boundingRect of the road(edge):
QRectF Edge::boundingRect() const
{
int x1= startPoint.x();
int y1= startPoint.y();
int x2= endPoint.x();
int y2= endPoint.y();
if (y1 != y2)
{
return QRectF(x1 - width/2, y1, width, y2 - y1);
}
if (x1 != x2)
{
return QRectF(x1, y1 - width/2, x2 - x1, width);
}
}

Related

How to draw focus icon with QPainter

In the project I am working now we already have the left arrow(which can be seen left bottom corner) drawn with QPaint the code is like :
const qreal x1 = left ? (rect.left() + 19) : (rect.right() - 19);
const qreal x2 = left ? (rect.left() + 12) : (rect.right() - 12);
const qreal y2 = rect.y() + rect.height() / 2.0;
const qreal y1 = y2 - 8;
const qreal y3 = y2 + 8;
painter->setPen(QPen(InternalStuff::Instance()->GetColor(ILMCTheme::ColorIconInfoBarArrow), 3, Qt::SolidLine, Qt::RoundCap));
const QVector<QPointF> points = QVector<QPointF>() << QPointF(x1, y1) << QPointF(x2, y2) << QPointF(x2, y2) << QPointF(x1, y3);
painter->setRenderHint(QPainter::Antialiasing);
painter->drawLines(points);
Now a new mock up is send to me and I have been asked to implement a focus icon which can be seen in top left corner.
Two requests are a bit different for example focus icon is filled with white color.
Now I am thinking creating a square and fill it with white than draw two lines in the middle of it. Is it a good way to go? Did anyone done something like this before and has samples?
If anyone need something like that the code below created a similar shape to the one at the top left of the picture in the question
QRectF newRec = rect;
painter->save();
newRec.setSize( newRec.size() / 20 * 11 );
painter->setBrush(Qt::white);
QColor colorRect( LMCTheme::Instance()->GetColor(InternalStuff::ColorIconInfoBarArrow));
painter->setPen(QPen( colorRect, 3, Qt::SolidLine, Qt::RoundCap));
painter->translate( rect.width()/2, rect.height()/10 );
painter->rotate(45);
painter->setRenderHint(QPainter::Antialiasing);
painter->drawRect(newRec);
painter->restore();
QColor color(LMCTheme::Instance()->GetColor(InternalStuff::ColorIconInfoBarBackground));
painter->setRenderHint(QPainter::Antialiasing);
painter->setCompositionMode(QPainter::CompositionMode_Clear);
painter->setPen(QPen(color, 3, Qt::SolidLine, Qt::RoundCap));
painter->drawLine( QLine(QPoint(rect.width()/2, rect.height()/10 ), QPoint(rect.width()/2, rect.height() - rect.height()/10 ) ) );
painter->drawLine( QLine(QPoint(rect.width()/10, rect.height()/2), QPoint(rect.width()*9/10, rect.height()/2)) );

Allegro Circle Collision Detection

I have a function that is supposed to return true if two circles are colliding and false otherwise, and to help while developing I have also added a part within the function to draw the hitbox only when they're not colliding.
My issue is even when they are colliding it will continue to draw the hitbox, and say they're not colliding, indicating that the function isn't working properly.
int colliding(int x, int y, int r, int x1, int y1, int r1)
{
//compare the distance to combined radii
int dx = x1 - x;
int dy = y1 - y;
int radii = r + r1;
if ((dx * dx) + (dy * dy) < radii * radii)
{
return true;
}
else
{
player.hitbox.draw();
return false;
}
}
int main()
{
while (true)
{
player.draw();
int cx = 300;
int cy = 300;
int cr = 50;
al_draw_filled_circle(camera.getScreenPosX(cx), camera.getScreenPosY(cy), cr, al_map_rgb(0, 0, 0));
colliding(player.hitbox.posX, player.hitbox.posY, player.hitbox.radius, cx, cy, cr);
al_flip_display();
al_clear_to_color(al_map_rgb(255, 255, 255));
}
}
I would assume that camera.getScreenPosX/Y() transforms your cx/cy/cr circle into another space than the one where player.hitbox.posx/y are. I cannot be sure however, because implementation of player.hitbox.draw() is not given.
Your collision function seems fine, so I'd go and check whether player.hitpox.posx/y and cx/cy are in the same coordinate space.

draw line on form show/visible C++

Hey I want to draw something when a form pops up. I noticed the OnPaint even but it doesn't show my drawing. When I add it to a button it does show what I wanted to draw.
This is what I draw:
void TSelectElementForm::DrawLinesInLayout()
{
EraseLayOut(); //draws a new square over the previous lines
int X_origin, Y_origin;
int X1, Y1, X2, Y2, X3, Y3, X4, Y4;
int VectorSize;
int scale;
X_origin = Layout->Left +5;
Y_origin = Layout->Top +5;
Canvas->Pen->Color=clRed;
Canvas->MoveTo(X_origin,Y_origin);
Canvas->LineTo(X_origin,Y_origin+10);
Canvas->MoveTo(X_origin,Y_origin);
Canvas->LineTo(X_origin+10,Y_origin);
VectorSize = ElementVector[Index].vectorLP.size();
scale = CalculateScale();
for(int i =0; i < VectorSize; i++)
{
int LPX = ElementVector[Index].vectorLP[i].X;
int LPY = ElementVector[Index].vectorLP[i].Y;
int BeginX = ElementVector[Index].el_beginX;
Canvas->Pen->Color=clLime;
X1 = X_origin + ((LPX - BeginX) / scale);
Y1 = Y_origin + (LPY / scale);
if(i == 0)
{
Canvas->MoveTo(X1, Y1);
}
else
{
Canvas->LineTo(X1, Y1);
}
}
}
So when I call this method in a button it works, but when I call this in a OnShow event or OnPaint event it doesn't draw anything.
The strange thing is, when I simply just create a new project with a drawLine() method(that draws 1 line) and a OnPaint event It DOES work. How is that possible?
So, simply 2 questions:
Is this method to long to draw it before my form pops up?
How can I get this drawn on my form right away when it pops up?

How to make manually calculated orbital paths agree with Qt's ellipse drawing method?

I'm attempting to draw celestial bodies moving around on simplified, perfectly circular orbits. I'm also drawing the projected orbital paths these objects will take. However, the problem is that the actual path the objects take doesn't agree with the projection on zooming in closely enough.
Video demonstrating the issue: https://www.youtube.com/watch?v=ALSVfx48zXw
If zoomed out, the problem is non-existent, because the deviation is too small. The apparent size of the deviation appears to be affected primarily by the visible curvature of the circles - notice how the paths of the moons agree with their motion. If one were to zoom in so that the moons' projected paths appear close to straight lines, they would have the same pattern of deviations as the planet shows.
Coordinates calculating methods:
double getX (long int time) {
return orbit * cos(offset + time * speed);
}
double getY (long int time) {
return orbit * sin(offset + time * speed);
}
Projected orbit drawing:
ellipse = scene->addEllipse(system.starX-body.orbit,
system.starY-body.orbit,
body.orbit*2,body.orbit*2,greenPen,transBrush);
Drawing the celestial bodies where they actually appear:
ellipse = scene->addEllipse(-body.radius,
-body.radius,
body.radius*2,body.radius*2,blackPen,greenBrush);
ellipse->setFlag(QGraphicsItem::ItemIgnoresTransformations);
ellipse->setPos(system.starX+body.getX(date2days(game.date)),
system.starY+body.getY(date2days(game.date)));
How do I fix this so that the celestial bodies are always on the predicted curve?
EDIT1:
I have attempted using the suggested algorithm for drawing my own ellipse. The version adapted for use with Qt I reproduce here:
QPoint get_point(double a, double b, double theta, QPoint center)
{
QPoint point;
point.setX(center.x() + a * cos(theta));
point.setY(center.y() + b * sin(theta));
return point;
}
void draw_ellipse(double a, double b, QPoint center, double zoom_factor, QGraphicsScene * scene, QPen pen)
{
double d_theta = 1.0d / zoom_factor;
double theta = 0.0d;
int count = 2.0d * 3.14159265358979323846 / d_theta;
QPoint p1, p2;
p1 = get_point(a, b, 0.0f, center);
for (int i = 0; i <= count; i++)
{
theta += d_theta;
p2 = p1;
p1 = get_point(a, b, theta, center);
scene->addLine(p1.x(),p1.y(),p2.x(),p2.y(),pen);
}
}
The results weren't encouraging:
In addition to not looking pretty at zoom_factor 360, the application ran extremely sluggishly, using much more resources than previously.
EDIT2:
The improved version gives much better results, but still slow. Here is the code:
QPointF get_point(qreal a, qreal b, qreal theta, QPointF center)
{
QPointF point;
point.setX(center.x() + a * cos(theta));
point.setY(center.y() + b * sin(theta));
return point;
}
void draw_ellipse(qreal a, qreal b, QPointF center, qreal zoom_factor, QGraphicsScene * scene, QPen pen)
{
qreal d_theta = 1.0d / zoom_factor;
qreal theta = 0.0d;
int count = 2.0d * 3.14159265358979323846 / d_theta;
QPointF p1, p2;
p1 = get_point(a, b, 0.0f, center);
for (int i = 0; i <= count; i++)
{
theta = i * d_theta;
p2 = p1;
p1 = get_point(a, b, theta, center);
scene->addLine(p1.x(),p1.y(),p2.x(),p2.y(),pen);
}
}
It appears that Qt does not auto-adjust the drawing precision or 'sampling resolution'.
You could try to draw the ellipse yourself, by drawing a loop of lines. Increase the sample resolution of the drawing when you zoom in - i.e. make the sampled points closer to each other.
Take the parametric equation of an ellipse
x = a cos (theta), y = b sin (theta)
where a and b are the semi-major and semi-minor axes of the ellipse, and sample the points with it:
(pseudo C++-style code)
point get_point(float theta, point center)
{
return point(center.x + a * cos(theta), center.y + b * sin(theta));
}
void draw_ellipse(float a, float b, point center, float zoom_factor)
{
float d_theta = 1.0f / zoom_factor;
float theta = 0.0f;
int count = 2.0f * PI / d_theta;
point p1, p2;
p1 = get_point(0.0f, center);
for (int i = 0; i < count; i++)
{
theta += d_theta;
p2 = p1;
p1 = get_point(theta, center);
drawline(p1, p2);
}
}
Sorry if the code looks arbitrary (I'm not familiar with Qt), but you get the point.
Assuming that all of the parameters you pass to addEllipse are of sufficient resolution, the issue seems to be with how Qt renders ellipses. The discretization used in ellipse drawing is not dependent on the transformation matrix of the view.
When a QGraphicsItem is being rendered in a view, its paint method certainly has access to the paint device (in this case: a widget). It could certainly determine the proper discretization step in terms of angle. Even if a graphics item were to render using regular painter calls, the painter has the same information, and the paint device certainly has this information in full. Thus there's no reason for Qt to do what it does, I think. I'll have to trace into this code and see why it fails so badly.
The only fix is for you to implement your own ellipse item, and chose the discretization step and begin/end angles according to the viewport size at the time of rendering.
qreal is a double - so that shouldn't be an issue unless Qt is configured with -qreal float.

How to draw a line using SDL without using external libraries

How can i draw a 2D line between two given points using SDL c++ library. I don't want to use any other external libraries like SDL_draw or SDL_gfx .
Up-to-date answer for the coders who are struggling with the same issue.
In SDL2, there are a couple of functions in SDL_Render.h to achive this without implementing your own line drawing engine or using an external library.
You likely want to use:
int SDL_RenderDrawLine( SDL_Renderer* renderer, int x1, int y1, int x2, int y2 );
Where renderer is the renderer you created before, and x1 & y1 are for the beginning, and x2 & y2 for the ending.
There is also an alternative function where you could draw a line with multiple points right away, instead of calling the mentioned function several times:
int SDL_RenderDrawPoints( SDL_Renderer* renderer, const SDL_Point* points, int count );
Where renderer is the renderer you created before, points is a fixed-array of the known points, and count the amount of points in that fixed-array.
All mentioned functions give a -1 back when error, and 0 on success.
Rosetta Code has some examples:
void Line( float x1, float y1, float x2, float y2, const Color& color )
{
// Bresenham's line algorithm
const bool steep = (fabs(y2 - y1) > fabs(x2 - x1));
if(steep)
{
std::swap(x1, y1);
std::swap(x2, y2);
}
if(x1 > x2)
{
std::swap(x1, x2);
std::swap(y1, y2);
}
const float dx = x2 - x1;
const float dy = fabs(y2 - y1);
float error = dx / 2.0f;
const int ystep = (y1 < y2) ? 1 : -1;
int y = (int)y1;
const int maxX = (int)x2;
for(int x=(int)x1; x<maxX; x++)
{
if(steep)
{
SetPixel(y,x, color);
}
else
{
SetPixel(x,y, color);
}
error -= dy;
if(error < 0)
{
y += ystep;
error += dx;
}
}
}
You can use any of the line drawing algorithms.
Some common and easy ones are:
Digital Differential Analyzer (DDA)
Bresenham's line algorithm
Xiaolin Wu's line algorithm