orbit-0
2012. 06. 07.

It's a metaphor of planets revoling around a star. This is actually an ongoing work, nevertheless as it already looks quite nice, I decided to post it as it is now.

Click on the screen to regenerate the pattern. Press enter key to toggle background color.

» launch orbit-0

rint = (a, b) -> parseInt random(a, b)
Array::sample = -> this[rint(0, this.length)]

class AngularRange
  constructor: ->
    @slices = [[0, TWO_PI]]

  consume: (f, t) ->
    new_slices = []
    _(@slices).each (r) ->
      if r[0] > f or r[1] < t
        new_slices.push r
      else
        new_slices.push [r[0], f] if f > r[0]
        new_slices.push [t, r[1]] if t < r[1]
    @slices = new_slices

  sample: -> @slices.sample()

class Planet
  constructor: (@t, @td, @diameter, @R, @c) ->
    @h = hue @c
    @s = saturation @c
    @b = brightness @c
    @attrs = {}
    for s in [@diameter/2..@diameter] by 2
      @attrs[s] = {
        rd: random(-1, 1)
        rx: random(-1, 1) * @diameter * 0.1
        ry: random(-1, 1) * @diameter * 0.1
        sx: s * random(0.8, 1.2)
        sy: s * random(0.8, 1.2)
      }
    null

  move: ->
    pushMatrix()
    # @t += @td
    x = @R * cos(@t)
    y = @R * sin(@t)
    translate x, y
    for s in [@diameter/2..@diameter] by 2
      fill @h, @s, 1, opacity / @diameter
      pushMatrix()
      rotate @attrs[s].rd * p5.frameCount
      ellipse(
        @attrs[s].rx,
        @attrs[s].ry,
        @attrs[s].sx,
        @attrs[s].sy
      )
      popMatrix()
    popMatrix()

p5      = processing
min_r   = undefined
step    = undefined
min_R   = undefined
max_R   = undefined
colors  = undefined
sun     = undefined
planets = []
safed   = 0.1
dense   = 5
opacity = 8
bg = 1

setup = ->
  colorMode HSB, 1.0
  ellipseMode CENTER
  noStroke()
  noLoop()
  mousePressed()

draw = ->
  background bg
  pushMatrix()
  translate p5.width / 2, p5.height / 2
  fill hue(sun), saturation(sun), brightness(sun), 0.01
  for R in [min_R..max_R] by 4
    ellipse random(-step, step), random(-step, step),
            R * random(0.9, 1.1), R * random(0.9, 1.1)
  p.move() for p in planets
  popMatrix()

mousePressed = ->
  size $(window).width(), $(window).height()
  max_R = max(p5.width / 2, p5.height / 2)

  planets = []
  colors = for i in [0...rint(5, 10)]
      color(random(1.0), random(0.4, 1.0), random(0.5, 1.0))
  sun = colors.sample()

  angles = (r, R, R2) ->
    theta = 2 * asin(r / (2 * R))
    phi   =
      if R + r > R2
        acos((pow(R, 2) + pow(R2, 2) - pow(r, 2)) / (2 * R * R2))
      else
        null
    [theta, phi]

  r_from_theta = (theta, R) -> 2 * R * sin(theta / 2)

  min_r = rint(4, 20)
  step  = random(min_r, 100)
  min_R = rint(step * 2, min(p5.width / 3, p5.height / 3))
  center_r = min_R - step * (1 + safed)
  planets.push new Planet(0, 0, center_r * 2, 0, colors.sample())

  ranges = {}
  for R in [min_R..max_R + step] by step
    ranges[R] = new AngularRange()

  for R in [min_R..max_R] by step
    null while abs(td = random(-0.1, 0.1)) < 0.03
    R1_5 = R + step * safed
    for t in [0...(dense * 2 * PI * R / step)]
      range = ranges[R].sample()
      break unless range?

      rad = random range[0], range[1]
      max_theta = min(rad - range[0], range[1] - rad)
      max_r = min( r_from_theta(max_theta, R), step )

      continue if max_r < min_r

      d = max_r - min_r
      r = max_r - pow(random(), 5) * d
      [theta, phi] = angles(r, R, R1_5)

      planets.push new Planet(rad, td, r * 2, R, colors.sample())

      ranges[R].consume(rad - theta, rad + theta)
      if phi
        ranges[R + step].consume(rad - phi, rad + phi)
  draw()

keyPressed = ->
  if keyCode() == ENTER || keyCode() == RETURN
    bg = (bg + 1) % 2
    draw()
» capture | close