Chaos game rhinopython

Hi there! I am trying to generate chaos game code in rhinopython. I am trying to create something similar in photo that i uploaded. I was able to find this python code for chaos game but i don’t know how to implement this to rhinopython. Any ideas? Thanks in advance.

import pygame
import random
import math
import colorsys
from pygame.locals import *

idx = [0, 0, 0]

def mark_pixel(surface, pos, pcol):
col = surface.get_at(pos)
surface.set_at(pos, (min(col[0] + pcol[0]/10, 255),
min(col[1] + pcol[1]/10, 255),
min(col[2] + pcol[2]/10, 255)))

def random_point_index(p):
global idx
idx[2] = idx[1]
idx[1] = idx[0]
dst1 = abs(idx[1] - idx[2])

while True:
    idx[0] = random.randint(0, len(p) - 1)
    dst = abs(idx[0] - idx[1])
    if dst1 == 0 and (dst == 1 or dst == len(p) - 1):
        continue
    else:
        break

return idx[0]

def init_polygon(width, height, n):
delta_angle = 360/n
r = width/2 - 10
p =

for i in range(0, n):
    angle = (180 + i*delta_angle) * math.pi / 180
    color = colorsys.hsv_to_rgb((i*delta_angle)/360, 0.8, 1)
    p.append(((width/2 + r*math.sin(angle),
               height/2 + r*math.cos(angle)),
              (int(color[0]*255), int(color[1]*255), int(color[2]*255))))
return p

def main(width, height, n, r):
pygame.init()
surface = pygame.display.set_mode((width, height))
pygame.display.set_caption(‘Das Chaos Spiel’)

p = init_polygon(width, height, n)

x, y = (400, 300)
for i in range(0, width*height*3):
    i = random_point_index(p)
    x += (p[i][0][0] - x) * r
    y += (p[i][0][1] - y) * r

    mark_pixel(surface, (int(x), int(y)), p[i][1])
    if i % 1000 == 0:
        pygame.display.update()

    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            return

pygame.image.save(surface, 'chaosspiel.jpg')
pygame.quit()

if name == “main”:
main(500, 500, 5, 0.45)

Check this out:

:wink:

1 Like

hello! it look great thanks! but how can i implement it to rhino python? I couldn’t do it unfortunately:(

Check out the link I posted above. It leads to a reply of mine, where a Grasshopper file is linked. In the file, you’ll find a Grasshopper Python component that includes the relevant code. Simply double click it and the code editor will pop up, showing the code.

Here’s a revised, simplified version of the script that works in Rhino:

"""chaos_game_rhino.py: Chaos Game Rhino implementation inspired by Daniel Shiffman's approach in Processing from Coding Train on YouTube."""

__author__ = "diff-arch (diff-arch.xyz)"
__version__ = "0.2.0b (2022-02-06)"


from scriptcontext import sticky
import rhinoscriptsyntax as rs
import Rhino.Geometry as rg
import random
import math


class ChaosGame:
    def __init__(self, update_speed=1, randomize=False):
        self.width = 500
        self.height = 500
        self.update_speed = int(update_speed)  # default 1 equals real time
        self.randomize = randomize
        self.percent = 0.5  # percentile of the distance between two points (usually 0.5 for the center)
        self.num_init_pts = 3
        self.init_pts = []  # initial needle points
        self.per_pts = [[] for i in xrange(self.num_init_pts)]
        self.curr_pt = rg.Point3d(random.uniform(0.0, self.width), random.uniform(0.0, self.height), 0.0)
        self.prev_pt = rg.Point3d(0.0, 0.0, 0.0)
        
    def setup(self):
		if len(self.init_pts) == 0:
			if self.randomize: 
	            # Randomly distribute the initial points
				for i in xrange(self.num_init_pts):
					pt = rg.Point3d(random.uniform(0.0, self.width), random.uniform(0.0, self.height), 0.0)
					self.init_pts.append(pt)
			else:
				# Distribute the initial points around a circle
				radius = min(self.width, self.height) / 2
				theta = (2 * math.pi) / 3
				for i in xrange(self.num_init_pts):
					delta = theta * i + math.pi / 2
					pt = rg.Point3d(math.cos(delta) * radius, math.sin(delta) * radius, 0)
					self.init_pts.append(pt)
    
    def update(self):
        for i in xrange(self.update_speed):  # update conditionless (without rules)
            r = random.randint(0, self.num_init_pts-1)
            next_pt = self.init_pts[r]
        
            if sum([len(lt) for lt in self.per_pts]) == 0: # first update
                per_pt = (next_pt + self.curr_pt) * self.percent
            else: # other updates
                per_pt = (next_pt + self.curr_pt) * self.percent
        
            self.curr_pt = per_pt
            self.per_pts[r].append(per_pt)


##################################################################################            


if __name__ == "__main__":
	
	#Get user input
	count = rs.GetInteger("Number of Iterations", 500, 1)
	speed = rs.GetInteger("Update Speed", 15, 1)
	randomize = rs.GetBoolean("Randomize Sierpinski Needle Points", [("Randomize", "Off", "On")], (False))[0]
	
    # Initialize Chaos Game
	cg = ChaosGame(speed, randomize)
	cg.setup()
	curr_pts = cg.init_pts
	
	# Bake the intial points to rhino
	for i in xrange(len(curr_pts)):
		rs.AddPoint(curr_pts[i])
	rs.Redraw()
	
	# Update Chaos Game
	for i in xrange(count):
		cg.update()
	
	# Bake the Chaos Game points to Rhino
	rs.EnableRedraw(False)
	curr_pts = cg.per_pts
	for i in xrange(len(curr_pts)):
		for pt in curr_pts[i]:
			rs.AddPoint(pt)
	rs.EnableRedraw(True)

For the sake of simplicity, I’ve removed the classes that inherit from the base Chaos Game one and produce more advanced game outcomes. Currently, the script thus only generates regular and/or randomized Sierpinski triangles, but you should be able to reintroduce the other classes from the GHPython component, since the code is pretty modular.

The script regularly produces a couple of random stray points that are not part of the desired outcome, here the Sierpinski triangle. If I remember right, this is due to Shiffman’s method begin flawed, from which my code was initially derived.
Since the whole process is randomized, you can rerun the script until you get a perfect iteration.

chaos_game_rhino.py (2.9 KB)

1 Like