Skip to content

3D line object loses its color cycle in a function animation #3362

Closed

Description

@whymranderson

Python 2.7.5, NumPy 1.7.1, SciPy 0.13.0, Matplotlib 1.3.1

This is a script to simulate the rolling of a coin( a circle) on a surface without slipping in 3 dimensions. I tried to paste on a shorter version of my code but it is not easy because of the physics part. Please bare with me. My question relates to the animation part. If one uncomments the last two lines related to function animation just before the pyplot.show(), we will get a colored circle. But if we keep the function animation, the circle in motion will only have one color. The circle is a 3D line object and, it is being updated by update_line function. It seems the color information is left behind during the update_line or animation period. I tried many methods like, passing in the color argument into the update_line function and use the set_c method, or passing in a colormap and a scale. None of them worked for me. Does anyone know how to give the rolling circle a color gradient/cycle, to make it looks more like a rolling without slipping motion? Thanks in advance! Tony :)

import numpy as np
from matplotlib import pyplot
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation

def rotation_matrix(axis,theta):
    axis = axis/np.sqrt(np.dot(axis,axis))
    a = np.cos(theta/2)
    b,c,d = axis*np.sin(theta/2)
    return np.array([[a*a+b*b-c*c-d*d, 2*(b*c-a*d), 2*(b*d+a*c)],
                     [2*(b*c+a*d), a*a+c*c-b*b-d*d, 2*(c*d-a*b)],
                     [2*(b*d-a*c), 2*(c*d+a*b), a*a+d*d-b*b-c*c]])

xx = 5
R = 5*np.sqrt(2)
ncp = 10        # number of 'new' CP, not including origin
onetheta = 30   # the angle of triangles which approximate a circle
angles = range(0,360+onetheta,onetheta)                               #[0,2,...,360]
theta = np.array(angles) * np.pi / 180                 #in radians
C = np.zeros((ncp+1,3,3))

# Set the orientation of z w.r.t lab_z
leananl=np.pi/5
C[0,:,:] = np.dot(rotation_matrix(np.array([1,0,0]),leananl),np.eye(3))
Cprime = np.zeros((ncp+1,3,3))
CM = np.zeros((ncp+1,3))                                #CM position vector array
CM[0,:]= R*C[0,:,1]    #CM(t0)
CMprime = np.zeros((ncp+1,3))
z = np.array([0,0,1])
cirCP = np.zeros((len(angles),3,ncp))                   #circle of the coin
rotheta = onetheta*np.pi/180                                   # theta
CP = np.zeros((ncp+1,3))
CPprime = np.zeros((ncp+1,3))
lowerangle = -np.radians(np.linspace(0,ncp,ncp)/ncp)
omega_z = np.zeros(ncp+1)
w = np.zeros((ncp+1,3))
w[:,2]=w[:,2]+0.0005
w[:,1]=w[:,1]+0.001
w[:,0]=w[:,0]-0.0005

for j in range(ncp):
    betaj=np.pi/2 - np.arccos(np.dot(C[j,:,1],z))
    alphaangl = np.arcsin(np.sin(rotheta/2)/np.cos(betaj))
    kappaj = 2*alphaangl
    phi = np.arccos(np.cos(alphaangl)/np.cos(np.radians(onetheta/2)))
    twodt=2*phi/w[j+1,1] #phi/wy
    Avecinspace = np.dot(rotation_matrix(z,kappaj/2),
                         C[j,:,0]*R*np.sin(rotheta/2)*2)
    Cprime[j,:,:] = np.dot(rotation_matrix(z,kappaj),C[j,:,:])
    C[j+1,:,:] = np.dot(rotation_matrix(Cprime[j,:,0],w[j+1,0]*twodt),Cprime[j,:,:])
    CPprime[j,:] = CP[j,:] +Avecinspace
    CP[j+1,:] = CPprime[j,:] + Cprime[j,:,0]*w[j+1,2]*twodt*R
    CMprime[j,:]=CM[j,:] + Cprime[j,:,0]*w[j+1,2]*twodt*R
    CM[j+1,:] = CP[j+1,:] + C[j+1,:,1]*R
    for i in range(len(angles)):
        cirCP[i,:,j] = np.dot(rotation_matrix(C[j,:,2],theta[i]),CP[j,:]-CM[j,:]) + CM[j,:]
    omega_z[j+1] = omega_z[j] + w[j+1,2]*twodt

