Search code examples
pythonmatrixopenglrotation

How can I make a sphere follow an eight-like path in Python using OpenGL?


I have to make a sphere move in the shape of the infinite symbol for my computer graphics class.

I think I have managed to recreate the exact movement of the sphere in the following Python program as the task's diagram suggests.

These are the results:

The sphere following a path in the shape of the number eight

Here's my problem:

My program needs to do certain things in order to accomplish this:

  1. I have to draw two models of the sphere. One is located at (-8,4,0) and the other is located at (7,-3,0). I do this in order to place the spheres around two coordinates that are different from the origin.

  2. I make the first model rotate around the coordinate (-8,4,0) until it does a half-loop. After that, I change it to the second model whose origin is at (7,-3,0) and it rotates all the way until it does a whole loop. Once it happens, I change it again to the first model and does a whole loop and a half. It repeats again, thus following the trajectory of an infinite symbol.

This what I have done in my python program

run = True
angle = 0
x_position = -8
y_position = 4
points_matrix = generate_sphere_points(x0=x_position, y0=y_position, z0=0)
points_matrix2 = generate_sphere_points(x0=-x_position-1, y0=-y_position+1, z0=0)
index = False
matrices_points = [points_matrix, points_matrix2]
delta_angle = -4
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    draw_world_axes()
    glLineWidth(1)
    glTranslatef(x_position, y_position, 0)
    glRotatef(angle, 0, 0, 1)
    plot_parametric_mesh(matrices_points[index])
    pygame.display.flip()
    pygame.time.wait(10)

    if angle <= -180 or angle >= 180:
        index = not index
        x_position = -x_position - 1
        y_position = -y_position + 1
        delta_angle *= -1

    angle += delta_angle

pygame.quit()

While it works, I think you only need one sphere model. However,I haven't figured out how to make a sphere or even an object rotate around a coordinate different than the origin (0,0,0) without displacing that object as I create it. I know you can use glRotatef()*glTranslatef() to make an object rotate around the origin, but what I want is that it rotates around a coordinate I specify.

In this case, make one sphere model that rotates around one coordinate, then switches its center to a different coordinate, and repeat throughout the entire 8-loop.

Does anybody have an idea of how I could go about making a sphere rotate around a coordinate just using transformation matrices (translatef and rotatef) without having to change the sphere's center in its parametric equation and using only one model of the sphere to do the 8 loop instead of swapping the two models?


Solution

  • I think what you are looking for is a Lissajous curve. This can be created by applying a transformation with different angles to the two axes. If both angles are the same, you get a circle. If one angle is twice as big as the other, you get an 8:

    import pygame
    from OpenGL.GL import *
    import math
    
    pygame.init()
    display = (400, 400)
    screen = pygame.display.set_mode(display, pygame.DOUBLEBUF | pygame.OPENGL)
    clock = pygame.time.Clock()
    
    run = True
    angle1 = 0
    angle2 = 0
    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
    
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        x = math.cos(angle1) * 0.8
        y = math.sin(angle2) * 0.4
        glTranslatef(x, y, 0)
        angle1 += 0.01
        angle2 += 0.02
        glBegin(GL_QUADS)
        glVertex2f(-0.1, -0.1)
        glVertex2f(0.1, -0.1)
        glVertex2f(0.1, 0.1)
        glVertex2f(-0.1, 0.1)
        glEnd()
    
        clock.tick(100)
        pygame.display.flip()
    
    pygame.quit()