1
$\begingroup$

In Blender 3.2.1, I need a unit mesh circle at origin and path on the x-axis with a length of $2\pi$. Object constraint on circle to follow path. Keyframe on unit circle at 0 & 250 with $360^{\circ}$ z-rotation. How do I get a single vertex on the unit circle to trace the sine or cosine wave?

$\endgroup$
7

1 Answer 1

2
$\begingroup$

Just copy the script and execute then press spacebar in the Timeline to play the animation or you can scrub through the timeline from frame 0 to 250.

enter image description here

import bpy
import math

REVOLUTION = 360
TOTAL_FRAMES = 250
SINEWAVE_RESOLUTION = 64
RADIUS = 1

bpy.context.scene.frame_start = 1
bpy.context.scene.frame_end = TOTAL_FRAMES
bpy.context.scene.frame_current = 1


def get_object(name):
    objects = bpy.context.scene.objects
    if name in objects:
        return objects[name]
    m = bpy.data.meshes.new(name + "-mesh")
    o = bpy.data.objects.new(name, m)
    #o.modifiers.new(name, 'SKIN')
    bpy.context.collection.objects.link(o)
    return o 

def get_empty(name):
    objects = bpy.context.scene.objects
    if name in objects:
        return objects[name]
    bpy.ops.object.empty_add(type='SPHERE', align='WORLD', location=(0, 0, 0))
    empty = bpy.context.object
    empty.name = name
    empty.scale = 0.1, 0.1, 0.1 
    return empty

# ==================================================================================================
# Descritpion: plot the sinwave with some points 
# ==================================================================================================

def get_range(start, end, step = 2):
    return [x * 0.1 for x in range(start * 10, end * 10, step)]

def get_sinewave_x(angle):
    return RADIUS * math.cos(REVOLUTION - math.radians(angle))

def get_sinewave_y(angle):
    return RADIUS * math.sin(math.radians(angle))

def get_sinewave_length():
    return RADIUS * 4

def draw_sinwave(angle, id):
    verts = []
    xunits = get_sinewave_length()
    for u in range(0, SINEWAVE_RESOLUTION + 1):
        a = u / SINEWAVE_RESOLUTION * REVOLUTION
        px = xunits / SINEWAVE_RESOLUTION * u
        py = get_sinewave_y(a)
        if (a <= angle):
            verts.append([px, py, 0.0])

    #verts.append([xunits, 0, 0.0])
    edges = []
    for i in range(len(verts)-1):
        edges.append((i, i+1))

    o = get_object("sinewave-" + id)
    m = o.data
    m.clear_geometry()
    m.from_pydata(verts, edges, ())

# ==================================================================================================
# Create Circle
# ==================================================================================================

def create_circle(n):
    o = get_object("circle")
    exists = len(o.data.vertices) > 0
    if exists:
        return o
 
    verts = []
    for i in range(n):
        verts.append((0.0, 0.0, 0.0))
    edges = []
    for i in range(len(verts)-1):
        edges.append((i, i+1))
    edges.append((i+1, 0))

    m = o.data
    m.clear_geometry()
    m.from_pydata(verts, edges, ())
    return o

def draw_circle(r, segments = 32):
    o = create_circle(segments)
    o.location = 0.0, 0.0, 0.0

    for i in range(segments):
        a = 2 * math.pi / segments * i
        o.data.vertices[i].co = math.cos(a) * r, math.sin(a) * r, 0.0

# ==================================================================================================
# draw lines connecting circle and x-axis
# ==================================================================================================

def draw_line(x, y, px, py, id):
    o = get_object("line-" + id)
    m = o.data
    verts = m.vertices
    exists = len(verts) > 0

    if exists:
        verts[0].co = x, y, 0.0
        verts[1].co = px, py, 0.0
        return

    verts = []
    verts.append((x, y, 0.0))
    verts.append((px, py, 0.0))

    m.clear_geometry()
    m.from_pydata(verts, [(0,1)], ())

# ==================================================================================================
# draw geometry
# ==================================================================================================

def draw_empties(x, y, px, py):
    get_empty("empty-1").location = x, y, 0.0
    get_empty("empty-2").location = px, py, 0.0

def draw_circle_angle(angle):
    rad = math.radians(angle)
    draw_circle(RADIUS)
    l1x = RADIUS * math.cos(math.pi * 2 - rad)
    l1y = RADIUS * math.sin(rad)
    l2x = angle / REVOLUTION * get_sinewave_length()
    l2y = get_sinewave_y(angle)
    draw_line(0, 0, l1x, l1y, "1")
    draw_line(l2x, 0, l2x, l2y, "2")
    draw_empties(l2x, l2y, l1x, l1y)

# ==================================================================================================
# animation functions
# ==================================================================================================

def animate():
    f = bpy.context.scene.frame_current
    angle = REVOLUTION / TOTAL_FRAMES * f
    draw_sinwave(angle, '1')
    draw_circle_angle(angle)

# ==================================================================================================
# execute code
# ==================================================================================================

def on_enter_frame(a,b):
    animate()

bpy.app.handlers.frame_change_post.clear() 
bpy.app.handlers.frame_change_post.append(on_enter_frame)

draw_sinwave(REVOLUTION, '2')
on_enter_frame(None, None)

for o in bpy.context.scene.objects:
    o.select_set(True)
bpy.context.view_layer.objects.active = get_object("sinewave-1")

bpy.context.scene.objects['sinewave-2'].select_set(False)
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .