3
$\begingroup$

just adding a circular array feature for Blender for Python addon.

enter image description here

I have:

  1. The blue circle and it's center (c1)
  2. a variable number of items in the array (count)
  3. The center for the array (CF).
  4. The normal of the blue circle

Are there some good ideas how to calculate c2..c4 in 3d space?

With this code it kind of works:

Almost :-) With this code, the centers of the array are not build around the CF:

def create_circle_array(self, count: int):
    self._array.clear()

    CF = get_face_center(self._hit_face, self._hit_obj)
    
    # Just to test if CF is set correctly.
    bpy.context.scene.cursor.location = CF 

    # self._center_3d is center of circle c1
    v1 = (self._center_3d - CF)

    r = v1.length  # Radius

    count = int(count + 1) 

    v1.normalize()

    v2 = self._normal.cross(v1)

    v1 = Vector(v1)
    v2 = Vector(v2)

    verts = []

    t = 0
    offset = 0  # Increase this to offset (in radians) the points along the circle perimeter
    while t < 2 * pi + offset:
        verts.append(CF + r * cos(t) * v1 + r * sin(t) * v2)
        t += 2 * pi / count

But the new centers are not build around CF:

enter image description here

$\endgroup$

1 Answer 1

4
$\begingroup$

Thankfully there are this answer on the maths sister site and this answer (in python, yay!) on SO that make the process pretty straightforward.

Given a normal normal and a point in space p we can position the points of a "slanted" circle like so :

from mathutils import Vector
from math import pi, cos, sin
from random import random, seed

n = normal
seed(0)  # Change seed to get a different pattern
v1 = Vector((random(), random(), random()))
# OR you can use the vector (C1 <-> CF) which you already know is orthogonal to n
v1 -= v1.dot(n) * n
v1.normalize()

v2 = v1.cross(n) # Get a third orthogonal vector

r = 1  # Circle Radius when looking in the direction of the normal
points = 9  # Here's your array counter

verts = []

offset = 0  # Increase this to offset (in radians) the points along the circle perimeter
t = offset
while t < 2 * pi + offset:
    verts.append(p + r * cos(t) * v1 + r * sin(t) * v2)
    t += 2 * pi / points

I created a simple script to test this in a scene with a plane. I added this beforehand :

import bpy
new_mesh = bpy.data.meshes.new("mesh")
new_obj = bpy.data.objects.new(name="mesh", object_data=new_mesh)

bpy.data.collections["Collection"].objects.link(new_obj)

normal = bpy.data.objects["Plane"].data.polygons[0].normal
p = Vector(bpy.data.objects["Plane"].data.polygons[0].center + bpy.data.objects["Plane"].matrix_world.translation)

and this at the end :

new_mesh.from_pydata(verts, [], [range(len(verts))])

Result :

enter image description here

Or use an operator:

bpy.ops.mesh.primitive_circle_add(
    vertices=points, 
    radius=r,
    location=p, 
    rotation=Vector((0, 0, 1)).rotation_difference(n).to_euler()
)
$\endgroup$
5
  • $\begingroup$ Almost :-) With the code (see my edition), the centers of the array are not build around the CF $\endgroup$
    – Jayanam
    Commented Jan 3, 2022 at 23:59
  • 1
    $\begingroup$ Alright, got it now, had to subtract self._center_3d Vector :-) Thx! $\endgroup$
    – Jayanam
    Commented Jan 4, 2022 at 0:06
  • $\begingroup$ Solved, but when I move the circle along the normal and then calculate the points, v1 is not orthogonal anymore. Do you have an idea for this? $\endgroup$
    – Jayanam
    Commented Jan 4, 2022 at 17:55
  • $\begingroup$ Maybe I'm not seeing this correctly but can't you also move C1 (or CF) by the same amount along the normal and recalculate v1 ? $\endgroup$
    – Gorgious
    Commented Jan 4, 2022 at 18:16
  • $\begingroup$ Yep, was missing this step: v1 -= v1.dot(self._normal) * self._normal Then radius before normalizing r = v1.length Now it works, thx again $\endgroup$
    – Jayanam
    Commented Jan 4, 2022 at 18:35

You must log in to answer this question.

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