Circular array of points perpendicular to a 3d vector

I’m trying to write an algorithm to calculate 3d points evenly spaced along the circumference of a circle on the face of a sphere.

The input values/variables I’m working with are:

R1 - Sphere radius (centre at 0,0,0)
R2 - Intersecting circle radius
L1 - Centre of sphere to centre of circle
P1 - Centre of circle (x,y,z)
N - Number of divisions of the circumference

I know this can be done for each point using trilateration (point intersection of 3 spheres) by establishing a chord length, but this seems quite cumbersome given that Sketchup Ruby has the Geom::Vector3d module.

On the image attached, an array of the x,y,z values at the ends of the spokes is the desired output.

Looking forward to a good discussion as I am new to this forum.


We usually ask that you at least make an attempt at solving your challenge.

I’d suggest defaulting N to 24 so you get a point every 15 degrees with 8 of the cardinal points.

I posted an example of generating 3D points (although for camera positions) using azimuth and elevation by applying a rotational transformation:

Thanks for replying and thanks for clarifying the protocol. Much appreciated.
My script has been designed to place component instances on polar vectors working with an icosphere data set containing only azimuth and altitude values. I then expanded the script to place scaled and rotated rings at any number of points along the vectors to create a more refined spherical shape.
Your rotational transformation idea should work as in all instances I have the coordinates of the centre of the circle and its radius and can derive the coordinates of the uppermost point of the circle using the altitude value of the vector. This uppermost point can then have a rotational transformation applied to array it around the circumference. I’ll give it a go and post what happens!

1 Like

Hi again, in working my way through this, but way before I get to the rotation translations, I’m getting what seems like a trigonometry glitch I can’t fathom when using atan() asin() or acos() to calculate the deviation angle from the centre to the top/bottom of each circle from the origin. I was getting such strange results I had to resort to using ent.add_text to work out what was going on. The sin, cos and tan ratios are correct, but they are returning incorrect angles, although very consistently. I’ve searched the web far and wide and have go no where!. Screenshot and script extract from the sub sub loop below.

  nnn = 1
  circ_rad = rad * new_scale

  # calculate x1, y1 & z1 (top/bottom point of circle)
  # rad is the sphere radius and new_rad is the distance from origin to the centre of each circle

  tan_ratio = circ_rad/new_rad
  sin_ratio = circ_rad/rad
  cos_ratio = new_rad/rad
  tan_angle = Math::atan(tan_ratio)
  sin_angle = Math::asin(sin_ratio)
  cos_angle = Math::acos(cos_ratio)

The values on the screenshot are from here:

  text_3 = "tan ratio #{tan_ratio}"     
  text_4 = "sin ratio #{sin_ratio}"
  text_5 = "cos ratio #{cos_ratio}"
  text_6 = "tan angle #{tan_angle * 180} deg"
  text_7 = "sin angle #{sin_angle * 180} deg"
  text_8 = "cos angle #{cos_angle * 180} deg"

I feel like I’m going a bit mad or overlooking something completely obvious. Just to confirm, the ratios are absolutely correct, but the atan etc values being returned are not. In the example screenshot, the angle should be around 11.2 degrees, not 35.

Thanks again!


The angles are radians. You need to scale by 180/pi not just 180.

1 Like

I knew it would be simple!!! I’ll try again!.. Thank you sooo much.


Note that SU extends the Numeric class with helper conversion methods between radians and angles: Class: Numeric — SketchUp Ruby API Documentation

180.degrees # Returns the radians

Yes it does, thank you. My problem was that I was using both and losing track, other than that stupid bit forgetting to divide by pi, which I have done elsewhere in the algorithm without issue. It just takes another pair of eyes. I have the primary rotation sorted now, just working on the remaining transformations to get the objects all facing the origin. Test render screenshot attached. Once complete I will post the full script.

1 Like

Hi again. I got there in the end. The transformations to get the components facing the centre of the sphere were quite complex, with exceptions for z< 0, altitude (elevation) > pi/2 etc etc. The final code and test render are below. The input variables were:

n_divs = 10 (circles per hemisphere)
sub_divs = 7 (number of insertion points on each circle)

alt = the altitude (elevation) of the vector at the centre of the circles
azi = the azimuth of the same vector
rad = sphere radius
new_rad = circle radius
new_alt = the altitude (elevation) of the top/bottom of the circle, where the component is first placed

The final rotation, placing the components around each circle was simple, orienting them towards the centre was not and took 4 separate rotations!

Thanks to all who contributed for the invaluable help.


calculate component rotation values

angle_1 = (pi/2 + azi*pi/180)
angle_2 = pi/2
angle_3 = (pi/2 - alt*pi/180)
  if z < 0
    angle_1 = angle_1 * -1
    angle_3 = angle_3 * -1
  if face_origin == 0
    angle_2 = 0
    angle_3 = 0

  if z_scale == 0
    z_scale = new_scale
    z_scale = z_scale + scale_inc

  # insert, scale and rotate components

  if sub_divs > 0  # points code block and loop

  circ_rad = rad * new_scale
  circ_angle = 2 * pi / sub_divs

  # calculate x1, y1 & z1 (top/bottom point of circle)

  if z >= 0
    new_alt = pi*alt/180 + Math::atan(circ_rad/new_rad)
    new_alt = pi*alt/180 - Math::atan(circ_rad/new_rad)

  if (-pi/2..pi/2) === new_alt
    angle_3a = pi/2 - new_alt
    angle_3a = new_alt - pi/2

    x1 = rad * Math::sin(pi/2 - new_alt) * Math::cos(azi * pi/180)
    y1 = rad * Math::sin(pi/2 - new_alt) * Math::sin(azi * pi/180)
    z1 = rad * Math::cos(pi/2 - new_alt)

    if z1 < 0
      angle_3a = angle_3a * -1

    nnn = 1

    loop do   # points loop

    if z > 0
      angle_5 = (nnn + 0.5) * circ_angle
      angle_5 = ((nnn + 0.5) * circ_angle) + pi

    ent = Sketchup.active_model.entities
    t0 = Geom::Transformation.scaling new_scale, 0.5, 1
    t1 = Geom::Transformation.translation [x1, y1, z1]
    t2 = Geom::Transformation.rotation [x1, y1, z1], [0, 0, z1], angle_1
    t3 = Geom::Transformation.rotation [x1, y1, z1], [0, 0, z1], angle_2
    t4 = Geom::Transformation.rotation [x1, y1, z1], [x1, y1+0.00000001, 0], angle_3a
    t5 = Geom::Transformation.rotation [x1, y1, z1], [0, 0, z1], angle_2
    t6 = Geom::Transformation.rotation [0, 0, 0], [x, y, z], angle_5

    inst = ent.add_instance comp_def_4, t6 * t5 * t4 * t3 * t2 * t1 * t0

    nnn = nnn + 1

    if nnn > sub_divs

    end   # close points loop

  end   # close points code block and loop