Orbiting four objects around centroid in an elliptic shape, Python

692 Views Asked by At

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.

1

There are 1 best solutions below

0
On BEST ANSWER

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()