I am trying to create a pendulum with oscillating string and mass which is expected to oscillate for a period of time and then stop. From what I wrote, the mass could not stop oscillating. Kindly help me out.
import pyglet
import pymunk
import chipmunk
from pymunk import Vec2d
from pymunk.pyglet_util import DrawOptions
window = pyglet.window.Window(1300,700,"Oscillation",resizable=False)#
W,H
options=DrawOptions()
space=pymunk.Space()
space.gravity= 0,-100
b0= space.static_body
p0= 100,200
body= pymunk.Body(mass=0.5, moment =1)
body.position = (100,50)
circle= pymunk.Circle(body, radius=20)
joint= pymunk.constraint.DampedSpring(b0, body,p0, (0,0), 100, 10, 0)
space.add(body, circle, joint)
#window.event
def on_draw():
window.clear()
space.debug_draw(options)
def update(dt):
space.step(dt)
if __name__=="__main__":
pyglet.clock.schedule_interval(update,1.0/60)
pyglet.app.run()
You did not provide damping in the last parameter of the call:
joint= pymunk.constraint.DampedSpring(b0, body,p0, (0,0), 100, 10, 0)
So it will oscillate forever. Put some amount of damping and it should slow down.
Related
I have created a complete snake game using C++ and OpenGL before, and I want to do the same using Python, pygame, and PyOpenGL. The current problem I have is that after I spawn a fruit, it does not appear on the screen. Here's the code for my main function:
def main(): # Main function
# Initialize game components
game = Game(800, 600)
test_fruit = game.spawn_fruit(Point(100, 100))
# Initialize pygame module
pygame.init()
pygame.display.set_mode(game.get_window_size(), DOUBLEBUF | OPENGL)
pygame.display.set_caption("Python Game")
# Define variable to control main loop
running = True
# Main loop
while running:
# event handling, gets all event from the event queue
for event in pygame.event.get():
# only do something if the event is of type QUIT
if event.type == pygame.QUIT:
# change the value to False, to exit the main loop
running = False
# Modify game properties
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
game.draw_shapes()
pygame.display.flip()
pygame.time.wait(5)
It's possible I'm missing a pygame or pyopengl function, but I'm not sure. I've also tried changing pygame.display.flip() to pygame.display.update(), yet it gives me an error ("cannot update an OpenGL display") instead.
Here's the code for the shape I am attempting to display:
class Circle:
def __init__(self, pivot: Point, radius: int, sides: int, fill: bool, color: Color):
self.pivot = pivot
self.radius = radius
self.sides = sides
self.fill = fill
self.color = color
# Draw the shape of the circle
def draw(self):
glColor3f(self.color.r, self.color.g, self.color.b)
if self.fill:
glBegin(GL_POLYGON)
else:
glBegin(GL_LINE_LOOP)
for i in range(100):
cosine = self.radius * cos(i*2*pi/self.sides) + self.pivot.x
sine = self.radius * sin(i*2*pi/self.sides) + self.pivot.y
glVertex2f(cosine, sine)
glEnd()
OpenGL coordinates are in range [-1.0, 1.0] (Normalized Device Space). The Normalized device space is a unique cube from the left, bottom, near (-1, -1, -1) to the right, top, far (1, 1, 1).
If you want to use "window" coordinates, you must specify an Orthographic projection using glOrtho:
glOrtho(0, 800, 600, 0, -1, 1)
Choose the matrix mode with glMatrixMode and load the Identity matrix with glLoadIdentity.
Example:
def main(): # Main function
# Initialize game components
game = Game(800, 600)
test_fruit = game.spawn_fruit(Point(100, 100))
# Initialize pygame module
pygame.init()
pygame.display.set_mode(game.get_window_size(), DOUBLEBUF | OPENGL)
pygame.display.set_caption("Python Game")
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, 800, 600, 0, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
# Define variable to control main loop
running = True
# [...]
I have a function that calculates the Mandelbrot set in an numpy array of size (500,500).
I can draw it in matplotlib. Now i want to zoom into the image via mouse click. I want to use pyglet for that but struggle with rendering. I know this (not working) solution is inelegant, so I am happy for advice how to do this better!
PS: The zoom level is set arbitrary right now, my problem I ask for help is the refreshing.
The functions I replaced with a docstring can be found here
from pyglet.window import mouse
import pyglet
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import cm
from pyglet import image
"""
def calc_pixel_array(x_center: float = 0, y_center: float = 0,
scale: float = 1.0, width: int = 500,
height: int = 500, threshold: int = 200):
Returns numpy array with shape (500,500) with color values
def plot_pixel_array(pixel_array, cmap: str = 'gnuplot', show_plot: bool = False):
Draws pixel_array via matplotlib and saves in curent_mandelbrot.png
"""
pixel_array = calc_pixel_array()
plot_pixel_array(pixel_array)
scale = 1
i = 1
window = pyglet.window.Window(width = 500, height = 500)
image = pyglet.resource.image('curent_mandelbrot.png')
pic = image.load('curent_mandelbrot.png')
#window.event
def on_draw():
window.clear()
pic.blit(0, 0)
#window.event
def on_mouse_press(x, y, button, modifiers):
if button == mouse.LEFT:
i = np.random.randint(5)+1
pixel_array = calc_pixel_array(scale/(10*i))
plot_pixel_array(pixel_array)
window.clear()
image = pyglet.resource.image('curent_mandelbrot.png')
image.blit(0,0)
pyglet.app.run()
Hi after a lot of trying around I found this
def draw_pixel_array(self, window):
main_batch = pyglet.graphics.Batch()
shape = self.pixel_array.shape
for x in range(shape[0]):
for y in range(shape[1]):
color = int(self.pixel_array[x, y])
pyglet.graphics.draw(1, pyglet.gl.GL_POINTS,
('v2i', (x, y)),
('c3B', (color, color, 0)))
main_batch.draw()
Got it running by removing the on draw and putting everything (calculating and drawing) in the on_mouse_press function.
You will find the complete solution here. It is still very slow in drawing, any suggestions there are warmly welcome!
Beginner in pyglet. I have an issue when drawing GL_POINT using pyglet.graphicss.draw(). I want this GL_POINT to be drawn after another on the next pixel buffer, but it seems the function does not keep the last GL_POINT to be drawn on the next pixel buffer.
import pyglet
from pyglet.gl import *
from pyglet.window import key # for key input, on_key_press
window = pyglet.window.Window(800, 600) # create a window object with the resolution of 800x600
window.set_caption('window title')
glClear(GL_COLOR_BUFFER_BIT)
#window.event
def on_key_press(symbol, modifiers): # keyboard input handler
if symbol == key.L: # Drawing a center point
print("DRAWING TEST A POINT (400, 300)")
pyglet.graphics.draw(
1, pyglet.gl.GL_POINTS,
('v2i', (400, 300))
)
elif symbol == key.K: # Drawing a bit further 100 more horizontally from center point
print("DRAWING TEST A POINT (500, 300)")
pyglet.graphics.draw(
1, pyglet.gl.GL_POINTS,
('v2i', (500, 300))
)
pyglet.app.run()
Pressing L would draw a center point.
Then pressing K would draw 100 more horizontally from the center point with the last center point gone.
Where is the bug? is there something wrong with my code? if not,
my guess would be, does pyglet.graphicss.draw() function actually redraw one after another primitive shape? How do I code to draw one after another?
The issue is caused by Double buffering. You can solve the issue by drawing the point to both buffers. Draw the point twice and swap the OpenGL front and back buffers in between by (flip).
pyglet.graphics.draw(
1, pyglet.gl.GL_POINTS,
('v2i', (400, 300))
)
window.flip()
pyglet.graphics.draw(
1, pyglet.gl.GL_POINTS,
('v2i', (400, 300))
)
But I recommend to add the points to a list and to draw the list. e.g.:
import pyglet
from pyglet.gl import *
from pyglet.window import key # for key input, on_key_press
points = []
window = pyglet.window.Window(800, 600) # create a window object with the resolution of 800x600
window.set_caption('window title')
glClear(GL_COLOR_BUFFER_BIT)
#window.event
def on_key_press(symbol, modifiers): # keyboard input handler
global points
if symbol == key.L: # Drawing a center point
print("DRAWING TEST A POINT (400, 300)")
points += [400, 300]
elif symbol == key.K: # Drawing a bit further 100 more horizontally from center point
print("DRAWING TEST A POINT (500, 300)")
points += [500, 300]
pyglet.graphics.draw(len(points) // 2, pyglet.gl.GL_POINTS, ('v2i', points))
pyglet.app.run()
How can I get the value of pixel color using glReadPixels()? I did so many try but getting wrong value.
My background color is blue(0,1,1) and I have drawn a circle with boundary color red(1,0,0) and I want to get the color of any of boundary point. So it must give me red. but I am getting background color.
Here is my code in Python3 and OpenGL
from OpenGL.GLU import *
from OpenGL.GLUT import *
import time
from math import *
import numpy
import sys
def init():
glClearColor(0.0,1.0,1.0,0.0)
glClear(GL_COLOR_BUFFER_BIT)
glPointSize(3.0)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluOrtho2D(0.0,640.0,0.0,640.0)
def circle():
for i in range(361):
m=float(50*cos(i*pi/180.0))+320
n=float(50*sin(i*pi/180.0))+320
setpixc(m,n)
print(m,n)
redinput()
def redinput():
global x,y
x=int(input("enter x:"))
y=int(input("enter y:"))
setpixc(x,y)
pixel=[]
c=glReadPixels(x,y,1.0,1.0,GL_RGB,GL_UNSIGNED_BYTE,None)
print(c)
string_pixels=numpy_pixel.tolist()
print(string_pixels)
def setpixc(xcor,ycor):
glBegin(GL_POINTS)
glColor3f(1.0,0.0,0.0)
glVertex2f(xcor,ycor)
glEnd()
glFlush()
def Display():
circle()
print("hello")
def main():
glutInit(sys.argv)
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
glutInitWindowSize(600,600)
glutInitWindowPosition(10,10)
glutCreateWindow("line-dda")
glutDisplayFunc(Display)
init()
glutMainLoop()
main()
You are using an orthographic projection, which which projects the coordinates to the rectangle form (0, 0) to (640, 640):
gluOrtho2D(0.0,640.0,0.0,640.0)
But your window size is (600, 600):
glutInitWindowSize(600,600)
This causes that the coordinates in the range from (0, 0) to (640, 640) are drawn to the viewport from (0, 0) to (600, 600), by glVertex2f:
But when the coordinates are read by glReadPixels, then you would have to use viewport (pixel) coordinates.
To solve your is you can change the window size from (600, 600) to (640, 640):
glutInitWindowSize(640, 640)
Now e.g.
x=270
y=320
will return a red pixel.
Note, if you don't want to change the window size, then you would have to scale the input coordinates by 600/640.
scale = 600/640
c=glReadPixels(x*scale,y*scale,1.0,1.0,GL_RGB,GL_UNSIGNED_BYTE,None)
e.g.
x = 270 * 600 / 640 = 253
y = 320 * 600 / 640 = 300
Further note, that drawing by glBegin/glEnd sequences is deprecated since several years.
Read about Fixed Function Pipeline and see Vertex Specification and Shader for a state of the art way of rendering.
Anyway, I recommend to use double buffering
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)
and to do a single buffer swap, after the entire circle was drawn. Skip the glFlush call in setpixc and add a single glutSwapBuffers call to the Display function and don't forget to clear the display before rendering:
def Display():
glClear(GL_COLOR_BUFFER_BIT)
circle()
glutSwapBuffers()
glutPostRedisplay()
redinput()
print("hello")
It is up to you if you want to draw the circle by single points
def circle():
glPointSize(3.0)
glColor3f(1.0,0.0,0.0)
glBegin(GL_POINTS)
for i in range(360):
m=float(50*cos(i*pi/180.0))+320
n=float(50*sin(i*pi/180.0))+320
glVertex2f(m,n)
glEnd()
or a coherent line:
def circle():
glLineWidth(3.0)
glColor3f(1.0,0.0,0.0)
glBegin(GL_LINE_LOOP)
for i in range(360):
m=float(50*cos(i*pi/180.0))+320
n=float(50*sin(i*pi/180.0))+320
glVertex2f(m,n)
glEnd()
If you want to get the color of a pixel by a mouse click, the you can set a mouse call back by glutMouseFunc:
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from math import *
def init():
global width, height
glClearColor(0.0, 1.0, 1.0, 0.0)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluOrtho2D(0.0, width, 0.0, height)
def circle():
glLineWidth(3.0)
glColor3f(1.0, 0.0, 0.0)
glBegin(GL_LINE_LOOP)
for i in range(360):
m=float(50*cos(i*pi/180.0))+320
n=float(50*sin(i*pi/180.0))+320
glVertex2f(m, n)
glEnd()
def Mouse(button, state, x, y):
global mouse_x, mouse_y, get_input
if button == GLUT_LEFT_BUTTON and state == GLUT_DOWN:
mouse_x = x
mouse_y = height - y # the y coordinate of the mouse has to be flipped
get_input = True
def redinput(x, y):
c = glReadPixels(x, y, 1.0, 1.0, GL_RGB,GL_UNSIGNED_BYTE, None)
print(c)
def Display():
global mouse_x, mouse_y, get_input
glClear(GL_COLOR_BUFFER_BIT)
circle()
glutSwapBuffers()
glutPostRedisplay()
if get_input:
redinput(mouse_x, mouse_y)
get_input=False
def main():
global width, height
glutInit(sys.argv)
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)
glutInitWindowSize(width, height)
glutInitWindowPosition(10, 10)
glutCreateWindow("line-dda")
glutDisplayFunc(Display)
glutMouseFunc(Mouse)
init()
glutMainLoop()
width = 640
height = 640
mouse_x = 0
mouse_y = 0
get_input = False
main()
There are couple of problems with your code.
your window size is different from data you are using in gluOrtho2D
you are relying on pixel exact rasterization, and OpenGL does not guarantee it.
The answer you are searching for is described in "Red Book" (i.e. OpenGL Programming Guide), specifically in Appendix G, Programming Tips. I'd also suggest you to read Appendix H, Invariance. Online version can be found on following link: https://www.glprogramming.com/red/
Also,
you do not need to call glFlush after every single point drawn, call it once just before glReadPixels...
you are using glBegin/glEnd pair for every single point, which is huge waste of
resources. you can draw complete circle using one glBegin/glEnd pair:
glBegin(GL_POINTS)
glColor3f(1.0, 0.0, 0.0)
for i in range(361):
x=float(50*cos(i*pi/180.0))+320
y=float(50*sin(i*pi/180.0))+320
glVertex2f(x,y)
glEnd()
you are using very dense set of GL_POINTS to draw circle, but this will not prodice correct circle. If radius is smaller, you will have multiple rasterization of same window pixel. If you increase radius enough, it will result in set of unconnected points. In your situation, I would use GL_LINE_LOOP:
glBegin(GL_LINE_LOOP)
glColor3f(1.0, 0.0, 0.0)
for i in range(0, 360, 5):
x=float(50*cos(i*pi/180.0))+320
y=float(50*sin(i*pi/180.0))+320
glVertex2f(x,y)
glEnd()
last but not least, this is ancient way of OpenGL usage. Unless you have very good reason, I'd suggest to move to some newer OpenGL version.
I have to evaluate the performance/functionality of Pyopengl vs Pyglet. The main concern is that it can't drop any frame while using high FPS. Before I start learning one of then, I need to see if it can do what the client needs.
I am trying to alternate (on vsync) between red and green (in fullscreen mode). If anybody can give me a good tutorial site, or help me with an example, it would be very nice.
I have looked at this post (and more):
FPS with Pyglet half of monitor refresh rate
Made a modification, but can't see how to switch from one color to another one on Vsync.
import pyglet
from pyglet.gl import *
fps = pyglet.clock.ClockDisplay()
# The game window
class Window(pyglet.window.Window):
def __init__(self):
super(Window, self).__init__(fullscreen=True, vsync = False)
self.flipScreen = 0
glClearColor(1.0, 1.0, 1.0, 1.0)
# Run "self.update" 128 frames a second and set FPS limit to 128.
pyglet.clock.schedule_interval(self.update, 1.0/128.0)
pyglet.clock.set_fps_limit(128)
def update(self, dt):
self.flipScreen = not self.flipScreen
pass
def on_draw(self):
pyglet.clock.tick() # Make sure you tick the clock!
if self.flipScreen == 0:
glClearColor(0, 1, 0, 1.0)
else:
glClearColor(1, 0, 0, 1.0)
self.clear()
fps.draw()
# Create a window and run
win = Window()
pyglet.app.run()
I have seen a lot of tutorials, but I haven't been able to understand how to run this one test.
Thanks for your help.
Here is a code that does it, using pyglet. It has been tested on 3 monitors at 60 Hz and 120Hz. Please note that the use of global variable is bad. It may not be very clean, but it clearly shows that vsync is taken into account.This is the purpose of the code to test the functionnality of vsync.
import pyglet
from pyglet.gl import *
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
# Direct OpenGL commands to this window.
config = Config(double_buffer = True)
window = pyglet.window.Window(config = config)
window.set_vsync(True)
window.set_fullscreen(True)
colorSwap = 0
fullscreen = 1
fps_display = pyglet.clock.ClockDisplay()
def on_draw(dt):
global colorSwap
glClear(GL_COLOR_BUFFER_BIT) # Clear the color buffer
glLoadIdentity() # Reset model-view matrix
glBegin(GL_QUADS)
if colorSwap == 1:
glColor3f(1.0, 0.0, 0.0)
colorSwap = 0
else:
glColor3f(0.0, 1.0, 0.0)
colorSwap = 1
glVertex2f(window.width, 0)
glVertex2f(window.width, window.height)
glVertex2f(0.0, window.height)
glVertex2f(0.0, 0.0)
glEnd()
fps_display.draw()
#window.event
def on_key_press(symbol, modifiers):
global fullscreen
if symbol == pyglet.window.key.F:
if fullscreen == 1:
window.set_fullscreen(False)
fullscreen = 0
else:
window.set_fullscreen(True)
fullscreen = 1
elif symbol == pyglet.window.key.ESCAPE:
print ''
dt = pyglet.clock.tick()
pyglet.clock.schedule_interval(on_draw, 0.0001)
pyglet.app.run()