Newbie question about updating multiple objects ...

F

fivenastydisco

All,

Apologies in advance: I'm new to Python and OO, and I'm sure this is a
really simple question, but I just can't seem to crack it on my own, or
through looking at examples. I'm playing around with VPython
(http://www.vypthon.org) to try and teach myself about objects: I've
got a box that I've put some balls in, and I'm trying to get said balls
to move around and bounce off the walls of the box -- that's literally
it. I can put multiple balls in the box, but I can't get them to update
their position -- it works when I have one ball, but now that I have a
list of balls, I can't get them to move.

I've copied in the code I'm using below, as it's not very long -- can
anyone help me?

<--START-->

from visual import *
from random import randrange # to create random numbers

numballs = 5 # number of balls
balls = [] # a list to contain the balls

# set up a box to collide with
side_length = 100
minx = side_length * -10
maxx = side_length * 10
miny = side_length * -4
maxy = side_length * 4

# create a wireframe of space
left = curve(pos=[(minx, miny),(minx, maxy)], color = color.white)
top = curve(pos = [(minx, maxy),(maxx, maxy)], color = color.white)
right = curve(pos = [(maxx, maxy),(maxx, miny)], color = color.white)
bottom = curve(pos = [(maxx, miny),(minx,miny)], color = color.white)

#create some balls
for b in range(numballs): # for each ball
x = randrange(minx, maxx)
y = randrange(miny, maxy)

balls.append(sphere(pos = (x,y), radius = 50, color = color.red)) #
add balls

dt = 0.05 # time interval
velocity = vector(2,0.2,1) # velocity of the balls

while (1==1): # forever ...
rate(50)
for b in range(numballs): # for each ball ...

if balls.x < minx: # check it hasn't overrun a boundary
balls.x = maxx
if balls.x > maxx:
balls.minx
if balls.y < miny:
balls.y = maxy
if balls.y > maxy:
balls.y = miny

# NB betting this is where it's going wrong ...
balls.pos = balls.pos + (velocity * dt) # update the
position of the ball

<--END-->

Any ideas? I've run out of options. Any help would be very *gratefully*
appreciatedd!!

Thanks in advance

JW
 
D

Diez B. Roggisch

I've copied in the code I'm using below, as it's not very long -- can
anyone help me?

I'll have some remarks:

<--START-->

from visual import *
from random import randrange # to create random numbers

numballs = 5 # number of balls
balls = [] # a list to contain the balls

# set up a box to collide with
side_length = 100
minx = side_length * -10
maxx = side_length * 10
miny = side_length * -4
maxy = side_length * 4

# create a wireframe of space
left = curve(pos=[(minx, miny),(minx, maxy)], color = color.white)
top = curve(pos = [(minx, maxy),(maxx, maxy)], color = color.white)
right = curve(pos = [(maxx, maxy),(maxx, miny)], color = color.white)
bottom = curve(pos = [(maxx, miny),(minx,miny)], color = color.white)

#create some balls
for b in range(numballs): # for each ball
x = randrange(minx, maxx)
y = randrange(miny, maxy)


Don't use range - use xrange. range actually creates a list of numbers,
where xrange creates a iterator that will just return the subsequent
numbers. The difference: the first solution consumes memory linear to the
number of numbers!
balls.append(sphere(pos = (x,y), radius = 50, color = color.red)) #
add balls

dt = 0.05 # time interval
velocity = vector(2,0.2,1) # velocity of the balls

while (1==1): # forever ...
rate(50)
for b in range(numballs): # for each ball ...

This is a C-ism (or JAVA-ism if you like). The for-loop in python allows you
to iterate over objects in iterables. And list is one of those. So

for ball in balls:
ball.x = ...

does the trick. And if you need an index, the commonly used idiom is

for i, ball in enumerate(balls):
...
if balls.x < minx: # check it hasn't overrun a boundary
balls.x = maxx
if balls.x > maxx:
balls.minx
if balls.y < miny:
balls.y = maxy
if balls.y > maxy:
balls.y = miny


You should do that checks above _after_ you changed the coordinates, not
before!
# NB betting this is where it's going wrong ...
balls.pos = balls.pos + (velocity * dt) # update the
position of the ball


This could be the problem: you are using a python tuple and a vector object
and add them. Are you sure that works? The python tuples themselves
concatenate under the +-operator. So you'd create a large tuple of
coordinate differences here.

I suppose that using a vector for the coordinates or doing the calculation
component-wise will solve the problem.

HTH,

Diez
 
D

donkeyboy

Awesome Diez -- I'll give this a go tomorrow, and hopefully it will all
work!!
I've copied in the code I'm using below, as it's not very long -- can
anyone help me?

I'll have some remarks:

<--START-->

from visual import *
from random import randrange # to create random numbers

numballs = 5 # number of balls
balls = [] # a list to contain the balls

# set up a box to collide with
side_length = 100
minx = side_length * -10
maxx = side_length * 10
miny = side_length * -4
maxy = side_length * 4

# create a wireframe of space
left = curve(pos=[(minx, miny),(minx, maxy)], color = color.white)
top = curve(pos = [(minx, maxy),(maxx, maxy)], color = color.white)
right = curve(pos = [(maxx, maxy),(maxx, miny)], color = color.white)
bottom = curve(pos = [(maxx, miny),(minx,miny)], color = color.white)

#create some balls
for b in range(numballs): # for each ball
x = randrange(minx, maxx)
y = randrange(miny, maxy)


Don't use range - use xrange. range actually creates a list of numbers,
where xrange creates a iterator that will just return the subsequent
numbers. The difference: the first solution consumes memory linear to the
number of numbers!
balls.append(sphere(pos = (x,y), radius = 50, color = color.red)) #
add balls

dt = 0.05 # time interval
velocity = vector(2,0.2,1) # velocity of the balls

while (1==1): # forever ...
rate(50)
for b in range(numballs): # for each ball ...

This is a C-ism (or JAVA-ism if you like). The for-loop in python allows you
to iterate over objects in iterables. And list is one of those. So

for ball in balls:
ball.x = ...

does the trick. And if you need an index, the commonly used idiom is

for i, ball in enumerate(balls):
...
if balls.x < minx: # check it hasn't overrun a boundary
balls.x = maxx
if balls.x > maxx:
balls.minx
if balls.y < miny:
balls.y = maxy
if balls.y > maxy:
balls.y = miny


You should do that checks above _after_ you changed the coordinates, not
before!
# NB betting this is where it's going wrong ...
balls.pos = balls.pos + (velocity * dt) # update the
position of the ball


This could be the problem: you are using a python tuple and a vector object
and add them. Are you sure that works? The python tuples themselves
concatenate under the +-operator. So you'd create a large tuple of
coordinate differences here.

I suppose that using a vector for the coordinates or doing the calculation
component-wise will solve the problem.

HTH,

Diez
 
S

Scott David Daniels

<some VPython code>
Any ideas? I've run out of options. Any help would be very *gratefully*
appreciatedd!!

Your time-step is too small.
Simply try: dt = .5, and you should see (slow) motion.

Note, I'd update the position, then wrap it, rather than wrapping and
then updating.
 
C

cben

Hello and welcome to Python.
Apologies in advance: I'm new to Python and OO, and I'm sure this is a
really simple question, but I just can't seem to crack it on my own, or
through looking at examples. I'm playing around with VPython
(http://www.vypthon.org) to try and teach myself about objects: I've
got a box that I've put some balls in, and I'm trying to get said balls
to move around and bounce off the walls of the box -- that's literally
it. I can put multiple balls in the box, but I can't get them to update
their position -- it works when I have one ball, but now that I have a
list of balls, I can't get them to move.

I've copied in the code I'm using below, as it's not very long -- can
anyone help me?
Below is your code with the many impovements (including other
suggestions in this thread). I've used double (``##``) comments to
indicate all changes.

Stylistic improvements aside, the biggest issue was that your code
didn't bounce the balls off the walls but just wrapped them to the
opposite wall. The simplest example of bouncing appears in
http://vpython.org/vpythonprog.htm
Bouncing requires a separate velocity per ball (when one bounces, the
others keep direction). Note that `b.velocity` has no signifacance to
the sphere object -- in Python you can add attributes to most objects
whenever you want. OOP purists would say this is dirty but
"practicality beats purity". You could also use a separate
`velocities` dictionary if you prefer.

# <--START-->

## Say this at the top if you use division. This fixes 3/2 to equal
1.5.
## (3//2 == 1 if you really want the floor of the ratio.)
from __future__ import division

from visual import *
from random import uniform ## why limit to integral [a,b) values?

# set up a box to collide with
side_length = 100
minx = side_length * -10
maxx = side_length * 10
miny = side_length * -4
maxy = side_length * 4

numballs = 5 # number of balls
## List comprehensions simplify the ``b=[]; for...: b.append(...)``
idiom
balls = [sphere(pos=(uniform(minx, maxx), uniform(miny, maxy)),
radius=50, color=color.red) for i in range(numballs)]

# create a wireframe of space
## Common spacing conventions: space after commas and around = in
## assignements but not = in keyword arguments
left = curve(pos=[(minx, miny), (minx, maxy)], color=color.white)
top = curve(pos=[(minx, maxy), (maxx, maxy)], color=color.white)
right = curve(pos=[(maxx, maxy), (maxx, miny)], color=color.white)
bottom = curve(pos=[(maxx, miny), (minx,miny)], color=color.white)

## Note that your `dt` was not equal to 1/rate, so you were not
simulating
## in real time. I'll assume your velocity is correct but will
increase
## the simulation speedup to make it less boring.
fps = 50 ## this should not be touched once you match it to your
computer
speedup = 40.0 ## tune this to watch the process at different speeds
dt = speedup/fps ## note that a big `dt` will increase simulation
errors

## bouncing requires separate velocity per ball
for b in balls:
## removed z component since you want a 2D simulation (right?)
b.velocity = vector(2, 0.2)

while True: ## the idiomatic way to say forever ...
rate(fps) ## see comment above
for b in balls: ## usually you don't need the indexes, only the
items

## First update, then bounce - this way you never display
## a ball outside the box.
b.pos += (b.velocity * dt) ## += avoids repeating yourself

# check it hasn't overrun a boundary
## if it has, reflect velocity, not wrap around the position
if b.x - b.radius < minx:
b.velocity.x *= -1
if b.x + b.radius > maxx:
b.velocity.x *= -1
if b.y - b.radius < miny:
b.velocity.y *= -1
if b.y + b.radius > maxy:
b.velocity.y *= -1

# <--END-->

The above is an approximation: when the ball has already penetrated the
wall to some depth, it only reverses the speed but leaves the ball in
the wall. We are simulating a continious process by discrete steps.
If the ball is inside the wall it means the collision occured earlier
and the ball has already moved away for that distance. (I'm ingnoring
ball and wall deformation here. This penetration depends randomly on
`dt` so it's not the proper way to simulate deformation anyway.)
So we shoud do::

if b.x - b.radius < minx:
b.velocity.x *= -1
#b.x = minx - (b.x - b.radius - minx) + b.radius
b.x = (minx + b.radius)*2 - b.x

and similarly for other cases...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,175
Latest member
Vinay Kumar_ Nevatia
Top