Skip to content

Commit bac4573

Browse files
committed
Mycelium Box
1 parent d2e23a1 commit bac4573

File tree

3 files changed

+148
-0
lines changed

3 files changed

+148
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
require 'forwardable'
2+
require_relative './rotator'
3+
4+
class Branch
5+
include Enumerable
6+
extend Forwardable
7+
def_delegators(:@children, :<<, :each, :length)
8+
# variance angle for growth direction per time step
9+
THETA = Math::PI / 6
10+
# max segments per branch
11+
MAX_LEN = 100
12+
# max recursion limit
13+
MAX_GEN = 3
14+
# branch chance per time step
15+
BRANCH_CHANCE = 0.05
16+
# branch angle variance
17+
BRANCH_THETA = Math::PI / 4
18+
attr_reader :position, :dir, :path, :children, :xbound, :speed, :ybound, :app, :zbound
19+
20+
def initialize(app, pos, dir, speed)
21+
@app = app
22+
@position = pos
23+
@dir = dir
24+
@speed = speed
25+
@path = []
26+
@children = []
27+
@xbound = Boundary.new(-600, 600)
28+
@ybound = Boundary.new(-150, 150)
29+
@zbound = Boundary.new(-150, 150)
30+
path << pos
31+
end
32+
33+
def run
34+
grow
35+
display
36+
end
37+
38+
private
39+
40+
def grow
41+
check_bounds(position + (dir * speed))
42+
@position += (dir * speed)
43+
Rotate.rotate_x!(dir, rand(-0.5..0.5) * THETA)
44+
Rotate.rotate_y!(dir, rand(-0.5..0.5) * THETA)
45+
Rotate.rotate_z!(dir, rand(-0.5..0.5) * THETA)
46+
path << position
47+
if (length < MAX_GEN) && (rand < BRANCH_CHANCE)
48+
branch_dir = dir.copy
49+
Rotate.rotate_x!(branch_dir, rand(-0.5..0.5) * BRANCH_THETA)
50+
Rotate.rotate_y!(branch_dir, rand(-0.5..0.5) * BRANCH_THETA)
51+
Rotate.rotate_z!(branch_dir, rand(-0.5..0.5) * BRANCH_THETA)
52+
self << Branch.new(app, position.copy, branch_dir, speed * 0.99)
53+
end
54+
each(&:grow)
55+
end
56+
57+
def display
58+
app.begin_shape
59+
app.stroke 255
60+
app.stroke_weight 0.5
61+
app.no_fill
62+
path.each { |vec| vec.to_vertex(app.renderer) }
63+
app.end_shape
64+
each(&:display)
65+
end
66+
67+
def check_bounds(pos)
68+
dir.x *= -1 if xbound.exclude? pos.x
69+
dir.y *= -1 if ybound.exclude? pos.y
70+
dir.z *= -1 if zbound.exclude? pos.z
71+
end
72+
end
73+
74+
# we are looking for excluded values
75+
Boundary = Struct.new(:lower, :upper) do
76+
def exclude?(val)
77+
true unless (lower...upper).cover? val
78+
end
79+
end
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Rotator module is required because rotate is not implemented for JRubyArt 3D
2+
# NB: we use quaternions in ArcBall (avoiding possible gimbal lock)
3+
module Rotate
4+
def self.rotate_x!(vec, theta)
5+
co = Math.cos(theta)
6+
si = Math.sin(theta)
7+
zz = co * vec.z - si * vec.y
8+
vec.y = si * vec.z + co * vec.y
9+
vec.z = zz
10+
vec
11+
end
12+
13+
def self.rotate_y!(vec, theta)
14+
co = Math.cos(theta)
15+
si = Math.sin(theta)
16+
xx = co * vec.x - si * vec.z
17+
vec.z = si * vec.x + co * vec.z
18+
vec.x = xx
19+
vec
20+
end
21+
22+
def self.rotate_z!(vec, theta)
23+
co = Math.cos(theta)
24+
si = Math.sin(theta)
25+
xx = co * vec.x - si * vec.y
26+
vec.y = si * vec.x + co * vec.y
27+
vec.x = xx
28+
vec
29+
end
30+
end

contributed/mycelium_box.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env jruby
2+
require 'picrate'
3+
require 'arcball'
4+
# Simple recursive branching system inspired by mycelium growth
5+
# After an original by Karsten Schmidt
6+
# The vanilla processing sketch was part of the SAC 2013 workshop project
7+
# translated to PiCrate by Martin Prout 2018
8+
class MyceliumBox < Processing::App
9+
load_library :branch3D
10+
11+
attr_reader :renderer, :root
12+
13+
def setup
14+
sketch_title 'Mycelium Box'
15+
Processing::ArcBall.init self
16+
@renderer = AppRender.new(self)
17+
@root = Branch.new(
18+
self,
19+
Vec3D.new(0, 10, 10),
20+
Vec3D.new(1, 0, 0),
21+
6.0
22+
)
23+
end
24+
25+
def draw
26+
background(0)
27+
no_fill
28+
stroke_weight 2
29+
stroke(200, 0, 0, 100)
30+
box(1200, 300, 300)
31+
root.run
32+
end
33+
34+
def settings
35+
size(1280, 600, P3D)
36+
end
37+
end
38+
39+
MyceliumBox.new

0 commit comments

Comments
 (0)