### plot figure
fig1 = pyplot.figure(1)
ax = p3.Axes3D(fig1)
ax.view_init(elev=35, azim=-45)

def update_line(x, line, cirCP, CP):
    for indl, linein in enumerate(line):
        for li, angx1 in zip(linein,range(len(angles))):
            li.set_data(cirCP[0:2+indl,0,x],cirCP[0:2+indl,1,x])
            li.set_3d_properties(cirCP[0:2+indl,2,x])
    return line

line = [ax.plot(*[cirCP[j:j+2,i,0] for i in range(3)]) for j in range(len(angles))]

ax.set_xbound(-10,10)
ax.set_ybound(0,20)
ax.set_zbound(0,10)

line_ani = animation.FuncAnimation(fig1, update_line, ncp, fargs=(line,cirCP,CP),
                              interval=500, blit=False,repeat=True)

pyplot.show()

Activity

  1. WeatherGod commented on Aug 12, 2014

    @WeatherGod
    Member

    Confirmed (with mpl 1.2.1, though). I will need to double-check with the upcoming 1.4.0 release. I am not sure why this is happening as the Line3D code does not touch the color information. Indeed, if you put a "print li.get_color()" into the update_lines() function, you will see that it will still report colors and not grayscale values.

  2. whymranderson commented on Aug 14, 2014

    @whymranderson
    Author

    Thank you very much for checking this.

  3. tacaswell commented on Aug 14, 2014

    @tacaswell
    Member

    @WeatherGod I am inclined to not make this a blocker for 1.4.0 given how long it has been outstanding. Re-tag if you disagree.

  4. added this to the v1.4.x milestone on Aug 14, 2014
  5. WeatherGod commented on Aug 14, 2014

    @WeatherGod
    Member

    Absolutely, it is not a blocker. And it is certainly a puzzler, too..

    On Thu, Aug 14, 2014 at 11:56 AM, Thomas A Caswell <[email protected]

    wrote:

    @WeatherGod https://github.com/WeatherGod I am inclined to not make
    this a blocker for 1.4.0 given how long it has been outstanding. Re-tag if
    you disagree.

    —
    Reply to this email directly or view it on GitHub
    #3362 (comment)
    .

  6. durden commented on Aug 14, 2014

    @durden

    I've seen an issue that I think is related to this using 1.3.1. It seems as though calling set_color on a Patch3DCollection is completely ignored. I only see the issue when using a 3D plot.

    Below is a snippet similar to what I've been trying. You can comment the lines at the bottom to alternate which version (2d or 3d) you're testing. The same mechanism works for a 2d scatter plot, but not for a 3d one.

    The goal is to add a new point on each animation. Then, change all older points to a different color. It's not optimal and will constantly change the color of old points even if they've already been changed. However, it clearly demonstrates that the call to set_color doesn't have any effect even though it's a somewhat silly example.

    from mpl_toolkits.mplot3d import Axes3D
    import matplotlib.animation as animation
    import matplotlib.pylab as plt
    
    import numpy as np
    
    NUM_POINTS = 10
    values = []
    
    for ii in xrange(NUM_POINTS):
        values.append(np.random.random_integers(0, 500, 3))
    
    points = []
    
    n = 0
    
    
    def two_dim_example():
        def animate(i):
            """
            Goal is to slowly add new points and have the most current point in red
            while all other points are gray
            """
    
            global n
    
            # No more animation needed
            if n >= NUM_POINTS:
                return
    
            # Change color of all old points
            for point in points:
                point.set_color('gray')
    
            x, y, z = values[n]
    
            point = ax2D.scatter(x, y, s=75, c='red', marker ='o')
            points.append(point)
    
            n += 1
    
        ax2D = fig1.add_subplot(111)
        ani = animation.FuncAnimation(fig1, animate, interval=500)
        plt.show()
    
    
    def three_dim_example():
        def animate(i):
            """
            Goal is to slowly add new points and have the most current point in red
            while all other points are gray
            """
    
            global n
    
            # No more animation needed
            if n >= NUM_POINTS:
                return
    
            # Change color of all old points
            for point in points:
                point.set_color('gray')
    
            x, y, z = values[n]
    
            point = ax3D.scatter(x, y, z, s=50, c='red', marker ='o')
            points.append(point)
    
            n += 1
    
        ax3D = fig2.add_subplot(111, projection='3d')
        ani = animation.FuncAnimation(fig2, animate, interval=500)
        plt.show()
    
    
    # Uncomment to see that 2d works
    #fig1 = plt.figure()
    #two_dim_example()
    
    fig2 = plt.figure()
    three_dim_example()
  7. WeatherGod commented on Aug 14, 2014

    @WeatherGod
    Member

    Actually, that is a separate issue that has since been resolved, I believe
    (I haven't confirmed with your example against the upcoming 1.4.0 release).
    Patch3D objects weren't maintaining their colors correctly. These are
    Line3D objects, and they don't do anything with colors, just pass them down
    to the 2D versions. As I noted before, if you query the objects throughout
    the animation, they report back the correct colors, it is just the drawing
    that fails to use those colors, it seems.

    On Thu, Aug 14, 2014 at 5:03 PM, Luke Lee [email protected] wrote:

    I've seen an issue that I think is related to this using 1.3.1. It seems
    as though calling set_color on a Patch3DCollection is completely ignored.
    I only see the issue when using a 3D plot.

    Below is a snippet similar to what I've been trying. You can comment the
    lines at the bottom to alternate which version (2d or 3d) you're testing.
    The same mechanism works for a 2d scatter plot, but not for a 3d one.

    The goal is to add a new point on each animation. Then, change all older
    points to a different color. It's not optimal and will constantly change
    the color of old points even if they've already been changed. However, it
    clearly demonstrates that the call to set_color doesn't have any effect
    even though it's a somewhat silly example.

    from mpl_toolkits.mplot3d import Axes3Dimport matplotlib.animation as animationimport matplotlib.pylab as plt
    import numpy as np
    NUM_POINTS = 10values = []
    for ii in xrange(NUM_POINTS):
    values.append(np.random.random_integers(0, 500, 3))
    points = []
    n = 0

    def two_dim_example():
    def animate(i):
    """ Goal is to slowly add new points and have the most current point in red while all other points are gray """

        global n
    
        # No more animation needed
        if n >= NUM_POINTS:
            return
    
        # Change color of all old points
        for point in points:
            point.set_color('gray')
    
        x, y, z = values[n]
    
        point = ax2D.scatter(x, y, s=75, c='red', marker ='o')
        points.append(point)
    
        n += 1
    
    ax2D = fig1.add_subplot(111)
    ani = animation.FuncAnimation(fig1, animate, interval=500)
    plt.show()
    

    def three_dim_example():
    def animate(i):
    """ Goal is to slowly add new points and have the most current point in red while all other points are gray """

        global n
    
        # No more animation needed
        if n >= NUM_POINTS:
            return
    
        # Change color of all old points
        for point in points:
            point.set_color('gray')
    
        x, y, z = values[n]
    
        point = ax3D.scatter(x, y, z, s=50, c='red', marker ='o')
        points.append(point)
    
        n += 1
    
    ax3D = fig2.add_subplot(111, projection='3d')
    ani = animation.FuncAnimation(fig2, animate, interval=500)
    plt.show()
    

    Uncomment to see that 2d works#fig1 = plt.figure()#two_dim_example()

    fig2 = plt.figure()three_dim_example()

    —
    Reply to this email directly or view it on GitHub
    #3362 (comment)
    .

  8. durden commented on Aug 14, 2014

    @durden

    Ah, thanks for the update. Is there a commit or issue I can look into for that? I'm curious as to how it was fixed. Also, when is 1.4.0 slated for public release?

  9. WeatherGod commented on Aug 14, 2014

    @WeatherGod
    Member

    I don't have a link handy. In fact, the particular fix I was thinking off
    was already in v1.3.1, so this might actually be a legitimate bug. We are
    currently in release canidate mode, and you can get downloadable binaries
    from matplotlib's sourceforge page. If you are still experiencing the
    problem, then file a new bug report as it is involving a different mplot3d
    object type. If it is all related, we can join the reports later.

    On Thu, Aug 14, 2014 at 5:56 PM, Luke Lee [email protected] wrote:

    Ah, thanks for the update. Is there a commit or issue I can look into for
    that? I'm curious as to how it was fixed. Also, when is 1.4.0 slated for
    public release?

    —
    Reply to this email directly or view it on GitHub
    #3362 (comment)
    .

  10. durden commented on Aug 15, 2014

    @durden

    I've tested my snippet with the 1.4.x release candidate, and it still fails. I've created a new bug for this issue.

  11. modified the milestones: v1.4.x, on Aug 18, 2014
  12. whymranderson commented on Aug 20, 2014

    @whymranderson
    Author

    Yet again I think I have made a mistake!(another embarrassing moment :p ) After I changed the update_line function to the following, (changing only the start of the slicing index on the variable cirCP from 0 to indl) it seems to work just fine now! Still special thanks to WeatherGod for pointing out the color reporting issue that made me decide to keep digging into it. And also many many thanks to everyone for checking the code for me. It is such a small thing that it hides so well and I really had to go a huge circle to find it... Really apologize for reporting a wrong issue and it's damage. I am still quite new here so I won't be able to comment the other Patch3DCollection issue. Still appreciate if someone can confirm the following adjusted code is fine. Then maybe I should close this issue?

    def update_line(x, line, cirCP, CP):
        for indl, linein in enumerate(line):
            for li, angx1 in zip(linein,range(len(angles))):
                li.set_data(cirCP[indl:2+indl,0,x],cirCP[indl:2+indl,1,x])
                li.set_3d_properties(cirCP[indl:2+indl,2,x])
        return line
    
  13. whymranderson commented on Aug 20, 2014

    @whymranderson
    Author

    I guess it is another great lesson or example of showing the important of MWE (minimal working example). :)

  14. WeatherGod commented on Aug 20, 2014

    @WeatherGod
    Member

    Ah, I see what was happening! The last line being drawn was yellow (well,
    actually, that nasty, ugly dull "yellow"). With the update code, that
    yellow line had all of the segments of the coin and was being drawn on top
    of all of the other colored segments. I tried out the fix, and it seems to
    work fine. I think the only question is if it is intended for the colors of
    the segments to change. Picturing how a coin settles, the edges mostly only
    move up and down, but this visualization gives the impression that the coin
    is rotating significantly. Perhaps there is still some other error left in
    the code?

    On Wed, Aug 20, 2014 at 5:51 AM, whymranderson [email protected]
    wrote:

    I guess it is another great lesson or example of showing the important of
    MWE (minimal working example). :)

    —
    Reply to this email directly or view it on GitHub
    #3362 (comment)
    .

  15. whymranderson commented on Aug 21, 2014

    @whymranderson
    Author

    :) You are right. The code there does not fully contain the part that tracks the rolling without slipping motion. This code here does. I wasn't able to fix this part because of my mistake that makes the circle uni-color. Now that problem is fix, I also fix this rolling part too. Feel free to give the simulation a try. :) But I have to clearly indicate, I am still working on this, so it is still an artificial simulation, even though it got the rigid body equation of motion in it. Thanks a lot for the feedback :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions