Greetings,
I was looking for a Python solution for circle packing on surface, or at least on a boundary.
Most useful seems to be the VBscript written by Steven Janssen, which I translated to Python using ChatGPT. (maybe somone should include the translation on the developer page?)
But anyway, is there any Python that can solve circle packing on a surface or boundary? I don’t like to use Kangaroo.
Thank you!
# Original VBScript by Steven Janssen
# https://developer.rhino3d.com/samples/rhinoscript/circle-packing/
# Translated to Python by BC using ChatGPT, 241120
import rhinoscriptsyntax as rs
import random
import math
def main():
# Get Input Circles
arr_input_r = rs.GetObjects("Select Circles", rs.filter.curve)
if not arr_input_r:
return
# Get Radii from Input Circles
for i, curve in enumerate(arr_input_r):
if rs.IsCircle(curve):
arr_input_r[i] = rs.CircleRadius(curve)
# Get Number of Circles
int_circle_number = rs.GetInteger("Number of Circles", 1000)
if int_circle_number is None:
return
int_circle_number -= 1
arr_point = [None] * (int_circle_number + 1)
arr_radius = [None] * (int_circle_number + 1)
# Draw 1st Circle
arr_radius[0] = random.choice(arr_input_r)
arr_point[0] = rs.GetPoint("Centre of Circle")
if not arr_point[0]:
return
str_current_circle_id = rs.AddCircle(arr_point[0], arr_radius[0])
rs.EnableRedraw(False)
# Draw 2nd Circle
arr_radius[1] = random.choice(arr_input_r)
arr_point[1] = list(arr_point[0]) # Convert to list for mutability
arr_point[1][0] += arr_radius[0] + arr_radius[1]
str_current_circle_id = rs.AddCircle(arr_point[1], arr_radius[1])
int_current_centre = 0
int_hole = 1
# Draw other Circles
for k in range(2, int_circle_number + 1):
rs.StatusBarMessage(f"{k+1}/{int_circle_number+1}")
arr_radius[k] = random.choice(arr_input_r)
while True:
marker = 0
# Calculate the lengths of the sides
arr_side = [0, 0, 0]
arr_side[0] = rs.Distance(arr_point[int_current_centre], arr_point[k - int_hole])
arr_side[1] = arr_radius[k] + arr_radius[int_current_centre]
arr_side[2] = arr_radius[k] + arr_radius[k - int_hole]
# Calculate Angle
dbl_cos_a = (arr_side[0] ** 2 + arr_side[1] ** 2 - arr_side[2] ** 2) / (2 * arr_side[0] * arr_side[1])
if dbl_cos_a > 1:
marker = 1
else:
dbl_rot_a = math.degrees(math.acos(dbl_cos_a))
# Create, rotate, and scale Vector
vector = rs.VectorCreate(arr_point[k - int_hole], arr_point[int_current_centre])
vector = rs.VectorRotate(vector, dbl_rot_a, [0, 0, 1])
vector = rs.VectorScale(vector, arr_side[1] / arr_side[0])
arr_point[k] = rs.VectorAdd(vector, arr_point[int_current_centre])
# Check if Circle will Intersect with Existing Circles
for checkloop in range(k - 1, -1, -1):
checkdistance_a = rs.Distance(arr_point[k], arr_point[checkloop]) + 0.001
checkdistance_b = arr_radius[k] + arr_radius[checkloop]
if checkdistance_a < checkdistance_b:
marker = 1
break
if marker == 0:
str_current_circle_id = rs.AddCircle(arr_point[k], arr_radius[k])
# Exit the loop if the Circle is Good
if marker == 0:
int_hole = 1
break
int_current_centre += 1
if int_current_centre == k - int_hole:
int_hole += 1
int_current_centre = 0
rs.EnableRedraw(True)
if __name__ == "__main__":
main()