This is surely a beginner's question, I'm just having trouble searching for it.
What I want to do is calculate points on a circle for thousands of circles which I will later render. Because I will be rendering many thousands of circles a second, I thought I'd try to avoid unncessary overhead. To do this, I've created a function that calculates a number of points (which I want to be variable, a larger circle will need more points calculated) on a unit circle, and another function which can take these points, translate them and then scale by the radius.
My original code ended up something like this:
class Circle():
...
def CalcCircle(segments):
does some stuff to calculate generic coordinates
def CreateCircle(x, y, r, segments):
does some stuff to create a circle using CalcCircle(segments)
Obviously the problem was that even though I might only want to create circles with 20 segments, I was calling the CalcCircle function (and repeating the same calculations) every time I called CreateCircle.
The only way I could figure out how to fix this was:
class Circle():
...
def CalcCircle(segments):
does some stuff to calculate generic coordinates
CreateCircle_has_not_been_run = True
def CreateCircle(x, y, r, segments):
if Circle.TransCircle_has_not_been_run:
generic_circle = Circle.CalcCircle(segments)
Circle.CreateCircle_has_not_been_run = False
does some stuff to create a circle using generic_circle
I've never formally learnt programming so I'm not sure if this is considered good design. Surely it would become messy if every time I wanted to "initialize" data or call a function only on the first run through I had to make a random class variable. The reason I ask is I'm constantly running into this problem, so I assume there must be a standard way of doing it.
Edit: An example of how the call will be made.
#window.event
def on_draw():
window.clear()
width = window.get_size()[0]
height = window.get_size()[1]
radius = int(width/50)
segments = int(radius*1.5)
for i in range(N):
pyglet.gl.glColor3f(0.05,0.2,0.9)
DrawCircle(positions[i][0],positions[i][1],width,segments)
DrawCage(width,height)
DrawLabel(width,height)
etc.
I'm aware that there's problems here but I'm just trying to illustrate the example (positions comes from the update function if anyone is wondering). As I've said earlier, this is a problem I run into all the time.
I could call Circle.CalcCircle() from the on_resize() function as per Achim's suggestion. I have a hard time believing however that standard practice is to stick two random functions into a class (as it stands neither of them necessarily need to even be in the Circle class), one of which is implicitly dependent on the other and both of which are called in different parts of the code.
I would do something like this:
class Circle:
def __init__(self):
self.unit_circle_points = None
def CalcCircle(self, segments):
# Do some stuff to calculate segments,
# assign calculated values to class attribute
self.unit_circle_points = calculated_points
def CreateCircle(self, X, y, r, segments):
# If circle points have not yet been calculated then calculate
# and store, else just load stored points
if self.unit_circle_points is None:
self.CalcCircle(segments)
unit_circle_points = self.unit_circle_points
# Now use unit_circle_points to do some calculations
Every time you instantiate a circle object it will come with an attribute named unit_circle_points that is initialized to None. When you call the CreateCircle method on that object for the first time it will see that the unit_circle_points attribute is None and perform the necessary computations by calling CalcCircle, storing the results. On subsequent calls to the CreateCircle method of this Circle object the unit_circle_points attribute will no longer be None, and the method will simply use the values stored in the attribute.
Edit:
If this requires to much "implicit" behavior for your taste, you can shift things around so that CalcCircle must be called explicitly by the user to generate the pre-calculated data.
class Circle:
def __init__(self):
self.unit_circle_points = None
def CalcCircle(self, segments):
# Do some stuff to calculate segments,
# assign calculated values to class attribute
self.unit_circle_points = calculated_points
return self
def CreateCircle(self, X, y, r):
# If circle points have not yet been calculated then raise an error,
# else load previously calculated points
if self.unit_circle_points is None:
raise Exception("You'd better explicitly call CalcCircle first.")
unit_circle_points = self.unit_circle_points
# Now use unit_circle_points to do some calculations
Related
I am trying to use Shady to present a sequence of image frames. I'm controlling the flow from another machine, so that I first instruct the machine running Shady to present the first frame, and later on to run the rest of the frames.
I create a World instance, and attach to it an animation callback function. Within this callback I listen for communications from the other machine (using UDP).
First I receive a command to load a given sequence (stored as a numpy array), and I do
def loadSequence(self, fname):
yy = np.load(fname)
pages = []
sz = yy.shape[0]
for j in range(yy.shape[1]/yy.shape[0]):
pages.append(yy[:, j*sz:(j+1)*sz])
deltax, deltay = (self.screen_px[0] - sz) / 2, (self.screen_px[1] - sz) / 2
if (self.sequence is None):
self.sequence = self.wind.Stimulus(pages, 'sequence', multipage=True, anchor=Shady.LOCATION.UPPER_LEFT, position=[deltax, deltay], visible=False)
else:
self.sequence.LoadPages(pages, visible=False)
When I receive the command to show the first frame, I then do:
def showFirstFrame(self, pars):
self.sequence.page = 0 if (pars[0] == 0) else (len(self.sequence.pages) - 1)
self.sequence.visible = True
But what do I do now to get the other frames to be be displayed? In the examples I see, s.page is set as a function of time, but I need to show all frames, regardless of time. So I was thinking of doing something along these lines:
def showOtherFrames(self, pars, ackClient):
direction, ack = pars[0], pars[2]
self.sequence.page = range(1, len(self.sequence.pages)) if (direction == 0) else range(len(self.sequence.pages)-2, -1, -1)
But this won't work. Alternatively I thought of defining a function that takes t as argument, but ignores it and uses instead a counter kept in a global variable, but I'd like to understand what is the proper way of doing this.
When you make s.page a dynamic property, the function assigned to it must take one argument (t), but you can still just use any variables in the space when defining that function, and not even use the time argument at all.
So, for example, you could do something as simple as:
w = Shady.World(...)
s = w.Stimulus(...)
s.page = lambda t: w.framesCompleted
which will set the page property to the current frame count. That sounds like it could be useful for your problem.
Your global-variable idea is one perfectly valid way to do this. Or, since it looks like you're defining things as methods of an instance of your own custom class, you could use instance methods as your animation callbacks and/or dynamic property values—then, instead of truly global variables, it makes sense to use attributes of self:
import Shady
class Foo(object):
def __init__(self, stimSources):
self.wind = Shady.World()
self.stim = self.wind.Stimulus(stimSources, multipage=True)
self.stim.page = self.determinePage # dynamic property assignment
def determinePage(self, t):
# Your logic here.
# Ignore `t` if you think that's appropriate.
# Use `self.wind.framesCompleted` if it's helpful.
# And/or use custom attributes of `self` if that's
# helpful (or, similarly, global variables if you must).
# But since this is called once per frame (whenever the
# frame happens to be) it could be as simple as:
return self.stim.page + 1
# ...which is indefinitely sustainable since page lookup
# will wrap around to the number of available pages.
# Let's demo this idea:
foo = Foo(Shady.PackagePath('examples/media/alien1/*.png'))
Shady.AutoFinish(foo.wind)
Equivalent to that simple example, you could have the statement self.stim.page += 1 (and whatever other logic) inside a more-general animation callback.
Another useful tool for frame-by-frame animation is support for python's generator functions, i.e. functions that include a yield statement. Worked examples are included in python -m Shady demo precision and python -m Shady demo dithering.
It can also be done in a StateMachine which is always my preferred answer to such things:
import Shady
class Foo(object):
def __init__(self, stimSources):
self.wind = Shady.World()
self.stim = self.wind.Stimulus(stimSources, multipage=True)
foo = Foo(Shady.PackagePath('examples/media/alien1/*.png'))
sm = Shady.StateMachine()
#sm.AddState
class PresentTenFrames(sm.State):
def ongoing(self): # called on every frame while the state is active
foo.stim.page += 1
if foo.stim.page > 9:
self.ChangeState()
#sm.AddState
class SelfDestruct(sm.State):
onset = foo.wind.Close
foo.wind.SetAnimationCallback(sm)
Shady.AutoFinish(foo.wind)
I am using the Turtles Module where I am creating a U.S. Flag. The user decides the size of the flag and with that size I will be using it to create the width and length.
I am trying to group/compress all of my subfunctions into one huge function so the user can simply type Draw_USAFlag(t, w) ## T = turtle W = Size and it will carry out the task of the 5 functions that I already have.
For example I have 2 subfunctions: draw_rectangle(t, w) and draw_stripes (t, w) ; I want to group these two subfunctions into one function that will be called Draw_USAFlag(t, w) where it will use the user inputted size (w) throughout all of the functions. Any help is greatly appreciated! Thanks!
Quite simply, make a function that calls the others:
def draw_rectangle(t, w):
# however you do this
...
def draw_rectangle(t, w):
# however you do this
...
def Draw_USAFlag(t, w):
draw_rectangle(t, w)
draw_stripes(t, w)
This assumes they don't return anything, so whatever they do works by side-effects on some other object. If e.g. they return an image, this will need changing depending on the exact structure of your system.
I'm fairly new to classes in Python. While coding a battleship game I ran into a problem with choosing random x,y coordinates for the locations of computer's ships and the computer's attack coordinates. I am confused about whether to generate random numbers as a local variable in one of the functions or as Class attribute or instance attribute.
Initially I thought to create an instance attribute (below), but i got rand_x is not defined. I tried creating a Battleship function that generated random numbers, but it returned the same pair of coordinates every time it was called. Is the only way to do this to create a local variable for random #s? Because I will be using the random generator more than once it would be nice not to have to repeat that code.
Thanks for your patience.
EDIT: I've included more code with the randomness function, and replaced size in randomness function with self.size.
For example, Battleship(4,2,0,0) might give me a hitlist of [[2,1],[2,1]] I would like random #s inside hitlist.
import random
hitlist=[]; #a global variable
class Battleship(object):
""" Ship object container. A game where the user tries to destroy the enemy's ships User tries to guess computer's position x and y """
def __init__(self, size, numberships,position_x,position_y):
self.position_x=position_x
self.position_y=position_y
self.numberships=numberships
self.size = size
def plotships(self,r):
"""input is integer coordinates for ships and output is an array of arrays with battleship locations CREATES THE HITLIST DONT REPEAT"""
print('plotships function running')
for i in range(self.numberships):
hitlist.append(r) #random number from function randomness
print(hitlist)
return hitlist
def randomness(self):
rand_x=random.choice(range(self.size))
rand_y=random.choice(range(self.size))
randcoord=[rand_x,rand_y]
return randcoord
#Game Interface
size=int(input('Gameboard size'))
numberships=int(input('Waiting for Number of enemy ships'))
b=Battleship(size,numberships,0,0)
random=b.randomness() #create a random x y coordinate
b.plotships(random) #create a hitlist
I think it's because you're calling random.choice with size and not self.size.
i.e.
rand_x = random.choice(range(self.size))
Also, where are you defining self.rand? Surely you're getting problems in the constructor trying to print it?
EDIT - IN RESPONSE TO COMMENT BELOW:
If you want hitlist to be populated with self.numberships pairs of independent random number pairs, the write the plotships method as:
def plotships(self):
"""input is integer coordinates for ships and output is an
array of arrays with battleship locations CREATES THE
HITLIST DONT REPEAT"""
print('plotships function running')
for i in range(self.numberships):
hitlist.append(randomness()) #random number from function randomness
print(hitlist)
return hitlist
To get a random number maybe you could import the random library.
You could use it to initialize your (X, Y) coordenates on an instance of your class.
import random
Battleship(size, numberships, random.randint(0, WIDTH), random.randint(0, HEIGHT))
I'm assuming you have the screen's widht and Height available. Hope it helps.
So, what I am trying to do in detail:
I have a device that acts as a display, although is technically not one (that's not really important, it can be handled like a normal display) and I want to display a series of images on it. When each image is displayed i call a function that takes readings from another device. So far I have used pyglet for that and I can get it to display the images in the correct order and where I want them, but the readings taken seem not to correspond to the right image.
The simplest way to explain what I want to do is:
for a in range(10):
display('image%d'%a)
values(a) = measure()
Imagine values being initiated in the correct form.
What I tried so far was:
import pyglet
import numpy as np
size = 64
no = 512/size
a,b,c = (0,0,0)
values = np.zeros((no,no,3))
display = pyglet.window.get_platform().get_default_display()
screens = []
for screen in display.get_screens():
screens.append(screen)
window = pyglet.window.Window(screen = screens[1], fullscreen=1)
#here i choose the mentioned device, since it is connected to my computer via display port
#window.event
def update(dt):
global a,b,c
if a == no/2. and b == no/2.:
values[a,b,c] = 0
window.clear()
else:
image = pyglet.image.load('folder_%d/grating_%d_%d_%d.bmp' % (size,a,b,c))
window.clear()
print (a,b,c)
image.blit(0,0)
values[a,b,c] = measure()
c+=1
if c == 3:
b += 1
c = 0
if b == no:
a += 1
b = 0
if a == no:
pyglet.app.exit()
pyglet.clock.schedule_interval(update, .2)
pyglet.app.run()
where measure() is my own function. "no" is an index for my images, they range from (0,0,0),(0,0,1),(0,0,2),(0,1,0)... to (no,no,2) and are called to be called one by one. Also the case of a = b = no/2 is a special case that is of no special importance to my problem, I think.
first of all: I am pretty sure this is neither elegant nor efficient but I am not creating software that anyone else is ever likely to use. I am also aware that using global variables is bad practice but their use is due to me not knowing how to use eventloops correctly.
I am not happy with this solution, because the readings i take always seem to correspond to the previous image.
I guess I misuse the eventloop badly, but the pyglet documentation does not really help me here.
Also I feel like I am building a whole truck just to carry a bag across the street...
I have already looked into pyplot as an alternative, since the imshow() function works in the way I want, but the plotting libraries seem to display images in random sizes, which I cannot figure out how to control properly.
I appreciate any help on using pyglet correctly as well as alternative libraries that can help.
Thank you already,
Mopsi
An extra-long comment, that requires formatted code
From your example, you don't need a,b,c outside of the update function and all the global stuff is about having values that stay alive across invocations. If I'm correct this is better suited by a closure, like
...
def make_update(no, window):
from itertools import product
abcs = product(range(no),range(no),range(3))
#window.event
def _update(...):
try:
a, b, c = next(abcs)
except StopIteration:
... wind up ...
...
return _update
update = make_update(no, window)
...
Alright, I did not actually solve the problem but found a workaround:
I just flatten my image nomenclature, e.g
0_0_0 -> 000
0_0_1 -> 001
0_0_2 -> 002
0_1_0 -> 003
etc.
With values now being an array with dimensions [no*no*3,1]
And since for the n-th iteration and therefore n-th measurement I see the (n-1)th image, I simply add a dummy image and assign the n-th measured value to values[n-1].
The first measurement being useless is no problem here, since values[-1] is the last element, which gets overwritten with a meaningful measurement in the end.
Since the number of images is known and predetermined I can reshape my array of values afterwards.
If anyone cares to explain why no the displayed image is shifted by one iteration (with no image at all being displayed at the first iteration) please go ahead.
I have a code which creates a class called 'frame'. When you call it, you define a set of arbitrary coordinates and it will create the necessary amount of self.x'n' and self.y'n' variables where 'n' is the number of coordinates you have input. Here is the code so far:
class NoCoords(Exception): "raised if no coordinates are input"
class frame:
def __init__(self,*coords):
try:
for count,pos in enumerate(coords,1):
exec('self.x%s,self.y%s=%s'%(count,count,pos))
except(IndexError): raise NoCoords()
The issue I'm having is that if I define frame1 as a frame class with 2 coordinates and then frame2 as a frame class with 3 coordinates, it won't create a separate amount of x and y coordinates for frame2. It will simply create the x1,y1 and x2,y2 variables required for frame1, and when frame2 is created it will only create the x1,y1 and x2,y2 variables, it won't create a third set of variables (x3,y3) to contain the 3rd coordinate designated in frame2.
So this brings about a few questions:
1. What could be done (if anything) to fix this?
2. Is this a stupid way of going about doing this?
3. If the answer to 2 is 'yes' then what is a better way of doing what I want to accomplish?
def __init__(self,*coords):
self.coords = {}
for count,pos in enumerate(coords,1):
self.coords["x%s" % count] = pos
self.coords["y%s" % count] = pos
Dynamically creating variables in that manor is frowned upon. Its far nicer to just store these "variables" in a dict.
Rather than doing
something.x1
You can do
something.coords["x1"]
An alternative as pointed out would be to use
for count,pos in enumerate(coords,1):
setattr(self, "x%s" % count, pos)
But to me, that just feels a touch dirty, it makes it harder to get the total number of "x" variables for a start.