I'm new to OpenGL and I try to use the following code (GL_POLYGON) to draw a circle using python. But it seems like it only draws it when the points are added in a clockwise manner, otherwise, it just draws nothing
This successfully draws a circle
p = self.pos + Y_VEC * self.height # center of the circle
dv = self.dir_vec * self.radius # vector point forward
rv = self.right_vec * self.radius # vector point to right
sides = 20 # sides of the circle (circle is in fact polygon)
angle = 0
inc = 2 * math.pi / sides
glColor3f(0, 1, 0)
glPointSize(10.0)
glBegin(GL_POLYGON) # GL_POLYGON drawn but not shown in top view? GL_LINE_LOOP works
# glVertex3f(*p) # used for TRIANGLE_FAN
for i in range(sides+1):
pc = p + dv * math.cos(angle) + rv * math.sin(angle)
glVertex3f(*pc)
angle -= inc
glEnd()
Nothing rendered (only change is "-=" to "+=")
angle = 0
inc = 2 * math.pi / sides
glColor3f(0, 1, 0)
glPointSize(10.0)
glBegin(GL_POLYGON) # GL_POLYGON drawn but not shown in top view? GL_LINE_LOOP works
# glVertex3f(*p) # used for TRIANGLE_FAN
for i in range(sides+1):
pc = p + dv * math.cos(angle) + rv * math.sin(angle)
glVertex3f(*pc)
angle += inc # change here
glEnd()
Is this normal? What am I doing wrong?
Make sure face culling is disabled with glDisable(GL_CULL_FACE).
Related
Goal: export this output as a GIF.
This is working code, using Processing and Python.
This actually runs forever, how do I stop it after a while so as I can save as GIF?
import random
radius = 0
def setup():
global displayWidth, displayHeight, radius
size(displayWidth, displayHeight)
background(0)
noFill()
stroke(255, 25)
radius = height / 2
def draw():
global radius
center_x = width / 2
center_y = height / 2
beginShape()
for i in range(360):
_noise = noise(i * 0.02, float(frameCount) / 50)
x = center_x + radius * cos(radians(i)) * _noise
y = center_y + radius * sin(radians(i)) * _noise
curveVertex(x, y)
if radius == 0:
stroke(225, 0, 0, 10)
if radius == 100:
stroke(0, 225, 0, 25)
if radius == 200:
stroke (0, 0, 225, 25)
endShape(CLOSE)
radius -= 1
Please let me know if there is anything else I should add to post.
You can't. GIFs are not supported in processing. However you can use saveFrame() to saves a numbered sequence of images. Just call saveFrame() at the end of draw. There are numerous tools that can create a GIF from a list of images.
I am using Zelle graphics, but I suppose it is the same for any graphics program. From what I can tell, Zelle graphics does not have a "setheading" or "rotate" command for the rectangle (this is probably because the bounding box is created with two points). In turtle graphics it is easy with setheading(). So presently, I have a class that creates a polygon. I have a draw method and a rotate method. In the rotate method I am changing the Point(x1,y1) for each corner in a FOR LOOP. I broke out some graph paper to get the initial first few points to rotate, there is complex math I believe that would make this more palatable.
Presently this is my init method with these attributes, but I am sure there is a better way:
class create():
def __init__(self, p1x,p1y,p2x,p2y,p3x,p3y,p4x,p4y):
self.p1x = p1x
self.p2x = p2x
self.p3x = p3x
self.p4x = p4x
self.p1y = p1y
self.p2y = p2y
self.p3y = p3y
self.p4y = p4y
NOTE : this is not the entirety of my code. This is just the INIT method. I don't want anyone to think I have not tried this. I have with working code to rotate a polygon to the left about 5 points, but this is just using self.p1x=self.p1x+1 - I know this is not the correct way to rotate a rectangle. But at present I have a working model for rotating it slightly.
EDIT :
Here is the new work I have tried. I am still trying to understand this :
I need the midpoint, because the equation is derived from it. That is after I get the rotation correct. I am going to assume a midpoint in a 10x10 starting from the origin is going to be 5,5 for this example.
Does this code look ok so far? I am going to assume a rotation angle of 10 degrees for this example.
import math
#assume a 10 x 10 rectangle top left corner 0,0
#top left coords
x=0
y=0
#midpoints
midx = 5
midy = 5
#degrees to radians???????
angle=math.radians(10)
#new top left x
newx1 = x - midx * math.cos(angle) - (y-midy)*math.sin(angle)+ midx
#new top left y
newy1 = y - midy * math.sin(angle) + (x-midx)*math.cos(angle)+ midy
print(newx1, newy1)
The output is : 0.9442021232736115 -0.7922796533956911
This is version which works for me - it rotates Point around midx, midy with angle.
First it has to move middle to (0,0) and get current angle.
def rotate_point(point, angle, midx, midy):
old_x = point.x
old_y = point.y
old_cx = old_x - midx # move middle to (0,0)
old_cy = old_y - midy # move middle to (0,0)
angle_radians = math.radians(angle)
angle_sin = math.sin(angle_radians)
angle_cos = math.cos(angle_radians)
new_cx = old_cx * angle_cos - old_cy * angle_sin
new_cy = old_cx * angle_sin + old_cy * angle_cos
new_x = new_cx + midx # move back
new_y = new_cy + midy # move back
point = Point(new_x, new_y)
return point
And next I can use it to rotate Polygon
def rotate_polygon(polygon, angle, midx, midy):
new_points = []
for p in polygon.getPoints():
new_p = rotate_point(p, angle, midx, midy)
new_points.append(new_p)
return Polygon(*new_points)
Here full code which rotates
rectangle around middle point (angles: 30, 60),
triangle around one of corners (angles: 90, 180, 270).
import math
from graphics import *
def rotate_point(point, angle, midx, midy):
old_x = point.x
old_y = point.y
old_cx = old_x - midx # move middle to (0,0)
old_cy = old_y - midy # move middle to (0,0)
angle_radians = math.radians(angle)
angle_sin = math.sin(angle_radians)
angle_cos = math.cos(angle_radians)
new_cx = old_cx * angle_cos - old_cy * angle_sin
new_cy = old_cx * angle_sin + old_cy * angle_cos
new_x = new_cx + midx # move back
new_y = new_cy + midy # move back
point = Point(new_x, new_y)
return point
def rotate_polygon(polygon, angle, midx, midy):
new_points = []
for p in polygon.getPoints():
new_p = rotate_point(p, angle, midx, midy)
new_points.append(new_p)
return Polygon(*new_points)
# --- main ---
win = GraphWin()
# --- rectangle ---
x = 100
y = 100
w = 50
h = 50
midx = x + w//2 # rectangle middle
midy = y + h//2 # rectangle middle
p1 = Polygon(Point(x, y), Point(x+w, y), Point(x+w, y+h), Point(x, y+h), Point(x, y))
p1.draw(win)
p2 = rotate_polygon(p1, 30, midx, midy)
p2.draw(win)
p3 = rotate_polygon(p1, 60, midx, midy)
p3.draw(win)
# --- triangle ---
x = 60
y = 60
w = 50
h = 50
midx = x #+5 # triangle corner
midy = y #+5 # triangle corner
t1 = Polygon(Point(x, y), Point(x+w, y), Point(x+w//2, y+h), Point(x, y))
t1.draw(win)
t2 = rotate_polygon(t1, 90, midx, midy)
t2.draw(win)
t3 = rotate_polygon(t1, 180, midx, midy)
t3.draw(win)
t4 = rotate_polygon(t1, 270, midx, midy)
t4.draw(win)
# ---
win.getMouse()
win.close()
I need to rotate a triangle (Not an image) around at the center of the screen. I have seen other people answer this question, but the triangle could not point upwards.
I have tried to use other peoples functions, but they see to only work partly, like the function I mentioned above.
import pygame
disp=pygame.display.set_mode((200,200))
import math
def rotate_triange(mouse_pos,triangle_pos):
#The code here
import time
while True:
time.sleep(1)
pygame.Surface.fill(disp,(255,255,255))
center = (100,100)
radius = 10
mouse_position = pygame.mouse.get_pos()
for event in pygame.event.get():
pass
points = rotate_triangle((100,100),mouse_position)
pygame.draw.polygon(disp,(0,0,0),points)
pygame.display.update()
In pygame 2 dimensional vector arithmetic is implemented in pygame.math.Vector2.
Define a Vector2 object for the mouse position and the center of the triangle. Calculate the angle of vector form the center point to the mouse position (.angle_to()):
vMouse = pygame.math.Vector2(mouse_pos)
vCenter = pygame.math.Vector2(center)
angle = pygame.math.Vector2().angle_to(vMouse - vCenter)
Define the 3 points of the triangle around the (0, 0) and rotate them by the angle (.rotate())
points = [(-0.5, -0.866), (-0.5, 0.866), (2.0, 0.0)]
rotated_point = [pygame.math.Vector2(p).rotate(angle) for p in points]
To calculate the final points, the points have to b scaled and translated by the center of the triangle:
triangle_points = [(vCenter + p*scale) for p in rotated_point]
See the example:
import pygame
import math
def rotate_triangle(center, scale, mouse_pos):
vMouse = pygame.math.Vector2(mouse_pos)
vCenter = pygame.math.Vector2(center)
angle = pygame.math.Vector2().angle_to(vMouse - vCenter)
points = [(-0.5, -0.866), (-0.5, 0.866), (2.0, 0.0)]
rotated_point = [pygame.math.Vector2(p).rotate(angle) for p in points]
triangle_points = [(vCenter + p*scale) for p in rotated_point]
return triangle_points
disp=pygame.display.set_mode((200,200))
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
mouse_position = pygame.mouse.get_pos()
points = rotate_triangle((100, 100), 10, mouse_position)
pygame.Surface.fill(disp, (255,255,255))
pygame.draw.polygon(disp, (0,0,0), points)
pygame.display.update()
A version of the algorithm, without the use of pygame.math.Vector2, looks as follows:
def rotate_triangle(center, scale, mouse_pos):
dx = mouse_pos[0] - center[0]
dy = mouse_pos[1] - center[1]
len = math.sqrt(dx*dx + dy*dy)
dx, dy = (dx*scale/len, dy*scale/len) if len > 0 else (1, 0)
pts = [(-0.5, -0.866), (-0.5, 0.866), (2.0, 0.0)]
pts = [(center[0] + p[0]*dx + p[1]*dy, center[1] + p[0]*dy - p[1]*dx) for p in pts]
return pts
Note this version is probably faster. It needs a math.sqrt operation, in compare to math.atan2 which is probably used by .angle_to() and math.sin respectively math.cos which is probably used by .rotate(), of the former algorithm.
The result coordinates are the same.
I am trying to make my circle bounce off of my rectangle using Zelle graphics.py. Once the circle bounces off of the rectangle I wanted it to keep moving randomly. Here is my code so far, and it's working!
Also I know that each circle graphics technically can use the points of the smallest possible square that would fit around the circle to do the collision but I'm having trouble with doing that.
from graphics import *
import random
def delay(d):
for i in range(d):
for i in range(50):
pass
#-------------------------------------------------
def main():
win=GraphWin("Moving Circle",500,400)
win.setBackground('white')
pt= Point(100,200)
cir=Circle(pt,30)
#changes the color of the circle for each game
r = random.randrange(256)
b = random.randrange(256)
g = random.randrange(256)
color = color_rgb(r, g, b)
cir.setFill(color)
cir.draw(win)
#rectangle
rec = Rectangle(Point(450,450), Point(275, 425))
rec.draw(win)
rec.setFill('black')
#-------------------------------------------------
pt5 = Point(250,30)
instruct1=Text(pt5, "click multiple times to start(rectangle can take multiple clicks to move)")
instruct1.setTextColor('black')
instruct1.draw(win)
#-------------------------------------------------
p=cir.getCenter()
p2=win.getMouse()
dx=1
dy=1
keepGoing=True
while keepGoing:
d = 100
delay(d)
cir.move(dx,dy)
p=cir.getCenter()
p2=win.checkMouse()
instruct1.setText("")
#rectanlge
isClicked= win.checkMouse()
if isClicked:
rp = isClicked
rc = rec.getCenter()
rdx = rp.getX() - rc.getX()
rdy = rp.getY() - rc.getY()
rec.move(rdx,rdy)
#circle
if((p.getX()-30)<=0.0) or ((p.getX()+30)>=500):
dx= -dx
if((p.getY()-30)<=0.0) or ((p.getY()+30)>=400):
dy=-dy
p3=win.checkMouse()
main()
I know that each circle graphics technically can use the points of the
smallest possible square that would fir around the circle to do the
collision
I'm playing with an alternate idea -- we could consider a circle around the rectangle instead of a square around the circle. The issue for me is that we not only need to detect collision, but come out with a sense of which way to move away from the other object. It's not just True and False but rather a (dx, dy) type of result.
Obviously, a circle around the rectangle is too crude, but suppose it were lots of smaller circles making up the rectangle and we measure circle center to center distance to detect a hit:
A hit on just a central (green) rectangle circle means reverse the vertical direction of the big circle. A hit on just the end (red) circle means reverse the horizontal direction of the big circle. And we can detect both kinds of hits and reverse the big circle completely.
Here's my rework of your code with the above in mind -- I also fixed your multiple clicking issue and made lots of style changes:
from random import randrange
from graphics import *
WIDTH, HEIGHT = 500, 400
RADIUS = 30
def delay(d):
for _ in range(d):
for _ in range(50):
pass
def distance(p1, p2):
return ((p2.getX() - p1.getX()) ** 2 + (p2.getY() - p1.getY()) ** 2) ** 0.5
def intersects(circle, rectangle):
dx, dy = 1, 1 # no change
center = circle.getCenter()
rectangle_radius = (rectangle.p2.getY() - rectangle.p1.getY()) / 2
rectangle_width = rectangle.p2.getX() - rectangle.p1.getX()
y = rectangle.getCenter().getY()
for x in range(int(rectangle_radius * 2), int(rectangle_width - rectangle_radius * 2) + 1, int(rectangle_radius)):
if distance(center, Point(rectangle.p1.getX() + x, y)) <= rectangle_radius + RADIUS:
dy = -dy # reverse vertical
break
if distance(center, Point(rectangle.p1.getX() + rectangle_radius, y)) <= rectangle_radius + RADIUS:
dx = -dx # reverse horizontal
elif distance(center, Point(rectangle.p2.getX() - rectangle_radius, y)) <= rectangle_radius + RADIUS:
dx = -dx # reverse horizontal
return (dx, dy)
def main():
win = GraphWin("Moving Circle", WIDTH, HEIGHT)
circle = Circle(Point(WIDTH / 5, HEIGHT / 2), RADIUS)
# change the color of the circle for each game
color = color_rgb(randrange(256), randrange(256), randrange(256))
circle.setFill(color)
circle.draw(win)
# rectangle
rectangle = Rectangle(Point(275, 425), Point(450, 450)) # off screen
rectangle.setFill('black')
rectangle.draw(win)
dx, dy = 1, 1
while True:
delay(100)
circle.move(dx, dy)
# rectangle
isClicked = win.checkMouse()
if isClicked:
point = isClicked
center = rectangle.getCenter()
rectangle.move(point.getX() - center.getX(), point.getY() - center.getY())
# circle
center = circle.getCenter()
if (center.getX() - RADIUS) <= 0.0 or (center.getX() + RADIUS) >= WIDTH:
dx = -dx
if (center.getY() - RADIUS) <= 0.0 or (center.getY() + RADIUS) >= HEIGHT:
dy = -dy
# collision bounce
x, y = intersects(circle, rectangle)
dx *= x
dy *= y
main()
Not perfect, but something to play around with, possibly plugging in a better intersects() implementation.
I need to make a circle by adjusting conditions on the heights, this program using a lot of random circles but I am unsure where to go from here? I am trying to use the following equation d = (sqrt)((x1 –x2)^2 +(y1 – y2)^2). Right now the program draws many random circles, so adjusting the formula i should be able to manipulate it so that certain circles are red in the centre (like the japan flag).
# using the SimpleGraphics library
from SimpleGraphics import *
# use the random library to generate random numbers
import random
diameter = 15
##
# returns a valid colour based on the input coordinates
#
# #param x is an x-coordinate
# #param y is a y-coordinate
# #return a colour based on the input x,y values for the given flag
##
def define_colour(x,y):
##
if y < (((2.5 - 0)**2) + ((-0.5 - 0)**2)**(1/2)):
c = 'red'
else:
c = 'white'
return c
return None
# repeat until window is closed
while not closed():
# generate random x and y values
x = random.randint(0, getWidth())
y = random.randint(0, getHeight())
# set colour for current circle
setFill( define_colour(x,y) )
# draw the current circle
ellipse(x, y, diameter, diameter)
Here's some code that endlessly draws circles. Circles that are close to the centre of the screen will be drawn in red, all other circles will be drawn in white. Eventually, this will create an image similar to the flag of Japan, although the edge of the inner red "circle" will not be smooth.
I have not tested this code because I don't have the SimpleGraphics module, and I'm not having much success locating it via Google or pip.
from SimpleGraphics import *
import random
diameter = 15
width, height = getWidth(), getHeight()
cx, cy = width // 2, height // 2
# Adjust the multiplier (10) to control the size of the central red portion
min_dist_squared = (10 * diameter) ** 2
def define_colour(x, y):
#Calculate distance squared from screen centre
r2 = (x - cx) ** 2 + (y - cy) ** 2
if r2 <= min_dist_squared:
return 'red'
else:
return 'white'
# repeat until window is closed
while not closed():
# generate random x and y values
x = random.randrange(0, width)
y = random.randrange(0, height)
# set colour for current circle
setFill(define_colour(x, y))
# draw the current circle
ellipse(x, y, diameter, diameter)