pygame 3d graphics rotation issue - python

this may be just as much a maths problem than a code problem. I decided to learn how 3d engines work, and i'm following http://petercollingridge.appspot.com/3D-tutorial/rotating-objects this guide, but converting the code to python. in the function for rotating on the Z-axis, my code looks like this:
def rotate_z(theta):
theta=math.radians(theta)
for i in ords:
i[0]= i[0]*math.cos(theta) - i[1]* math.sin(theta)
i[1]= i[1]*math.cos(theta) + i[0]* math.sin(theta)
which rotates the node the appropriate amount, but over maybe 5 seconds, or 150 frames, the nodes start to slowly move together, until, about 20 seconds in, they coalesce. my initial thought was that it was a round down on the last two lines, but i am stuck. any ideas anyone?

It looks like the problem is that you're changing the value of i[0] when you need the old value to set i[1]:
i[0]= i[0]*math.cos(theta) - i[1]*math.sin(theta) <-- You change the value of i[0]
i[1]= i[1]*math.cos(theta) + i[0]*math.sin(theta) <-- You use the changed value of i[0], not the original
So the value of i[0] gets replaced, when you still want to keep it.
You can solve this by using separate variables (as Peter Collingridge does):
for i in ords:
x = i[0]
y = i[1]
i[0]= x*math.cos(theta) - y*math.sin(theta)
i[1]= y*math.cos(theta) + x*math.sin(theta)
This way, you should not get the "feedback loop" which results in the points gradually floating together.

Related

Using variable names for 2d matrix elements for readability

While solving Leetcode problems I've been trying to make my answers as easily intelligible as possible, so I can quickly glance at them later and make sense of them. Toward that end I assigned variable names to indices of interest in a 2D list. When I see "matrix[i][j+1]" and variations thereof repeatedly, I sometimes lose track of what I'm dealing with.
So, for this problem: https://leetcode.com/problems/maximal-square/
I wrote this code:
class Solution:
def maximalSquare(self, matrix: List[List[str]]) -> int:
maximum = 0
for y in range(len(matrix)):
for x in range(len(matrix[0])):
#convert to integer from string
matrix[y][x] = int(matrix[y][x])
#use variable for readability
current = matrix[y][x]
#build largest square counts by checking neighbors above and to left
#so, skip anything in first row or first column
if y!=0 and x!=0 and current==1:
#assign variables for readability. We're checking adjacent squares
left = matrix[y][x-1]
up = matrix[y-1][x]
upleft = matrix[y-1][x-1]
#have to use matrix directly to set new value
matrix[y][x] = current = 1 + min(left, up, upleft)
#reevaluate maximum
if current > maximum:
maximum = current
#return maximum squared, since we're looking for largest area of square, not largest side
return maximum**2
I don't think I've seen people do this before and I'm wondering if it's a bad idea, since I'm sort of maintaining two versions of a value.
Apologies if this is a "coding style" question and therefore just a matter of opinion, but I thought there might be a clear answer that I just haven't found yet.
It is very hard to give a straightforward answer, because it might vary from person to person. Let me start from your queries:
When I see "matrix[i][j+1]" and variations thereof repeatedly, I sometimes lose track of what I'm dealing with.
It depends. People who have moderate programming knowledge should not be confused by seeing a 2-D matrix in matrix[x-pos][y-pos] shape. Again, if you don't feel comfortable, you can use the way you have shared here. But, you should try to adopt and be familiar with this type of common concepts parallelly.
I don't think I've seen people do this before and I'm wondering if it's a bad idea, since I'm sort of maintaining two versions of a value.
It is not a bad idea at all. It is "Okay" as long as you are considering to do this for your comfort. But, if you like to share your code with others, then it might not be a very good idea to use something that is too obvious. It might reduce the understandability of your code to others. But, you should not worry with the maintaining two versions of a value, as long as the extra memory is constant.
Apologies if this is a "coding style" question and therefore just a matter of opinion, but I thought there might be a clear answer that I just haven't found yet.
You are absolutely fine by asking this question. As you mentioned, it is really just a matter of opinion. You can follow some standard language guideline like Google Python Style Guide. It is always recommended to follow some standards for this type of coding style things. Always keep in mind, a piece of good code is always self-documented and putting unnecessary comments sometimes make it boring. Also,
Here I have shared my version of your code. Feel free to comment if you have any question.
# Time: O(m*n)
# Space: O(1)
class Solution:
def maximalSquare(self, matrix: List[List[str]]) -> int:
"""Given an m x n binary matrix filled with 0's and 1's,
find the largest square containing only 1's and return its area.
Args:
matrix: An (m x n) string matrix.
Returns:
Area of the largest square containing only 1's.
"""
maximum = 0
for x in range(len(matrix)):
for y in range(len(matrix[0])):
# convert current matrix cell value from string to integer
matrix[x][y] = int(matrix[x][y])
# build largest square side by checking neighbors from up-row and left-column
# so, skip the cells from the first-row and first-column
if x != 0 and y != 0 and matrix[x][y] != 0:
# update current matrix cell w.r.t. the left, up and up-left cell values respectively
matrix[x][y] = 1 + min(matrix[x][y-1], matrix[x-1][y], matrix[x-1][y-1])
# re-evaluate maximum square side
if matrix[x][y] > maximum:
maximum = matrix[x][y]
# returning the area of the largest square
return maximum**2

