سؤال

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:

Not pretty

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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top