I'm trying to build some micro "3d engine" as to understand the basics of this field.
Everything works quite fine except for rotations. I use the standard rotation matrices to do this, yet all 3 of the possible rotations not only rotate the points, they also scale them down. This behaviour is not intended or wished for.
Following is the relevant part of the code (I think):
class Point:
def __init__(self, x, y, z, ui):
self.x = mpf(x)
self.y = mpf(y)
self.z = mpf(z)
self.ui = ui
def subtractVectorFromPoint(self, vector):
self.x -= vector.x
self.y -= vector.y
self.z -= vector.z
return self
def subtractPointFromPoint(self, point):
TempVector = Vector(0, 0, 0)
TempVector.x = self.x - point.x
TempVector.y = self.y - point.y
TempVector.z = self.z - point.z
return TempVector
def setPointToPoint(self, point):
self.x = point.x
self.y = point.y
self.z = point.z
return self
class Vector:
def __init__(self, x, y ,z):
self.x = mpf(x)
self.y = mpf(y)
self.z = mpf(z)
def rotateXY(self, degrees):
radians = mpf(math.radians(degrees))
self.x = (math.cos(radians) * self.x) + (- math.sin(radians) * self.y)
self.y = (math.sin(radians) * self.x) + (math.cos(radians) * self.y)
return self
#rotate xy plane by 15 degrees on pressing r
if event.key == pygame.K_r:
origin = Vector(0, 0, 0)
UI.draw_background()
for point in pointList:
tempVector = point.subtractPointFromPoint(origin)
point.setPointToPoint(origin)
point.addVectorToPoint(tempVector.rotateXY(15))
point.drawPoint()
That should be all you need I think. Any pointers to where I went wrong are welcome.
I know the classes miss an indent in the example here, I suck at the layout on this website :)
p.s. I tried using the "mpf()" function to increase the precision but this had 0 result.
The problem is in this part:
def rotateXY(self, degrees):
radians = mpf(math.radians(degrees))
self.x = (math.cos(radians) * self.x) + (- math.sin(radians) * self.y)
self.y = (math.sin(radians) * self.x) + (math.cos(radians) * self.y)
return self
self.y is being calculated using a self.x that is already adjusted, resulting in given problem.
A fix for this is using a temp variable like this:
def rotateXY(self, degrees):
radians = mpf(math.radians(degrees))
tempX = (math.cos(radians) * self.x) + (- math.sin(radians) * self.y)
self.y = (math.sin(radians) * self.x) + (math.cos(radians) * self.y)
self.x = tempX
return self
Related
I have the following class which I made:
import math
class Point:
"""Two-Dimensional Point(x, y)"""
def __init__(self, x=0, y=0):
# Initialize the Point instance
self.x = x
self.y = y
def __iter__(self):
yield self.x
yield self.y
def __add__(self, other):
addedx = self.x + other.x
addedy = self.y + other.y
return Point(addedx, addedy)
def __mul__(self, other):
mulx = self.x * other
muly = self.y * other
return Point(mulx, muly)
def __rmul__(self, other):
mulx = self.x * other
muly = self.y * other
return Point(mulx, muly)
#classmethod
def from_tuple(cls, tup):
x, y = tup
return cls(x, y)
def loc_from_tuple(self, tup):
self.x, self.y = tup
#property
def magnitude(self):
# """Return the magnitude of vector from (0,0) to self."""
return math.sqrt(self.x ** 2 + self.y ** 2)
def distance(self, self2):
return math.sqrt((self2.x - self.x) ** 2 + (self2.y - self.y) ** 2)
def __str__(self):
return 'Point at ({}, {})'.format(self.x, self.y)
def __repr__(self):
return "Point(x={},y={})".format(self.x, self.y)
I don't exactly know how to explain it but I basically want to be able to maintain a points id despite mathematical operations. For example:
point1 = Point(2, 3)
point2 = Point(4, 5)
id1 = id(point1)
point1 += point2
print(point1)
Point(x=6, y=8)
print(id1 == id(point1))
True
print(point2)
Point(x=4, y=5)
Is there a reason this doesn't happen in my code. It says False during the id part in mine.
The id is basically the memory address. If you make a new object, it will probably have a different id. If you want a mutable Point object for some reason consider the __iadd__ (and friends) methods instead, which can do the update in-place.
I've got this code:
class Vector2D(object):
def __init__(self, x=0.0, y=0.0):
self.x, self.y = x, y
def rotate(self, angle):
angle = math.radians(angle)
sin = math.sin(angle)
cos = math.cos(angle)
x = self.x
y = self.y
self.x = x * cos - y * sin
self.y = x * sin + y * cos
def __repr__(self):
return '<Vector2D x={0}, y={1}>'.format(self.x, self.y)
class Polygon(object):
def __init__(self, points):
self.points = [Vector2D(*point) for point in points]
def rotate(self, angle):
for point in self.points:
point.rotate(angle)
def center(self):
totalX = totalY = 0.0
for i in self.points:
totalX += i.x
totalY += i.y
len_points = len(self.points)
return Vector2D(totalX / len_points, totalY / len_points)
The problem is that when I rotate the polygon it also moves, not only rotates.
So how to rotate polygon around its center, without changing its position?
You're rotating around 0/0, not around its center. Try moving the polygon before rotating, such that its center is 0/0. Then rotate it, and finally move it back.
For instance, if you only need movement of vertices/polygons for this particular case, you could probably simply adjust rotate to be:
def rotate(self, angle):
center = self.center()
for point in self.points:
point.x -= center.x
point.y -= center.y
point.rotate(angle)
point.x += center.x
point.y += center.y
class Ball:
def __init__(self,pos,vel):
self.pos = Vector(pos.x,pos.y)
self.vel = Vector(vel.x,vel.y)
def curx(self):
return (self.pos.x + self.vel.x)
def cury(self):
return (self.pos.y + self.vel.y)
def forcex(velx):
self.vel.deltax(velx)
def forcey(vely):
self.vel.deltay(vely)
class Vector:
def __init__(self,x,y):
self.x = x
self.y = y
def x(self):
return self.x
def y(self):
return self.y
def delx(self,deltax):
self.x = self.x + deltax
def dely(self,deltay):
self.y = self.y + deltay
Here are my two classes, but when I initialize and try to get curx or cury back from ball:
ball = Ball(Vector(0,0),Vector(0,0))
print ball.curx
I get: <bound method Ball.curx of <__main__.Ball instance at 0x1142fd0>>
I feel like there should be a fairly simple answer to this and I'm just not getting it.
curx is a method of Ball. So, you have to invoke it:
print ball.curx()
Edit:
#user2357112 has noticed two more problems:
The definitions of Ball.forcex and Ball.forcey are missing their self parameters.
Vector.x and Vector.y are entirely useless methods. You already have x and y as attributes of Vector through self.x and self.y. So, you should just remove the methods altogether.
Here is how the code should be:
class Ball:
def __init__(self,pos,vel):
self.pos = Vector(pos.x,pos.y)
self.vel = Vector(vel.x,vel.y)
def curx(self):
return (self.pos.x + self.vel.x)
def cury(self):
return (self.pos.y + self.vel.y)
def forcex(self, velx):
self.vel.deltax(velx)
def forcey(self, vely):
self.vel.deltay(vely)
class Vector:
def __init__(self,x,y):
self.x = x
self.y = y
def delx(self,deltax):
self.x = self.x + deltax
def dely(self,deltay):
self.y = self.y + deltay
ball = Ball(Vector(0,0),Vector(0,0))
print ball.curx()
How can I see if an x and y are on the edge or inside of the rectangle? Does anyone have any ideas of how to go about that?
I have tried to create a method that will do this called contains point but I am sure I have done this incorrectly
my code:
class Rectangle: # creates class Rectangle
def __init__(self, rX, rY, rW, rH): # initialized so we can call our variables
self.x = rX # x coord
self.y = rY # y coord
self.w = rW # width
self.h = rH # heigh
def __str__(self): # will return string
return 'Rectangle(' + str(self.x) + ',' + str(self.y) + ',' + str(self.w) + ',' + str(self.h)+')'
def right(self): # will check right edge
return self.x + self.w # returns right edge
def bottom(self):
return self.y + self.h
def size(self): # Method Size will obtain size of rectangle
return self.w, self.h
def position(self): # Method will show coords
return self.x, self.y
def area(self): # method will return area of rectangle
return self.w * self.h
def expand(self, offset): # expand will take in an offset value and apply it to a new rectangle
newX = self.x - offset
newY = self.y - offset
newW = self.w + (offset * 2)
newH = self.h + (offset * 2)
newRectangle = Rectangle(newX, newY, newW, newH)
return newRectangle
def contains_point(self, x, y): # contains point will take in 2 coords
# and check to see if they are in the rectangle
if x <= self.x and y <= self.y:
return True
else:
return False
r = Rectangle(30, 40, 100, 110)
print(r.expand(-5), r)
print(r.contains_point(0, 0))
If you have x1, x2 and y1, y2 defining the outer corners of your rectangle, then you can test a point.x, point.y by checking if point.x is between x1 and x2, and point.y is between y1 and y2.
def contains_point(self, x, y): # contains point will take in 2 coords
# and check to see if they are in the rectangle
if (self.x <= x <= self.right() and
self.y <= y <= self.bottom()):
return True
else:
return False
I am having trouble with circle to circle collision resolution.
First, i detect the collision, then if the balls collide, i separate them by the sum of their radii and set the velocities. That's easy.
My problem is when gravity is acting and a ball collides with another ball from above. It's suposed to bouce off but instead it slides very slowly until it drops in the ground.
Whats hapning is that afer the colision resolution, gravity pushes the ball down and causes another colision. I've tried separating the ball by the sum of their radii + x but it just slides a little faster.
You can watch the video at http://www.youtube.com/watch?v=tk7qQ9KDFp0&feature=youtu.be.
And here's the code that hadles colision:
for p in world.particle_list:
if not p == self:
if self.pos.sub(p.pos).get_length() <= self.radius * ppm + p.radius * ppm:
p_mass_ratio = float(self.mass) / (self.mass + p.mass)
self_mass_ratio = float(p.mass) / (self.mass + p.mass)
rel_pos = p.pos.sub(self.pos)
shift = rel_pos.set_length(- rel_pos.get_length() + self.radius * ppm + p.radius * ppm)
p.pos = p.pos.add(shift.scale(0.50))
self.pos = self.pos.add(shift.scale(-0.50))
p_speed = p.speed
self_speed = self.speed
self.speed = p_speed.add(self.speed.norm_reflect(rel_pos.set_angle(rel_pos.get_angle() + 90).scale(-self.friction))).scale(0.50 * self_mass_ratio)
p.speed = self_speed.add(p.speed.norm_reflect(rel_pos.set_angle(rel_pos.get_angle() + 90).scale(self.friction))).scale(0.50 * p_mass_ratio)
I made a vector class to handle this:
def dcos(x):
return cos(radians(x))
def dsin(x):
return sin(radians(x))
def dtan(x):
return tan(radians(x))
class Vec(object):
def __init__(self, x, y):
self.x = float(x)
self.y = float(y)
self.length = self.get_length()
self.angle = self.get_angle()
def get_length(self):
return sqrt(self.x ** 2 + self.y ** 2)
def get_angle(self):
return atan2(self.y, self.x) * 180 / pi
def add(self, vec1):
new_x = self.x + vec1.x
new_y = self.y + vec1.y
return Vec(new_x, new_y)
def sub(self, vec1):
new_x = self.x - vec1.x
new_y = self.y - vec1.y
return Vec(new_x, new_y)
def scale(self, k):
return Vec(self.x * k, self.y * k)
def set_angle(self, a):
new_x = self.length * dcos(a)
new_y = self.length * dsin(a)
if a == -90 or a == 90:
new_x = 0
if a == 180 or a == 0 or a == -180:
new_y = 0
return Vec(new_x, new_y)
def set_length(self, l):
new_x = l * dcos(self.angle)
new_y = l * dsin(self.angle)
return Vec(new_x, new_y)
def inverse(self):
return Vec(- self.x, - self.y)
def norm_reflect(self, vec1):
if self.get_angle == vec1.get_angle():
return Vec(self.x, self.y)
if vec1.get_angle() >= 0:
return self.set_angle(vec1.get_angle() - self.get_angle() + 90)
else:
return self.set_angle(vec1.get_angle() - self.get_angle() - 90)
(I don't know python, but I know physics and you aren't getting other answers so I'll take a crack at it.)
Look at
if self.pos.sub(p.pos).get_length() <= self.radius * ppm + p.radius * ppm:
...
rel_pos = p.pos.sub(self.pos)
shift = rel_pos.set_length(- rel_pos.get_length() + self.radius * ppm + p.radius * ppm)
p.pos = p.pos.add(shift.scale(0.50))
self.pos = self.pos.add(shift.scale(-0.50))
You're setting the length to a negative number, so you're moving the objects toward each other.
The norm_reflect() function doesn't make much sense, and I suspect it does something other than what you intend (in which case you're not testing your code).
These two lines:
self.speed = p_speed.add(self.speed.norm_reflect(rel_pos.set_angle(rel_pos.get_angle() + 90).scale(-self.friction))).scale(0.50 * self_mass_ratio)
p.speed = self_speed.add(p.speed.norm_reflect(rel_pos.set_angle(rel_pos.get_angle() + 90).scale(self.friction))).scale(0.50 * p_mass_ratio)
seem to be invoking norm_reflect() with only one argument, and you're bringing friction and mass ratios into the picture before the simple case is working, and you're reusing rel_pos after multiple transformations, and the physics is non-Newtonian (i.e. wrong enough to require starting over from a blank slate).