2

I have a cube and an icosphere and the cube inside the icosphere is rotating.

The vertices of the cube will connect to the vertices on the icosphere by its shortest distance which I use KD tree to implement. But when the cube is rotating, the shortest connection line will not update. Below is the code I use:

bpy.ops.mesh.primitive_cube_add(size=0.51,location=(0,0,0))
mycube = bpy.context.object  
bpy.ops.mesh.primitive_ico_sphere_add(radius=3,location=(0,0,0))
mysphere = bpy.context.object
mymodifier = mysphere.modifiers.new(name="wire", type='WIREFRAME')

degp = bpy.context.evaluated_depsgraph_get() cube_eval = mycube.evaluated_get(degp) cube_mesh_eval = cube_eval.data

sphere_eval = mysphere.evaluated_get(degp) sphere_mesh_eval = sphere_eval.data
spheresize=len(sphere_mesh_eval.vertices) kd = mathutils.kdtree.KDTree(spheresize)

for i, v in enumerate(sphere_mesh_eval.vertices): kd.insert(v.co, i)

kd.balance()

for v in cube_mesh_eval.vertices: print(v.co) #co_find = (0.0, 0.0, 0.0) co, index, dist = kd.find(v.co) print("Close to center:",v.index, co, index, dist) #add line bm = bmesh.new() v1 = bm.verts.new(v.co) v2 = bm.verts.new(co) bm.edges.new((v1, v2)) me = bpy.data.meshes.new("Mesh") bm.to_mesh(me) bm.free() # Add the mesh to the scene obj = bpy.data.objects.new("line"+str(v.index), me) bpy.context.collection.objects.link(obj)

mycube.rotation_euler.z =0 mycube.keyframe_insert("rotation_euler", frame=0)
mycube.rotation_euler.z =30 mycube.keyframe_insert("rotation_euler", frame=totalmoves)
degp.update

Duarte Farrajota Ramos
  • 59,425
  • 39
  • 130
  • 187
Derekcbr
  • 99
  • 1
  • 8
  • 1
    This is running once. If you want it to run on every frame, either use an animation node or a frame handler https://docs.blender.org/api/current/bpy.app.handlers.html It doesn't update because it is only running once AFAIK – 4nof Jun 22 '20 at 01:48
  • Correct, thanks a lot! I am trying to use handlers as Animation Nodes is a bit complicated. Which part of code should be inside my_handler to make it work. I tried may ways but still failed. def my_handler(scene): bpy.app.handlers.frame_change_post.append(my_handler) – Derekcbr Jun 22 '20 at 08:37

1 Answers1

1

Using the animation system

enter image description here Result of making an "edge object" parented to cubes zeroth vertex to the closest vertex point on the ico sphere. A cone is constrained to tip of edge and tracked to the cube. Its motion path has been calculated and displayed for 100 frames

Set Up

Set up your scene, blender rotation unit is natively radians, Using Q code and for this example adding keyframes to rotate cube one revolution in total_moves plus one frames.

import bpy
from math import radians

bpy.ops.mesh.primitive_cube_add( size=0.51, location=(0,0,0)) cube = bpy.context.object
bpy.ops.mesh.primitive_ico_sphere_add( radius=3, location=(0,0,0)) ico = bpy.context.object wfm = ico.modifiers.new(name="wire", type='WIREFRAME')

total_moves = 100

cube.rotation_euler.z = 0 cube.keyframe_insert("rotation_euler", frame=0)
cube.rotation_euler.z = radians(360) cube.keyframe_insert("rotation_euler", frame=total_moves + 1)

Add the "edge" mesh

Next will set up the shortest edge mesh. Contend that instead of creating an edge mesh for each frame can create a single edge mesh and move it. (would also note its possible that all verts of cube are equally close to a vert on ico,)

Make a two vert one edge mesh, both coords at origin (0, 0, 0) For use later have added one vertex to vertex group "Tip".

Consider all variables to be "as above" if not defined.

verts = [
    (0, 0, 0),
    (0, 0, 0),
]

edges = [ (0, 1), ]

me = bpy.data.meshes.new("Edge") me.from_pydata(verts, edges, []) edge = bpy.data.objects.new("Edge", me) vg = edge.vertex_groups.new(name="Tip") vg.add([1], 1.0, 'REPLACE') collection.objects.link(edge)

Parenting

Parenting can be our friend here. If we parent the edge to the cube, then the location of the edge can be set using local coordinates of cube.

Instead I'm going to use vertex parenting. The example below parents the edge to the zeroth (index=0) vertex.

edge.parent = cube
edge.parent_type = 'VERTEX'
edge.parent_vertices = [0, 0, 0]

Could set the location (and or parent) to cube vertex and the location of other vert of this one object using code in question in a frame change handler for example. See https://blender.stackexchange.com/a/183688/15543 re handler code using (non vertex) parented edge object to cube.

Shrinkwrap

Instead I'm going to put up the idea of using shrinkwrap.

swm = edge.modifiers.new("SWM", 'SHRINKWRAP')
swm.target = ico
swm.vertex_group = "Tip"

enter image description here

Image shows the result. The closest point on the wireframe ico is a halfway point on an edges (Since the verts are on sphere surface, a straight line to another cuts into the sphere...) Motion trail from tip to vert (gif above) displays the vert chosen.

Make a disc on vert shrinkwrap target

Make another ico with arbitrarily small faces at its verts.

from mathutils import Matrix
import bmesh

bm = bmesh.new() for v in ico.data.vertices: co = v.co M = v.co.to_track_quat('Z', 'Y').to_matrix().to_4x4() M.translation = v.co bmesh.ops.create_circle( bm, cap_ends=True, segments=16, radius=1e-6, matrix = M )

me = bpy.data.meshes.new("IcoSpots") bm.to_mesh(me) ico2 = bpy.data.objects.new("IcoSpots", me) ico2.matrix_world = ico.matrix_world.copy()

collection.objects.link(ico2)

This object can now instead be used as a target of the edge objects shrinkwrap modifier.

Notes.

This is put forward by way of concept. It is only creating the one edge object and parenting it to one vertex by way of demonstration. I also didn't use the modified ico mesh to make the "spot" mesh.

To display the shortest would be tempted to add an edge for each cube vert, and drive its visibility based on other lengths.

batFINGER
  • 84,216
  • 10
  • 108
  • 233