5

Some help from you blender wizards is needed

I have a task to create dials for nearly thousand clock designs with different shapes, something like this (the script generate mesh from .svg and places clock arrows by the coordinates in the middle) enter image description here

By creating 4 paths and then clamping dial objects to them I am able to move objects on coret axes but moving them by hand will take agesenter image description here

Guess I am looking for way to push dial objects inwards and stop them right before they touch the mesh. Remember that every object will have a different shape, so i cant use fixed numbers, I was thinking to use force field physics to push them inward, by magnetic force or something like that

Do you guys have some solutions or suggestions will appreciate any help, thank you?

Anton
  • 111
  • 6

3 Answers3

6

UPD

Finally have managed to solve the problem by using Rigid Body Constraints and wind force physics to push dial objects inwards

enter image description here

Anton
  • 111
  • 6
3

Thought about using Shrinkwrap + instancing, but that can make the arrows squeeze through tiny holes and overlap with the mesh. So I used lemon's answer here:

Python in Blender 2.8 - testing if two objects overlap in the XY Plane?

import bpy
from mathutils.bvhtree import BVHTree

center_object = bpy.data.objects["S"] initial_step = 0.01 # 1 cm initial step size min_step = 0.0001 # accuracy step_limit = 1000 # just in case to not hang, though you can always press CTRL+C in system console

def main(): for o in bpy.data.objects: if o.name.startswith("Cone"): adjust_cone(o)

def adjust_cone(cone): if not any(cone.location): print(f"{cone.name} is in the center") # can't figure direction return

while is_colliding(cone):
    # move away from center
    cone.location += cone.location.normalized()*.1
    bpy.context.view_layer.update()

current_step = initial_step
for _ in range(step_limit):
    prev_loc = cone.location
    cone.location -= cone.location.normalized()*current_step
    bpy.context.view_layer.update()
    if is_colliding(cone):
        cone.location = prev_loc
        bpy.context.view_layer.update()
        current_step /= 2
        if current_step < min_step:
            break


def is_colliding(cone): # Get the objects obj1 = cone obj2 = center_object

# Get their world matrix
mat1 = obj1.matrix_world
mat2 = obj2.matrix_world

# Get the geometry in world coordinates
vert1 = [mat1 @ v.co for v in obj1.data.vertices] 
poly1 = [p.vertices for p in obj1.data.polygons]

vert2 = [mat2 @ v.co for v in obj2.data.vertices] 
poly2 = [p.vertices for p in obj2.data.polygons]

# Create the BVH trees
bvh1 = BVHTree.FromPolygons(vert1, poly1)
bvh2 = BVHTree.FromPolygons(vert2, poly2)

# Test if overlap
return bool(bvh1.overlap(bvh2))


main()

The script is blatanly inefficient, but since it's a learning site I wanted to make the logic simple. Optimizing doesn't take a whole lot of effort, though: instead of moving the arrow object and updating the scene, just modify the coordinates of the vertices passed to the BVHTree as well as have a variable keeping the total tranlation, apply that to the object once in the end.

Markus von Broady
  • 36,563
  • 3
  • 30
  • 99
2

You can use object constraints for that. The "limit distance" object constraint does exactly this. It limits/sets the distance to outside/surface of the "target" object - which would be the clock in your case. With "distance" you can determine the...you guessed it - distance ;)

Like this:

enter image description here

Chris
  • 59,454
  • 6
  • 30
  • 84