How to create random-dot stereogram (RDS)?

I am trying to understand and code a python script that create a random-dot stereogram (RDS) from a depthmap and a random-dot generated pattern. From what I've understood, to create the illusion of depth, pixels are shifted so when we make them merge by changing focus the difference of shifting creates the illusion.
I put this into practice with this depth map:
Here is the result:
But I don't understand why I can see on the result 2 objects, 1 star "close" to me and an other star "far" from me. And there is different possible results depending of how I focus my eyes.
I have read many things on the subject but I don't get it. Maybe the problem is my poor english or understanding of what I've read but I will appreciate some detailed explanations since there not that much technical explanations on the web about how to code this from scratch.
Note: I have tried with different size on shift and pattern and it doesn't seem to change anything
Code: (Tell me if you need other part of the code or some comment about how it work. I didn't clean it yet)
import os, sys
import pygame
def get_linked_point(depthmap, d_width, d_height, sep):
"""
In this function we link each pixel in white in the depth map with the
coordinate of the shifted pixel we will need to create the illusion
ex: [[x,y],[x_shifted,y]]
:param sep: is the shift value in pixels
"""
deptharray = pygame.PixelArray(depthmap)
list_linked_point = []
for x in range(d_width):
for y in range(d_height):
if deptharray[x][y] != 0x000000:
list_linked_point.append([[x, y], [x+sep, y]])
del deptharray
return list_linked_point
def display_stereogram(screen, s_width, pattern, p_width, linked_points):
"""
Here we fill the window with the pattern. Then for each linked couple of
point we make the shifted pixel [x_shifted,y] equal to the other one
[x,y]
"""
x = 0
while x < s_width:
screen.blit(pattern, [x, 0])
x += p_width
pixAr = pygame.PixelArray(screen)
for pair in linked_points:
pixAr[pair[0][0], pair[0][1]] = pixAr[pair[1][0], pair[1][1]]
del pixAr
The problem "I can see on the result 2 objects, 1 star "close" to me and an other star "far" from me" is due to the fact that I get the wrong approach when I try to generalize my understanding of stereograms made with 2 images to stereograms using repeated pattern.
To create 2 images stereograms you need to shift pixels of one image to make the depth illusion.
What was wrong in my approch is that I only shift pixels that should create the star. What I didn't get is that because RDS are made by repeated patterns, shifting these pixels also create an opposite shifting with next patterns creating an other star of the opposite depth.
To correct this I paired every point of the depth map (not only the white one) in order to come back to the base shifting amount after the end of the star.
Here is the result:
Code: (This code is the previous one quickly modified after the help of Neil Slater so it's not clean yet. I will try to improve this)
def get_linked_point(depthmap, d_width, d_height, p_width, sep):
"""
In this function we link each pixel in white in the depth map with the
coordinate of the shifted pixel we will need to create the illusion
ex: [[x,y],[x_shifted,y]]
:param sep: is the shift value in pixels
"""
deptharray = pygame.PixelArray(depthmap)
list_linked_point = []
for x in range(d_width):
for y in range(d_height):
if deptharray[x][y] == 0x000000:
list_linked_point.append([[x, y], [x+p_width, y]])
else:
list_linked_point.append([[x, y], [x-sep+p_width, y]])
del deptharray
return list_linked_point
def display_stereogram(screen, s_width, pattern, p_width, linked_points):
"""
Here we fill the window with the pattern. Then for each linked couple of
point we make the shifted pixel [x_shifted,y] equal to the other one
[x,y]
"""
x = 0
while x < s_width:
screen.blit(pattern, [x, 0])
x += p_width
pixAr = pygame.PixelArray(screen)
for pair in linked_points:
pixAr[pair[1][0], pair[1][1]] = pixAr[pair[0][0], pair[0][1]]
del pixAr

Referencing the last vector in a matrix in python cycle

I'm trying to run a cycle that takes a matrix, vector-by-vector, then multiplies these vectors into a new matrix with some variation in each.
My code, however does not only alter the last-added vector, but all of them.
Here's a simplified sample:
origVector = [0,0,0,0]
nVectors=[]
for j in range(3):
print ("iteration: " + str(j))
nVectors.append(origVector)
nVectors[-1][0] += j+1
#at this point it should only change the last-added vector,
#but it changes them all
print(nVectors)
I've spent the last half day trying to figure out what's wrong with my referencing (for e.g. the matrixname[-1][0] reference is working fine if not inside the cycle)
Can someone please point me to the direction..? Thanx in advance!

fill missing values in python array

Using: Python 2.7.1 on Windows
Hello, I fear this question has a very simple answer, but I just can't seem to find an appropriate and efficient solution (I have limited python experience). I am writing an application that just downloads historic weather data from a third party API (wundergorund). The thing is, sometimes there's no value for a given hour (eg, we have 20 degrees at 5 AM, no value for 6 AM, and 21 degrees at 7 AM). I need to have exactly one temperature value in any given hour, so I figured I could just fit the data I do have and evaluate the points I'm missing (using SciPy's polyfit). That's all cool, however, I am having problems handling my program to detect if the list has missing hours, and if so, insert the missing hour and calculate a temperature value. I hope that makes sense..
My attempt at handling the hours and temperatures list is the following:
from scipy import polyfit
# Evaluate simple cuadratic function
def tempcal (array,x):
return array[0]*x**2 + array[1]*x + array[2]
# Sample data, note it has missing hours.
# My final hrs list should look like range(25), with matching temperatures at every point
hrs = [1,2,3,6,9,11,13,14,15,18,19,20]
temps = [14.0,14.5,14.5,15.4,17.8,21.3,23.5,24.5,25.5,23.4,21.3,19.8]
# Fit coefficients
coefs = polyfit(hrs,temps,2)
# Cycle control
i = 0
done = False
while not done:
# It has missing hour, insert it and calculate a temperature
if hrs[i] != i:
hrs.insert(i,i)
temps.insert(i,tempcal(coefs,i))
# We are done, leave now
if i == 24:
done = True
i += 1
I can see why this isn't working, the program will eventually try to access indexes out of range for the hrs list. I am also aware that modifying list's length inside a loop has to be done carefully. Surely enough I am either not being careful enough or just overlooking a simpler solution altogether.
In my googling attempts to help myself I came across pandas (the library) but I feel like I can solve this problem without it, (and I would rather do so).
Any input is greatly appreciated. Thanks a lot.
When I is equal 21. It means twenty second value in list. But there is only 21 values.
In future I recommend you to use PyCharm with breakpoints for debug. Or try-except construction.
Not sure i would recommend this way of interpolating values. I would have used the closest points surrounding the missing values instead of the whole dataset. But using numpy your proposed way is fairly straight forward.
hrs = np.array(hrs)
temps = np.array(temps)
newTemps = np.empty((25))
newTemps.fill(-300) #just fill it with some invalid data, temperatures don't go this low so it should be safe.
#fill in original values
newTemps[hrs - 1] = temps
#Get indicies of missing values
missing = np.nonzero(newTemps == -300)[0]
#Calculate and insert missing values.
newTemps[missing] = tempcal(coefs, missing + 1)

What is wrong with my snap to grid code?

First of all, I'm fairly sure snapping to grid is fairly easy, however I've run into some odd trouble in this situation and my maths are too weak to work out specifically what is wrong.
Here's the situation
I have an abstract concept of a grid, with Y steps exactly Y_STEP apart (the x steps are working fine so ignore them for now)
The grid is in an abstract coordinate space, and to get things to line up I've got a magic offset in there, let's call it Y_OFFSET
to snap to the grid I've got the following code (python)
def snapToGrid(originalPos, offset, step):
index = int((originalPos - offset) / step) #truncates the remainder away
return index * gap + offset
so I pass the cursor position, Y_OFFSET and Y_STEP into that function and it returns me the nearest floored y position on the grid
That appears to work fine in the original scenario, however when I take into account the fact that the view is scrollable things get a little weird.
Scrolling is made as basic as I can get it, I've got a viewPort that keeps count of the distance scrolled along the Y Axis and just offsets everything that goes through it.
Here's a snippet of the cursor's mouseMotion code:
def mouseMotion(self, event):
pixelPos = event.pos[Y]
odePos = Scroll.pixelPosToOdePos(pixelPos)
self.tool.positionChanged(odePos)
So there's two things to look at there, first the Scroll module's translation from pixel position to the abstract coordinate space, then the tool's positionChanged function which takes the abstract coordinate space value and snaps to the nearest Y step.
Here's the relevant Scroll code
def pixelPosToOdePos(pixelPos):
offsetPixelPos = pixelPos - self.viewPortOffset
return pixelsToOde(offsetPixelPos)
def pixelsToOde(pixels):
return float(pixels) / float(pixels_in_an_ode_unit)
And the tools update code
def positionChanged(self, newPos):
self.snappedPos = snapToGrid(originalPos, Y_OFFSET, Y_STEP)
The last relevant chunk is when the tool goes to render itself. It goes through the Scroll object, which transforms the tool's snapped coordinate space position into an onscreen pixel position, here's the code:
#in Tool
def render(self, screen):
Scroll.render(screen, self.image, self.snappedPos)
#in Scroll
def render(self, screen, image, odePos):
pixelPos = self.odePosToPixelPos(odePos)
screen.blit(image, pixelPos) # screen is a surface from pygame for the curious
def odePosToPixelPos(self.odePos):
offsetPos = odePos + self.viewPortOffset
return odeToPixels(offsetPos)
def odeToPixels(odeUnits):
return int(odeUnits * pixels_in_an_ode_unit)
Whew, that was a long explanation. Hope you're still with me...
The problem I'm now getting is that when I scroll up the drawn image loses alignment with the cursor.
It starts snapping to the Y step exactly 1 step below the cursor.
Additionally it appears to phase in and out of allignment.
At some scrolls it is out by 1 and other scrolls it is spot on.
It's never out by more than 1 and it's always snapping to a valid grid location.
Best guess I can come up with is that somewhere I'm truncating some data in the wrong spot, but no idea where or how it ends up with this behavior.
Anyone familiar with coordinate spaces, scrolling and snapping?
Ok, I'm answering my own question here, as alexk mentioned, using int to truncate was my mistake.
The behaviour I'm after is best modeled by math.floor().
Apologies, the original question does not contain enough information to really work out what the problem is. I didn't have the extra bit of information at that point.
With regards to the typo note, I think I may be using the context in a confusing manner... From the perspective of the positionChanged() function, the parameter is a new position coming in.
From the perspective of the snapToGrid() function the parameter is an original position which is being changed to a snapped position.
The language is like that because part of it is in my event handling code and the other part is in my general services code. I should have changed it for the example
Do you have a typo in positionChanged() ?
def positionChanged(self, newPos):
self.snappedPos = snapToGrid(newPos, Y_OFFSET, Y_STEP)
I guess you are off by one pixel because of the accuracy problems during float division. Try changing your snapToGrid() to this:
def snapToGrid(originalPos, offset, step):
EPS = 1e-6
index = int((originalPos - offset) / step + EPS) #truncates the remainder away
return index * gap + offset
Thanks for the answer, there may be a typo, but I can't see it...
Unfortunately the change to snapToGrid didn't make a difference, so I don't think that's the issue.
It's not off by one pixel, but rather it's off by Y_STEP. Playing around with it some more I've found that I can't get it to be exact at any point that the screen is scrolled up and also that it happens towards the top of the screen, which I suspect is ODE position zero, so I'm guessing my problem is around small or negative values.

Categories