require 'propane' require 'arcball' class FibonacciSphere < Propane::App # fibonacci_sphere.rb # After a vanilla processing sketch by Jim Bumgardner # http://www.openprocessing.org/sketch/41142 # # Controls: # 1. drag mouse to rotate sphere (uses builtin arcball library) # 2. click mouse to toggle add box to sphere surface # 3. press x, y, or z to constrain arcball rotation to that axis # PHI = (sqrt(5) + 1) / 2 - 1 # golden ratio GA = PHI * TAU # golden angle KMAX_POINTS = 100_000 attr_reader :pts, :rotation_x, :rotation_y, :nbr_points, :radius, :add_points def setup size(1024, 768, P3D) Processing::ArcBall.init(self, width / 2.0, height / 2.0) @rotation_x = 0 @rotation_y = 0 @nbr_points = 2000 @radius = 0.8 * height / 2 @add_points = true @pts = Array.new(KMAX_POINTS) init_sphere(nbr_points) background(0) end def draw if add_points @nbr_points += 1 @nbr_points = [nbr_points, KMAX_POINTS].min init_sphere(nbr_points) end background 0 lights ambient(200, 10, 10) ambient_light(150, 150, 150) render_globe end ########################################### # For Fibonacci Sphere ################################## def render_globe push_matrix (0..[nbr_points, pts.length].min).each do |i| lat = pts[i].lat lon = pts[i].lon push_matrix rotate_y(lon) rotate_z(-lat) fill(200, 10, 10) translate(radius, 0, 0) box(4, 7, 7) pop_matrix end pop_matrix end def mouse_clicked @add_points = !add_points end SpherePoint = Struct.new(:lat, :lon) def init_sphere(num) (0..num).each do |i| lon = GA * i lon /= TAU lon -= lon.floor lon *= TAU lon -= TAU if lon > PI # Convert dome height (which is proportional to surface area) to latitude # lat = asin(-1 + 2 * i / num.to_f) pts[i] = SpherePoint.new(asin(-1 + 2 * i / num.to_f), lon) end end end FibonacciSphere.new title: 'Fibonacci Sphere'