|
| 1 | +# After an openprocessing sketch by C.Andrews |
| 2 | +load_library :color_group |
| 3 | +attr_reader :strut_factor, :renderer, :cols |
| 4 | +PALETTE = %w[#fff0a5 #ffd500 #594a00 #9999ff #000059].freeze |
| 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 | + group = ColorGroup.from_web_array(PALETTE.to_java(:string)) |
| 11 | + @cols = group.colors |
| 12 | + background(0) |
| 13 | + no_loop |
| 14 | +end |
| 15 | + |
| 16 | +def draw |
| 17 | + translate(width / 2, height / 2) |
| 18 | + angle = TWO_PI / 5 |
| 19 | + radius = width / 2 |
| 20 | + points = (0...5).map do |i| |
| 21 | + x = radius * cos(angle * i) |
| 22 | + y = radius * sin(angle * i) |
| 23 | + Vec2D.new(x, y) |
| 24 | + end |
| 25 | + fractal = Pentagon.new(points, 4) |
| 26 | + fractal.draw |
| 27 | + save_frame(data_path('sutcliffe.png')) |
| 28 | +end |
| 29 | + |
| 30 | +def settings |
| 31 | + size(800, 800) |
| 32 | +end |
| 33 | + |
| 34 | +# Here we include Processing::Proxy to mimic vanilla processing inner class |
| 35 | +# access. |
| 36 | +class Pentagon |
| 37 | + include Processing::Proxy |
| 38 | + attr_reader :points ,:branches, :level, :midpoints, :innerpoints |
| 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 | + # find the midpoints on each edge |
| 45 | + @midpoints = (0...5).map do |i| |
| 46 | + midpoint(points[i], points[(i + 1) % 5]) |
| 47 | + end |
| 48 | + # find the inner points |
| 49 | + @innerpoints = (0...5).map do |i| |
| 50 | + opposite = points[(i + 3) % 5] |
| 51 | + x = midpoints[i].x + (opposite.x - midpoints[i].x) * strut_factor |
| 52 | + y = midpoints[i].y + (opposite.y - midpoints[i].y) * strut_factor |
| 53 | + Vec2D.new(x, y) |
| 54 | + end |
| 55 | + # Create the Pentagon objects representing the six inner |
| 56 | + # pentagons |
| 57 | + # the shape is very regular, so we can build the ring of five |
| 58 | + @branches = (0...5).map do |i| |
| 59 | + p = [ |
| 60 | + midpoints[i], |
| 61 | + innerpoints[i], |
| 62 | + innerpoints[(i + 1) % 5], |
| 63 | + midpoints[(i + 1) % 5], |
| 64 | + points[(i + 1) % 5] |
| 65 | + ] |
| 66 | + Pentagon.new(p, level - 1) |
| 67 | + end |
| 68 | + # add the final innermost pentagon |
| 69 | + branches << Pentagon.new(innerpoints, level - 1) |
| 70 | + end |
| 71 | + # This is a simple helper function that takes in two points (as Vec2D) and |
| 72 | + # returns the midpoint between them as Vec2D. |
| 73 | + def midpoint(point1, point2) |
| 74 | + (point2 + point1) * 0.5 |
| 75 | + end |
| 76 | + # This draws the fractal. If this is on level 0, we just draw the |
| 77 | + # pentagon formed by the points. When not level 0, iterate through the |
| 78 | + # six branches and tell them to draw themselves. |
| 79 | + def draw |
| 80 | + no_fill |
| 81 | + begin_shape |
| 82 | + stroke_weight level |
| 83 | + stroke cols[level] |
| 84 | + points.each do |point| |
| 85 | + point.to_vertex(renderer) |
| 86 | + end |
| 87 | + end_shape CLOSE |
| 88 | + return if level.zero? |
| 89 | + |
| 90 | + branches.each(&:draw) |
| 91 | + end |
| 92 | +end |
0 commit comments