I'm animating the coffee mug torus homeomorphism.
I want my animation to match this gif as closely as possible. The gif above, along with the POV-ray code used to create it, can be found here:https://en.wikipedia.org/wiki/File:Mug_and_Torus_morph.gif.
I'm working with a relatively low poly mesh for now and here is what I have created:
The first part of the Wikipedia animation where the base of the cup rises is easy to make, so I'm excluding that part of the animation for this post. I just need help on the main transformation part.
To create this, I wrote a python script that sends the points of the cup to the closest point on the torus. The pseudocode of the python code is roughly as follows:
# for each vertex of the mug, # keyframe the vertex coordinate at initial frame # find the angle of the vertex relative to the center of the torus # find the central point of the torus of which this angle belongs to (this new central point is a point along the major radius ring) # solve for where the line through the vertex and central point intersects the torus (there are two points of intersection) # set vertex coordinate to value of intersection that's closest to the vertex # keyframe vertex coordinate at final frame
Here is the actual code:
import bpy from math import * from mathutils import * mug = bpy.context.scene.objects["MorphingMug"] verts = mug.data.vertices #initial handle torus and target torus have a major radius of: bigR = .5 #target torus has radius of: r = .25 #center of both initial handle torus and target torus has x value of: originX = .8 #for each vertex of the mug for v in verts: #keyframe vertex at framei v.keyframe_insert(data_path = "co", frame = 1) #get x, y, and z values of vertex x = v.co.x y = v.co.y z = v.co.z #difference vector between vertex and (center of torus = [originX, 0, 0]) d = Vector([x - originX, y, z]) #find the angle in the xz-plane of this difference vector if v.co == Vector([originX, 0, 0]): theta = pi elif d.x == 0 and d.z > 0: theta = pi/2 elif d.x == 0 and d.z < 0: theta = -pi/2 elif d.x < 0: theta = pi + atan(d.z/d.x) else: theta = atan(d.z/d.x) #find the central point of the target torus of which this angle belongs to c_1 = bigR * cos(theta) + originX c_3 = bigR * sin(theta) #line parameterization values of where the line through vertex and central point intersect target torus t1 = -r/sqrt(c_1**2 - 2*c_1*x + c_3**2 - 2*c_3*z + x**2 + y**2 + z**2) + 1 t2 = r/sqrt(c_1**2 - 2*c_1*x + c_3**2 - 2*c_3*z + x**2 + y**2 + z**2) + 1 #first value of intersection v1 = x + t1*(c_1 - x) v2 = y + t1*(c_2 - y) v3 = z + t1*(c_3 - z) vec1 = [v1, v2, v3] #second value of intersection v1 = x + t2*(c_1 - x) v2 = y + t2*(-y) v3 = z + t2*(c_3 - z) vec2 = [v1, v2, v3] #difference of these intersection values and vertex diff1 = Vector(vec1) - v.co diff2 = Vector(vec2) - v.co #choose the value that's closest to vertex if diff1.magnitude > diff2.magnitude: vec = vec2 else: vec = vec1 #set vertex equal to closest intersection v.co = vec #keyframe at final position v.keyframe_insert(data_path = "co", frame = 120)
There are clearly some issues with my animation. I don't think I'm seeing the math to this projection clearly. I do see that the top face up the cup should be sent to the uppermost point of the torus and same for the bottom face and the lowermost point. I also see the rightmost points of the non-handle part of the cup that are also at y = 0 are mapped upwards and downwards, which is incorrect, except for the point [.8, 0, 0], which I manually mapped to the correct position.
Maybe I need to pick different projective origin points based on the vertex position? Maybe I should split the mesh into vertex groups to make it more of a piecewise transformation? Maybe I need to reposition the non-handle part of the mug slightly more in the +x direction? Any help would be greatly appreciated. Thanks.
my file can be downloaded here: