|
| 1 | +#!/usr/bin/env jruby |
| 2 | +require 'propane'# After an openprocessing sketch by C.Andrews |
| 3 | +class RecursivePentagons < Propane::App |
| 4 | + attr_reader :strut_factor, :renderer |
| 5 | + |
| 6 | + def setup |
| 7 | + sketch_title 'Recursive Pentagons' |
| 8 | + @strut_factor = 0.2 |
| 9 | + @renderer = AppRender.new self # so we can send Vec2D :to_vertex |
| 10 | + background 0 |
| 11 | + end |
| 12 | + |
| 13 | + def draw |
| 14 | + translate(width / 2, height / 2) |
| 15 | + angle = TWO_PI / 5 |
| 16 | + radius = width * 0.7 |
| 17 | + points = (0...5).map do |i| |
| 18 | + x = radius * cos(angle * i) |
| 19 | + y = radius * sin(angle * i) |
| 20 | + Vec2D.new(x, y) |
| 21 | + end |
| 22 | + fractal = PentagonFractal.new(points, 5) |
| 23 | + fractal.draw |
| 24 | + end |
| 25 | + |
| 26 | + def settings |
| 27 | + size(800, 800) |
| 28 | + end |
| 29 | +end |
| 30 | + |
| 31 | +RecursivePentagons.new |
| 32 | + |
| 33 | +# Here we include Processing::Proxy to mimic vanilla processing inner class |
| 34 | +# access. |
| 35 | +class PentagonFractal |
| 36 | + include Propane::Proxy |
| 37 | + attr_reader :points ,:branches, :level, :midpoints, :innerpoints |
| 38 | + COLOURS = %w[#ff0000 #00ff00 #00ffff #0000ff #0000ff #ffffff] |
| 39 | + |
| 40 | + def initialize(points, levels) |
| 41 | + @points = points |
| 42 | + @level = levels |
| 43 | + return if level.zero? # so called guard clause in ruby simplifies code |
| 44 | + |
| 45 | + @midpoints = (0...5).map do |i| # build an array of midpoints |
| 46 | + midpoint(points[i], points[(i + 1) % 5]) |
| 47 | + end |
| 48 | + @innerpoints = (0...5).map do |i| # build an array of inner points |
| 49 | + opposite = points[(i + 3) % 5] |
| 50 | + x = midpoints[i].x + (opposite.x - midpoints[i].x) * strut_factor |
| 51 | + y = midpoints[i].y + (opposite.y - midpoints[i].y) * strut_factor |
| 52 | + Vec2D.new(x, y) |
| 53 | + end |
| 54 | + # Create the PentagonFractal objects representing the six inner |
| 55 | + # pentagons |
| 56 | + # the shape is very regular, so we can build the ring of five |
| 57 | + @branches = (0...5).map do |i| |
| 58 | + p = [ |
| 59 | + midpoints[i], |
| 60 | + innerpoints[i], |
| 61 | + innerpoints[(i + 1) % 5], |
| 62 | + midpoints[(i + 1) % 5], |
| 63 | + points[(i + 1) % 5] |
| 64 | + ] |
| 65 | + PentagonFractal.new(p, level - 1) |
| 66 | + end |
| 67 | + # add the final innermost pentagon |
| 68 | + branches << PentagonFractal.new(innerpoints, level - 1) |
| 69 | + end |
| 70 | + # This is a simple helper function that takes in two points (as Vec2D) and |
| 71 | + # returns the midpoint between them as Vec2D. |
| 72 | + def midpoint(point1, point2) |
| 73 | + (point2 + point1) * 0.5 |
| 74 | + end |
| 75 | + |
| 76 | + def draw |
| 77 | + stroke 255 |
| 78 | + no_fill |
| 79 | + begin_shape |
| 80 | + stroke_weight 0.5 + 0.75 * level |
| 81 | + stroke color(COLOURS[level]), 100 |
| 82 | + points.each do |point| |
| 83 | + point.to_vertex(renderer) |
| 84 | + end |
| 85 | + end_shape CLOSE |
| 86 | + return if level.zero? |
| 87 | + branches.each(&:draw) |
| 88 | + end |
| 89 | +end |
0 commit comments