Orbiting four objects around centroid in an elliptic shape, Python
質問
Okay... I'm not so good at maths (didn't even finish our version of high school) and is really tired right now, but I need to orbit four objects elliptically around the centroid and have been stuck for a while, but it's not like I haven't tried or come anywhere by myself. This is what I've come up with so far:
from math import cos,sin,pi,sqrt,atan2
def orbit(p, deg, pivot=(0.32563325, 0.123498),ellipse=(0.5, 0.743992)):
# p = current (x,y)-position of current object,
# Pivot = (x,y)-position of point to orbit around,
# retrieved by centroid((x1,y1),(x2,y2),(x3,y3),(x4,y4))
# Ellipse = (width,height) of ellipse to rotate around
# retrieved by ellipse((x1,y1),(x2,y2),(x3,y3),(x4,y4))
px,py = pivot
if ellipse == (0.0,0.0):
o = polar(p[0]-px,p[1]-py)[0]
xr,yr = 1.0,1.0
else:
ew,eh = ellipse
if ew < eh:
o = eh/2 # Distance to the point most far away from the middle of the ellipse (Outer radius)
xr = ew/eh # Horizontal ratio of ellipse so we can move it back properly into ellipse after performing circular orbit
yr = 1.0 # Verical movement will not be affected because it's on the large axis
else:
o = ew/2
xr = 1.0
yr = eh/ew
x,y = p[0]-px,p[1]-py # Subtract pivot's position (that's the the point I want to orbit around)
d,a = polar(x,y) # Get angle
x,y = rect(o,a+deg) # Move point as far away from middle as it will be as most and make circular orbit around pivot by deg degrees
x,y = x*xr,y*yr # Move points back to elliptic shape by multiplying positions with according ratio <--- I guess it's here something goes wrong
x,y = x+px,y+py # Move point back to original position by adding pivot's positions
return x,y
# Other functions
def centroid(*points):
x,y = izip(*points)
return (sum(x) / len(points), sum(y) / len(points))
def ellipse(*points):
x,y = izip(*points)
xd,yd = max(x)-min(x),max(y)-min(y)
return (xd,yd)
def polar(x,y):
d = sqrt(x**2 + y**2)
a = atan2(y,x) * (180.0/pi)
return d, a
def rect(d,a):
x = d * cos(a*pi/180.0)
y = d * sin(a*pi/180.0)
return x, y
If i use ellipse=(0.0,0.0) and orbit everything in an "ordinary" circle instead of an elliptic shape it works just fine, so I guess it's when I try to multiply the x/y-position with the ratio ew/eh or eh/ew I do something wrong but I can't figure out what at the moment.
I am a bit tired and will try and get some sleep now and see if I can solve it tomorrow but some helpt would really be appriciated here.
解決
This encapsulates the math for each Ellipse, and shows sample usage; you will have to add code for whatever output you want.
from math import sin, cos, pi
class Ellipse(object):
@classmethod
def fromBox(cls, p1=(-1.,-1.), p2=(1.,1.), period=1., offs=0., clockwise=False):
"""
Construct an Ellipse from an axis-aligned bounding box
p1, p2 diagonally-opposed corners of the bounding box
period time for a complete orbit
offs offset into initial rotation
clockwise direction of rotation
"""
x1,y1 = p1
x2,y2 = p2
# find center point
cx, cy = (x1 + x2)*0.5, (y1 + y2)*0.5
# find major and minor semi-axes and corresponding theta
a,b = abs(x2 - x1)*0.5, abs(y2 - y1)*0.5
if a >= b:
theta = 0.0
else:
a,b = b,a
theta = pi/2
# return an Ellipse object
return cls(cx, cy, a, b, theta, period, offs, clockwise)
def __init__(self, x=0., y=0., a=1., b=1., theta=0., period=1., offs=0., clockwise=False):
"""
Create an ellipse
x,y center point
a semi-major axis
b semi-minor axis
theta major axis inclination (in radians)
period time for a complete orbit
offs offset into initial rotation
clockwise direction of rotation
"""
self.x = x
self.y = y
self.period = -period if clockwise else period
self._freq = self.period / (2. * pi)
self.offs = offs
s_th = sin(theta)
c_th = cos(theta)
self._ast = a * s_th
self._act = a * c_th
self._bst = b * s_th
self._bct = b * c_th
def at(self, t):
"""
Evaluate the ellipse at time t
"""
_t = (t - self.offs) * self._freq
st = sin(_t)
ct = cos(_t)
return self.x + self._act*ct - self._bst*st, self.y + self._act*st + self._bst*ct
def main():
a = Ellipse.fromBox((-0.67436675, -0.376502), (1.32563325, 0.623498))
b = Ellipse(0.32563325, 0.123498, 0.9, 0.6, pi/6)
c = Ellipse(0.32563325, 0.123498, 1.1, 0.5, pi/4)
d = Ellipse(0.32563325, 0.123498, 1.0, 0.5, pi/2)
t_step = 0.03
for t in xrange(200):
drawCircle(a.at(t*t_step), "red")
drawCircle(b.at(t*t_step), "blue")
drawCircle(c.at(t*t_step), "green")
drawCircle(d.at(t*t_step), "yellow")
if __name__=="__main__":
main()