propane 0.3.0.pre-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.mvn/extensions.xml +8 -0
- data/.mvn/wrapper/maven-wrapper.properties +1 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +69 -0
- data/Rakefile +59 -0
- data/VERSION.txt +4 -0
- data/bin/propane +8 -0
- data/examples/complete/Rakefile +32 -0
- data/examples/complete/data/Texture01.jpg +0 -0
- data/examples/complete/data/Texture02.jpg +0 -0
- data/examples/complete/data/Univers45.vlw +0 -0
- data/examples/complete/data/displaceFrag.glsl +8 -0
- data/examples/complete/data/displaceVert.glsl +201 -0
- data/examples/complete/glsl_heightmap_noise.rb +121 -0
- data/examples/complete/kinetic_type.rb +79 -0
- data/examples/regular/Rakefile +30 -0
- data/examples/regular/arcball_box.rb +36 -0
- data/examples/regular/creating_colors.rb +57 -0
- data/examples/regular/elegant_ball.rb +159 -0
- data/examples/regular/flight_patterns.rb +63 -0
- data/examples/regular/grey_circles.rb +28 -0
- data/examples/regular/jwishy.rb +100 -0
- data/examples/regular/letters.rb +42 -0
- data/examples/regular/lib/boundary.rb +38 -0
- data/examples/regular/lib/particle.rb +77 -0
- data/examples/regular/lib/particle_system.rb +111 -0
- data/examples/regular/liquidy.rb +40 -0
- data/examples/regular/mouse_button_demo.rb +34 -0
- data/examples/regular/polyhedrons.rb +248 -0
- data/examples/regular/ribbon_doodle.rb +89 -0
- data/examples/regular/vector_math.rb +36 -0
- data/examples/regular/words.rb +41 -0
- data/lib/PROCESSING_LICENSE.txt +456 -0
- data/lib/export.txt +10 -0
- data/lib/propane.rb +12 -0
- data/lib/propane/app.rb +197 -0
- data/lib/propane/helper_methods.rb +177 -0
- data/lib/propane/helpers/numeric.rb +9 -0
- data/lib/propane/library_loader.rb +117 -0
- data/lib/propane/runner.rb +88 -0
- data/lib/propane/underscorer.rb +19 -0
- data/lib/propane/version.rb +5 -0
- data/library/boids/boids.rb +201 -0
- data/library/control_panel/control_panel.rb +172 -0
- data/pom.rb +113 -0
- data/pom.xml +198 -0
- data/propane.gemspec +28 -0
- data/src/monkstone/ColorUtil.java +67 -0
- data/src/monkstone/MathTool.java +195 -0
- data/src/monkstone/PropaneLibrary.java +47 -0
- data/src/monkstone/core/AbstractLibrary.java +102 -0
- data/src/monkstone/fastmath/Deglut.java +115 -0
- data/src/monkstone/vecmath/AppRender.java +87 -0
- data/src/monkstone/vecmath/JRender.java +56 -0
- data/src/monkstone/vecmath/ShapeRender.java +87 -0
- data/src/monkstone/vecmath/vec2/Vec2.java +670 -0
- data/src/monkstone/vecmath/vec3/Vec3.java +708 -0
- data/test/respond_to_test.rb +208 -0
- data/vendors/Rakefile +48 -0
- metadata +130 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
# Noise-Based GLSL Heightmap by Amnon Owed (May 2013)
|
4
|
+
# https://github.com/AmnonOwed
|
5
|
+
# http://vimeo.com/amnon
|
6
|
+
#
|
7
|
+
# Creating a GLSL heightmap running on shader-based procedural noise.
|
8
|
+
#
|
9
|
+
# c = cycle through the color maps
|
10
|
+
#
|
11
|
+
# Tested with propane-0.3.0
|
12
|
+
#
|
13
|
+
# Photographs by Folkert Gorter (@folkertgorter / http://superfamous.com/) made available under a CC Attribution 3.0 license.
|
14
|
+
|
15
|
+
require 'propane'
|
16
|
+
|
17
|
+
class HeightMap < Propane::App
|
18
|
+
include Propane::Render
|
19
|
+
DIM = 300 # the grid dimensions of the heightmap
|
20
|
+
attr_reader :blur_factor # the blur for the displacement map
|
21
|
+
attr_reader :resize_factor # the resize factor for the displacement map
|
22
|
+
attr_reader :displace_strength
|
23
|
+
attr_reader :positions
|
24
|
+
attr_reader :tex_coords
|
25
|
+
attr_reader :height_map # PShape holds the geometry, textures etc.
|
26
|
+
attr_reader :displace # GLSL shader
|
27
|
+
attr_reader :images # array to hold 2 input images
|
28
|
+
attr_reader :color_map # variable to keep track of the current colorMap
|
29
|
+
|
30
|
+
def setup
|
31
|
+
size(1280, 720, P3D) # use the P3D OpenGL renderer
|
32
|
+
@blur_factor = 3
|
33
|
+
@resize_factor = 0.25
|
34
|
+
displace_strength = 0.25 # the displace strength of the GLSL shader displacement effect
|
35
|
+
# load the images from the _Images folder (relative path from this sketch's folder)
|
36
|
+
textures = %w(Texture01.jpg Texture02.jpg)
|
37
|
+
@images = textures.map { |texture| load_image(data_path(texture)) }
|
38
|
+
@color_map = 0
|
39
|
+
# load the PShader with a fragment and a vertex shader
|
40
|
+
@displace = load_shader('displaceFrag.glsl', 'displaceVert.glsl')
|
41
|
+
displace.set('displaceStrength', displace_strength) # set the displace_strength
|
42
|
+
displace.set('colorMap', images[color_map]) # set the initial colorMap
|
43
|
+
# create the heightmap PShape (see custom creation method) and put it in the global height_map reference
|
44
|
+
@height_map = create_plane(DIM, DIM)
|
45
|
+
end
|
46
|
+
|
47
|
+
def draw
|
48
|
+
# required for texLight shader
|
49
|
+
pointLight(255, 255, 255, 2 * (mouse_x - width / 2), 2 * (mouse_y - height / 2), 500)
|
50
|
+
translate(width / 2, height / 2) # translate to center of the screen
|
51
|
+
rotate_x(60.radians) # fixed rotation of 60 degrees over the X axis
|
52
|
+
rotate_z(frame_count * 0.005) # dynamic frameCount-based rotation over the Z axis
|
53
|
+
background(0) # black background
|
54
|
+
perspective(PI/3.0, width.to_f / height, 0.1, 1_000_000) # perspective for close shapes
|
55
|
+
scale(750) # scale by 750 (the model itself is unit length
|
56
|
+
displace.set('time', millis / 5_000.0) # feed time to the GLSL shader
|
57
|
+
shader(displace) # use shader
|
58
|
+
shape(height_map) # display the PShape
|
59
|
+
end
|
60
|
+
|
61
|
+
# custom method to create a PShape plane with certain xy DIMensions
|
62
|
+
def create_plane(xsegs, ysegs)
|
63
|
+
# STEP 1: create all the relevant data
|
64
|
+
|
65
|
+
@positions = [] # arrayList to hold positions
|
66
|
+
@tex_coords = [] # arrayList to hold texture coordinates
|
67
|
+
|
68
|
+
usegsize = 1 / xsegs.to_f # horizontal stepsize
|
69
|
+
vsegsize = 1 / ysegs.to_f # vertical stepsize
|
70
|
+
|
71
|
+
xsegs.times do |x|
|
72
|
+
ysegs.times do |y|
|
73
|
+
u = x / xsegs.to_f
|
74
|
+
v = y / ysegs.to_f
|
75
|
+
|
76
|
+
# generate positions for the vertices of each cell
|
77
|
+
# (-0.5 to center the shape around the origin)
|
78
|
+
positions << Vec3D.new(u - 0.5, v - 0.5, 0)
|
79
|
+
positions << Vec3D.new(u + usegsize - 0.5, v - 0.5, 0)
|
80
|
+
positions << Vec3D.new(u + usegsize - 0.5, v + vsegsize - 0.5, 0)
|
81
|
+
positions << Vec3D.new(u - 0.5, v + vsegsize - 0.5, 0)
|
82
|
+
|
83
|
+
# generate texture coordinates for the vertices of each cell
|
84
|
+
tex_coords << Vec2D.new(u, v)
|
85
|
+
tex_coords << Vec2D.new(u + usegsize, v)
|
86
|
+
tex_coords << Vec2D.new(u + usegsize, v + vsegsize)
|
87
|
+
tex_coords << Vec2D.new(u, v + vsegsize)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# STEP 2: put all the relevant data into the PShape
|
92
|
+
|
93
|
+
texture_mode(NORMAL) # set texture_mode to normalized (range 0 to 1)
|
94
|
+
tex = images[0]
|
95
|
+
|
96
|
+
mesh = create_shape # create the initial PShape
|
97
|
+
renderer = ShapeRender.new(mesh) # initialize the shape renderer
|
98
|
+
mesh.begin_shape(QUADS) # define the PShape type: QUADS
|
99
|
+
mesh.no_stroke
|
100
|
+
mesh.texture(tex) # set a texture to make a textured PShape
|
101
|
+
# put all the vertices, uv texture coordinates and normals into the PShape
|
102
|
+
positions.each_with_index do |p, i|
|
103
|
+
p.to_vertex_uv(renderer, tex_coords[i]) # NB: tex_coords as Vec2D
|
104
|
+
# p.to_vertex_uv(renderer, u, v) # u, v as floats is the alternate form
|
105
|
+
end
|
106
|
+
mesh.end_shape
|
107
|
+
mesh # our work is done here, return DA MESH! -)
|
108
|
+
end
|
109
|
+
|
110
|
+
def key_released
|
111
|
+
case key
|
112
|
+
when '1', '2'
|
113
|
+
@color_map = key.to_i - 1 # images.size for more than two
|
114
|
+
displace.set('colorMap', images[color_map])
|
115
|
+
else
|
116
|
+
puts format('key pressed: %s', key)
|
117
|
+
end # cycle through colorMaps (set variable and set colorMap in PShader)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
HeightMap.new title: 'Height Map'
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
# From the Processing Examples
|
4
|
+
# by Zach Lieberman
|
5
|
+
|
6
|
+
WORDS = %w(sometimes\ it's\ like the\ lines\ of\ text are\ so\ happy
|
7
|
+
that\ they\ want\ to\ dance or\ leave\ the\ page\ or\ jump
|
8
|
+
can\ you\ blame\ them? living\ on\ the\ page\ like\ that
|
9
|
+
waiting\ to\ be\ read...)
|
10
|
+
|
11
|
+
require 'propane' # temporary local
|
12
|
+
|
13
|
+
# dispay them 'words'
|
14
|
+
class KineticType < Propane::App
|
15
|
+
|
16
|
+
def setup
|
17
|
+
size(200, 200, P3D)
|
18
|
+
frame_rate 30
|
19
|
+
# Load the font from the sketch's data directory.
|
20
|
+
text_font load_font(data_path('Univers45.vlw')), 1.0
|
21
|
+
fill 255
|
22
|
+
# Creating the line objects
|
23
|
+
@lines = WORDS.map.with_index { |word, i| Line.new(self, word, 0, i * 70) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def draw
|
27
|
+
background 0
|
28
|
+
translate(-240, -120, -450)
|
29
|
+
rotate_y 0.3
|
30
|
+
# Now animate every line object & draw it...
|
31
|
+
@lines.each_with_index do |line, i|
|
32
|
+
push_matrix
|
33
|
+
translate 0.0, line.ypos, 0.0
|
34
|
+
line.draw(i)
|
35
|
+
pop_matrix
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
class Line
|
42
|
+
attr_accessor :app, :string, :xpos, :ypos, :letters
|
43
|
+
|
44
|
+
def initialize(app, string, x, y)
|
45
|
+
@app, @string, @xpos, @ypos = app, string, x, y
|
46
|
+
spacing = 0.0
|
47
|
+
@letters = string.split('').map do |c|
|
48
|
+
spacing += app.text_width(c)
|
49
|
+
Letter.new(c, spacing, 0.0)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def compute_curve(line_num)
|
54
|
+
base = app.millis / 10_000.0 * Math::PI * 2
|
55
|
+
Math.sin((line_num + 1.0) * base) * Math.sin((8.0 - line_num) * base)
|
56
|
+
end
|
57
|
+
|
58
|
+
def draw(line_num)
|
59
|
+
curve = compute_curve(line_num)
|
60
|
+
letters.each_with_index do |letter, i|
|
61
|
+
return if i < 0
|
62
|
+
app.translate(app.text_width(letters[i - 1].char) * 75, 0.0, 0.0)
|
63
|
+
app.rotate_y(curve * 0.035)
|
64
|
+
app.push_matrix
|
65
|
+
app.scale(75.0, 75.0, 75.0)
|
66
|
+
app.text(letter.char, 0.0, 0.0)
|
67
|
+
app.pop_matrix
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Letter
|
73
|
+
attr_accessor :char, :x, :y
|
74
|
+
def initialize(c, x, y)
|
75
|
+
@char, @x, @y = c, x, y
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
KineticType.new title: 'Kinetic Type'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Simple demo Rakefile to autorun samples in current directory
|
2
|
+
# adjust path to rp5 executable, and or opts as required
|
3
|
+
|
4
|
+
SAMPLES_DIR = './'
|
5
|
+
|
6
|
+
desc 'run demo'
|
7
|
+
task default: [:demo]
|
8
|
+
|
9
|
+
desc 'demo'
|
10
|
+
task :demo do
|
11
|
+
samples_list.shuffle.each { |sample| run_sample sample }
|
12
|
+
end
|
13
|
+
|
14
|
+
def samples_list
|
15
|
+
files = []
|
16
|
+
Dir.chdir(SAMPLES_DIR)
|
17
|
+
Dir.glob('*.rb').each do |file|
|
18
|
+
files << File.join(SAMPLES_DIR, file)
|
19
|
+
end
|
20
|
+
return files
|
21
|
+
end
|
22
|
+
|
23
|
+
def run_sample(sample_name)
|
24
|
+
puts "Running #{sample_name}...quit to run next sample"
|
25
|
+
open("|jruby #{sample_name}", 'r') do |io|
|
26
|
+
while l = io.gets
|
27
|
+
puts(l.chop)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'arcball'
|
2
|
+
|
3
|
+
############################
|
4
|
+
# Use mouse drag to rotate
|
5
|
+
# the arcball. Use mousewheel
|
6
|
+
# to zoom. Hold down x, y, z
|
7
|
+
# to constrain rotation axis.
|
8
|
+
############################
|
9
|
+
|
10
|
+
require_relative '../../lib/propane' # temporary local
|
11
|
+
|
12
|
+
# Include processing opengl classes that we'd like to use:
|
13
|
+
%w(PGL PGraphics3D PGraphicsOpenGL PShapeOpenGL Texture).each do |klass|
|
14
|
+
java_import "processing.opengl.#{klass}"
|
15
|
+
end
|
16
|
+
|
17
|
+
class ArcballBox < Propane::App
|
18
|
+
|
19
|
+
def setup
|
20
|
+
size(600, 600, P3D)
|
21
|
+
smooth(8)
|
22
|
+
Processing::ArcBall.init(self, 300, 300)
|
23
|
+
fill 180
|
24
|
+
end
|
25
|
+
|
26
|
+
def draw
|
27
|
+
background(50)
|
28
|
+
box(300, 300, 300)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
ArcballBox.new title: 'ArcBall Box'
|
34
|
+
|
35
|
+
|
36
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
# Creating Colors (Homage to Albers).
|
4
|
+
#
|
5
|
+
require 'propane'
|
6
|
+
|
7
|
+
# Creating variables for colors that may be referred to
|
8
|
+
# in the program by their name, rather than a number.
|
9
|
+
class CreatingColors < Propane::App
|
10
|
+
attr_reader :redder, :yellower, :orangish
|
11
|
+
|
12
|
+
def setup
|
13
|
+
size 640, 360
|
14
|
+
@redder = color 204, 102, 0
|
15
|
+
@yellower = color 204, 153, 0
|
16
|
+
@orangish = color 153, 51, 0
|
17
|
+
# These statements are equivalent to the statements above.
|
18
|
+
# Programmers may use the format they prefer.
|
19
|
+
|
20
|
+
# hex color as a String (NB quotes are required)
|
21
|
+
|
22
|
+
# @redder = color '#CC6600'
|
23
|
+
# @yellower = color '#CC9900'
|
24
|
+
# @orangish = color '#993300'
|
25
|
+
|
26
|
+
# or alternatively as a hexadecimal
|
27
|
+
|
28
|
+
# @redder = color 0xFFCC6600
|
29
|
+
# @yellower = color 0xFFCC9900
|
30
|
+
# @orangish = color 0xFF993300
|
31
|
+
end
|
32
|
+
|
33
|
+
def draw
|
34
|
+
no_stroke
|
35
|
+
background 51, 0, 0
|
36
|
+
push_matrix
|
37
|
+
translate 80, 80
|
38
|
+
fill orangish
|
39
|
+
rect 0, 0, 200, 200
|
40
|
+
fill yellower
|
41
|
+
rect 40, 60, 120, 120
|
42
|
+
fill redder
|
43
|
+
rect 60, 90, 80, 80
|
44
|
+
pop_matrix
|
45
|
+
push_matrix
|
46
|
+
translate 360, 80
|
47
|
+
fill redder
|
48
|
+
rect 0, 0, 200, 200
|
49
|
+
fill orangish
|
50
|
+
rect 40, 60, 120, 120
|
51
|
+
fill yellower
|
52
|
+
rect 60, 90, 80, 80
|
53
|
+
pop_matrix
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
CreatingColors.new title: 'Homage to Albers'
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# elegant_ball.rb
|
2
|
+
# After a vanilla processing sketch by
|
3
|
+
# Ben Notorianni aka lazydog
|
4
|
+
#
|
5
|
+
# elegant_ball.rb
|
6
|
+
|
7
|
+
require 'propane'
|
8
|
+
|
9
|
+
class ElegantBall < Propane::App
|
10
|
+
|
11
|
+
attr_reader :start_t
|
12
|
+
|
13
|
+
def setup
|
14
|
+
size(800, 800, P3D)
|
15
|
+
color_mode(RGB, 1)
|
16
|
+
end
|
17
|
+
|
18
|
+
def draw
|
19
|
+
background(0)
|
20
|
+
# Move the origin so that the scene is centered on the screen.
|
21
|
+
translate(width / 2, height / 2, 0.0)
|
22
|
+
# Set up the lighting.
|
23
|
+
setup_lights
|
24
|
+
# Rotate the local coordinate system.
|
25
|
+
smooth_rotation(5.0, 6.7, 7.3)
|
26
|
+
# Draw the inner object.
|
27
|
+
no_stroke
|
28
|
+
fill(smooth_colour(10.0, 12.0, 7.0))
|
29
|
+
draw_icosahedron(5, 60.0, false)
|
30
|
+
# Rotate the local coordinate system again.
|
31
|
+
smooth_rotation(4.5, 3.7, 7.3)
|
32
|
+
# Draw the outer object.
|
33
|
+
stroke(0.2)
|
34
|
+
fill(smooth_colour(6.0, 9.2, 0.7))
|
35
|
+
draw_icosahedron(5, 200.0, true)
|
36
|
+
end
|
37
|
+
|
38
|
+
def renderer
|
39
|
+
@renderer ||= Propane::Render::AppRender.new(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
def setup_lights
|
43
|
+
ambient_light(0.025, 0.025, 0.025)
|
44
|
+
directional_light(0.2, 0.2, 0.2, -1, -1, -1)
|
45
|
+
spot_light(1.0, 1.0, 1.0, -200, 0, 300, 1, 0, -1, Math::PI / 4, 20)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Generate a vector whose components change smoothly over time in the range
|
49
|
+
# (0..1). Each component uses a Math.sin function to map the current time in
|
50
|
+
# milliseconds in the range (0..1).A 'speed' factor is specified for each
|
51
|
+
# component.
|
52
|
+
def smooth_vector(s1, s2, s3)
|
53
|
+
mills = millis * 0.00003 ## Lazydogs factor
|
54
|
+
# mills = millis * 0.0000001 ## worked for me a bit slower!!
|
55
|
+
x = 0.5 * Math.sin(mills * s1) + 0.5
|
56
|
+
y = 0.5 * Math.sin(mills * s2) + 0.5
|
57
|
+
z = 0.5 * Math.sin(mills * s3) + 0.5
|
58
|
+
Vec3D.new(x, y, z)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Generate a colour which smoothly changes over time.
|
62
|
+
# The speed of each component is controlled by the parameters s1, s2 and s3.
|
63
|
+
def smooth_colour(s1, s2, s3)
|
64
|
+
v = smooth_vector(s1, s2, s3)
|
65
|
+
color(v.x, v.y, v.z)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Rotate the current coordinate system.
|
69
|
+
# Uses smooth_vector to smoothly animate the rotation.
|
70
|
+
def smooth_rotation(s1, s2, s3)
|
71
|
+
r1 = smooth_vector(s1, s2, s3)
|
72
|
+
rotate_x(2.0 * Math::PI * r1.x)
|
73
|
+
rotate_y(2.0 * Math::PI * r1.y)
|
74
|
+
rotate_x(2.0 * Math::PI * r1.z)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Draw an icosahedron defined by a radius r and recursive depth d.
|
78
|
+
# Geometry data will be saved into dst. If spherical is true then the
|
79
|
+
# icosahedron is projected onto the sphere with radius r.
|
80
|
+
def draw_icosahedron(depth, r, spherical)
|
81
|
+
# Calculate the vertex data for an icosahedron inscribed by a sphere radius
|
82
|
+
# 'r'. Use 4 Golden Ratio rectangles as the basis.
|
83
|
+
gr = (1.0 + Math.sqrt(5.0)) / 2.0
|
84
|
+
h = r / Math.sqrt(1.0 + gr * gr)
|
85
|
+
v = [
|
86
|
+
Vec3D.new(0, -h, h * gr),
|
87
|
+
Vec3D.new(0, -h, -h * gr),
|
88
|
+
Vec3D.new(0, h, -h * gr),
|
89
|
+
Vec3D.new(0, h, h * gr),
|
90
|
+
Vec3D.new(h, -h * gr, 0),
|
91
|
+
Vec3D.new(h, h * gr, 0),
|
92
|
+
Vec3D.new(-h, h * gr, 0),
|
93
|
+
Vec3D.new(-h, -h * gr, 0),
|
94
|
+
Vec3D.new(-h * gr, 0, h),
|
95
|
+
Vec3D.new(-h * gr, 0, -h),
|
96
|
+
Vec3D.new(h * gr, 0, -h),
|
97
|
+
Vec3D.new(h * gr, 0, h)
|
98
|
+
]
|
99
|
+
# Draw the 20 triangular faces of the icosahedron.
|
100
|
+
r = 0.0 unless spherical
|
101
|
+
begin_shape(TRIANGLES)
|
102
|
+
draw_triangle(depth, r, v[0], v[7], v[4])
|
103
|
+
draw_triangle(depth, r, v[0], v[4], v[11])
|
104
|
+
draw_triangle(depth, r, v[0], v[11], v[3])
|
105
|
+
draw_triangle(depth, r, v[0], v[3], v[8])
|
106
|
+
draw_triangle(depth, r, v[0], v[8], v[7])
|
107
|
+
draw_triangle(depth, r, v[1], v[4], v[7])
|
108
|
+
draw_triangle(depth, r, v[1], v[10], v[4])
|
109
|
+
draw_triangle(depth, r, v[10], v[11], v[4])
|
110
|
+
draw_triangle(depth, r, v[11], v[5], v[10])
|
111
|
+
draw_triangle(depth, r, v[5], v[3], v[11])
|
112
|
+
draw_triangle(depth, r, v[3], v[6], v[5])
|
113
|
+
draw_triangle(depth, r, v[6], v[8], v[3])
|
114
|
+
draw_triangle(depth, r, v[8], v[9], v[6])
|
115
|
+
draw_triangle(depth, r, v[9], v[7], v[8])
|
116
|
+
draw_triangle(depth, r, v[7], v[1], v[9])
|
117
|
+
draw_triangle(depth, r, v[2], v[1], v[9])
|
118
|
+
draw_triangle(depth, r, v[2], v[10], v[1])
|
119
|
+
draw_triangle(depth, r, v[2], v[5], v[10])
|
120
|
+
draw_triangle(depth, r, v[2], v[6], v[5])
|
121
|
+
draw_triangle(depth, r, v[2], v[9], v[6])
|
122
|
+
end_shape
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Draw a triangle either immediately or subdivide it first.
|
127
|
+
# If depth is 1 then draw the triangle otherwise subdivide first.
|
128
|
+
#
|
129
|
+
def draw_triangle(depth, r, p1, p2, p3)
|
130
|
+
if depth == 1
|
131
|
+
p1.to_vertex(renderer)
|
132
|
+
p2.to_vertex(renderer)
|
133
|
+
p3.to_vertex(renderer)
|
134
|
+
else
|
135
|
+
# Calculate the mid points of this triangle.
|
136
|
+
v1 = (p1 + p2) * 0.5
|
137
|
+
v2 = (p2 + p3) * 0.5
|
138
|
+
v3 = (p3 + p1) * 0.5
|
139
|
+
unless r == 0.0
|
140
|
+
# Project the vertices out onto the sphere with radius r.
|
141
|
+
v1.normalize!
|
142
|
+
v1 *= r
|
143
|
+
v2.normalize!
|
144
|
+
v2 *= r
|
145
|
+
v3.normalize!
|
146
|
+
v3 *= r
|
147
|
+
end
|
148
|
+
## Generate the next level of detail
|
149
|
+
depth -= 1
|
150
|
+
draw_triangle(depth, r, p1, v1, v3)
|
151
|
+
draw_triangle(depth, r, v1, p2, v2)
|
152
|
+
draw_triangle(depth, r, v2, p3, v3)
|
153
|
+
# Uncomment out the next line to include the central part of the triangle.
|
154
|
+
# draw_triangle(depth, r, v1, v2, v3)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
ElegantBall.new title: 'Elegant Ball'
|