cf3 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ # cf3ruby
2
+
3
+ **context-free DSL for ruby-1.9 and CF3 syntax**
4
+
5
+ Very much derived from [context-free.rb][] by Jeremy Ashkenas this version is updated to be more in line with CF3 and ruby 1.9 syntax. Tested as working with last rubygems release of ruby-processing (v 1.0.11) as well as the current [version][] (v 2.1.0).
6
+ [context-free.rb]:https://github.com/jashkenas/context_free/
7
+ [version]:https://github.com/monkstone/ruby-processing/releases/
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'cf3ruby'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install cf3
21
+
22
+ ## Usage
23
+
24
+ TODO: Write usage instructions here
25
+
26
+ ## Contributing
27
+
28
+ 1. Fork it
29
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
30
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
31
+ 4. Push to the branch (`git push origin my-new-feature`)
32
+ 5. Create new Pull Request
33
+
34
+ ![Y](http://3.bp.blogspot.com/-KNBKD7lArMA/UNBayboXQFI/AAAAAAAAD7A/YAgZCewTOxQ/s400/y.png)
35
+
@@ -0,0 +1,15 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :install => :build do
4
+ sh "jruby -S gem install #{Dir.glob('*.gem').join(' ')} --no-ri --no-rdoc"
5
+ end
6
+
7
+ task :build do
8
+ sh "gem build cf3ruby.gemspec"
9
+ end
10
+
11
+ task :test do
12
+ sh "jruby -S rp5 run test/test_cf3.rb"
13
+ end
14
+
15
+ task :default => [:install]
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #####
3
+ # Extract samples into users home directory
4
+ #####
5
+
6
+
7
+ home = ENV["HOME"]
8
+ local = home + '/cf3samples'
9
+ file = __FILE__
10
+ require "pathname"
11
+ require "fileutils"
12
+ file = Pathname.new(file).realpath
13
+
14
+ sample_dir = File.expand_path(File.dirname(file) + "/../samples")
15
+ FileUtils.mkdir local unless Dir.exists? local
16
+ FileUtils.cp_r sample_dir, local
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cf3/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cf3"
8
+ spec.version = Cf3::VERSION
9
+ spec.authors = ["Jeremy Ashkenas", "Martin Prout"]
10
+ spec.email = ["martin_p@lineone.net"]
11
+ spec.description = <<-EOF
12
+ A library for ruby-processing, that allows the writing of context free
13
+ sketches (like context free art) in a ruby DSL. It is a bit of a toy
14
+ compared to the c++ version. However you can get quite a bit of
15
+ satisfaction creating an interesting graphic, and you can't always
16
+ predict what you are going to get.
17
+ EOF
18
+ spec.summary = %q{A ruby-DSL library for CF3 sketches}
19
+ spec.homepage = "http://learning-ruby-processing.blogspot.co.uk/"
20
+ spec.default_executable = "cf3samples"
21
+ spec.license = "GPL3"
22
+ spec.files = `git ls-files`.split($/)
23
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
24
+ spec.executables = ["cf3samples"]
25
+ spec.require_paths = ["lib"]
26
+ spec.add_runtime_dependency 'ruby-processing', '>= 1.0.11'
27
+ spec.add_development_dependency "bundler", "~> 1.3"
28
+ spec.add_development_dependency "rake"
29
+ end
@@ -0,0 +1,240 @@
1
+ # A Context-Free library for Ruby-Processing, inspired by
2
+ # based on context_free.rb by Jeremy Ashkenas. Which in turn
3
+ # was inspired by contextfreeart.org
4
+
5
+ module Processing
6
+
7
+ class ContextFree
8
+
9
+ include Processing::Proxy
10
+
11
+ attr_accessor :rule, :app, :width, :height
12
+
13
+ AVAILABLE_OPTIONS = [:x, :y, :rotation, :size, :flip, :color, :hue, :saturation, :brightness, :alpha]
14
+ HSB_ORDER = {hue: 0, saturation: 1, brightness: 2, alpha: 3}
15
+ TRIANGLE_TOP = -1 / Math.sqrt(3)
16
+ TRIANGLE_BOTTOM = Math.sqrt(3) / 6
17
+
18
+ # Define a context-free system. Use this method to create a ContextFree
19
+ # object. Call render() on it to make it draw.
20
+ def self.define(&block)
21
+ cf = ContextFree.new
22
+ cf.instance_eval &block
23
+ cf
24
+ end
25
+
26
+
27
+ # Initialize a bare ContextFree object with empty recursion stacks.
28
+ def initialize
29
+ @app = $app
30
+ @graphics = $app.g
31
+ @width = $app.width
32
+ @height = $app.height
33
+ @finished = false
34
+ @rules = {}
35
+ @rewind_stack = []
36
+ @matrix_stack = []
37
+ end
38
+
39
+
40
+ # Create an accessor for the current value of every option. We use a values
41
+ # object so that all the state can be saved and restored as a unit.
42
+ AVAILABLE_OPTIONS.each do |option_name|
43
+ define_method option_name do
44
+ @values[option_name]
45
+ end
46
+ end
47
+
48
+
49
+ # Here's the first serious method: A Rule has an
50
+ # identifying name, a probability, and is associated with
51
+ # a block of code. These code blocks are saved, and indexed
52
+ # by name in a hash, to be run later, when needed.
53
+ # The method then dynamically defines a method of the same
54
+ # name here, in order to determine which rule to run.
55
+ def shape(rule_name, prob=1, &proc)
56
+ @rules[rule_name] ||= {procs: [], total: 0}
57
+ total = @rules[rule_name][:total]
58
+ @rules[rule_name][:procs] << [(total...(prob+total)), proc]
59
+ @rules[rule_name][:total] += prob
60
+ unless ContextFree.method_defined? rule_name
61
+ self.class.class_eval do
62
+ eval <<-METH
63
+ def #{rule_name}(options)
64
+ merge_options(@values, options)
65
+ pick = determine_rule(#{rule_name.inspect})
66
+ @finished = true if @values[:size] < @values[:stop_size]
67
+ unless @finished
68
+ get_ready_to_draw
69
+ pick[1].call(options)
70
+ end
71
+ end
72
+ METH
73
+ end
74
+ end
75
+ end
76
+
77
+
78
+ # Rule choice is random, based on the assigned probabilities.
79
+ def determine_rule(rule_name)
80
+ rule = @rules[rule_name]
81
+ chance = rand * rule[:total]
82
+ pick = @rules[rule_name][:procs].select {|the_proc| the_proc[0].include?(chance) }
83
+ return pick.flatten
84
+ end
85
+
86
+
87
+ # At each step of the way, any of the options may change, slightly.
88
+ # Many of them have different strategies for being merged.
89
+ def merge_options(old_ops, new_ops)
90
+ return unless new_ops
91
+ # Do size first
92
+ old_ops[:size] *= new_ops[:size] if new_ops[:size]
93
+ new_ops.each do |key, value|
94
+ case key
95
+ when :size
96
+ when :x, :y
97
+ old_ops[key] = value * old_ops[:size]
98
+ when :rotation
99
+ old_ops[key] = value * (Math::PI / 180.0)
100
+ when :hue, :saturation, :brightness, :alpha
101
+ adjusted = old_ops[:color].dup
102
+ adjusted[HSB_ORDER[key]] *= value
103
+ old_ops[:color] = adjusted
104
+ when :flip
105
+ old_ops[key] = !old_ops[key]
106
+ when :width, :height
107
+ old_ops[key] *= value
108
+ when :color
109
+ old_ops[key] = value
110
+ else # Used a key that we don't know about or trying to set
111
+ merge_unknown_key(key, value, old_ops)
112
+ end
113
+ end
114
+ end
115
+
116
+
117
+ # Using an unknown key let's you set arbitrary values,
118
+ # to keep track of for your own ends.
119
+ def merge_unknown_key(key, value, old_ops)
120
+ key_s = key.to_s
121
+ if key_s.match(/^set/)
122
+ key_sym = key_s.sub('set_', '').to_sym
123
+ if key_s.match(/(brightness|hue|saturation)/)
124
+ adjusted = old_ops[:color].dup
125
+ adjusted[HSB_ORDER[key_sym]] = value
126
+ old_ops[:color] = adjusted
127
+ else
128
+ old_ops[key_sym] = value
129
+ end
130
+ end
131
+ end
132
+
133
+
134
+ # Doing a 'split' saves the context, and proceeds from there,
135
+ # allowing you to rewind to where you split from at any moment.
136
+ def split(options=nil, &block)
137
+ save_context
138
+ merge_options(@values, options) if options
139
+ yield
140
+ restore_context
141
+ end
142
+
143
+
144
+ # Saving the context means the values plus the coordinate matrix.
145
+ def save_context
146
+ @rewind_stack.push @values.dup
147
+ @matrix_stack << @graphics.get_matrix
148
+ end
149
+
150
+
151
+ # Restore the values and the coordinate matrix as the recursion unwinds.
152
+ def restore_context
153
+ @values = @rewind_stack.pop
154
+ @graphics.set_matrix @matrix_stack.pop
155
+ end
156
+
157
+
158
+ # Rewinding goes back one step.
159
+ def rewind
160
+ @finished = false
161
+ restore_context
162
+ save_context
163
+ end
164
+
165
+
166
+ # Render the is method that kicks it all off, initializing the options
167
+ # and calling the first rule.
168
+ def render(rule_name, starting_values={})
169
+ @values = {x: 0, y: 0,
170
+ rotation: 0, flip: false,
171
+ size: 20, width: 20, height: 20,
172
+ start_x: width/2, start_y: height/2,
173
+ color: [0.5, 0.5, 0.5, 1],
174
+ stop_size: 1.5}
175
+ @values.merge!(starting_values)
176
+ @finished = false
177
+ @app.reset_matrix
178
+ @app.rect_mode CENTER
179
+ @app.ellipse_mode CENTER
180
+ @app.no_stroke
181
+ @app.color_mode HSB, 1.0
182
+ @app.translate @values[:start_x], @values[:start_y]
183
+ self.send(rule_name, {})
184
+ end
185
+
186
+
187
+ # Before actually drawing the next step, we need to move to the appropriate
188
+ # location.
189
+ def get_ready_to_draw
190
+ @app.translate(@values[:x], @values[:y])
191
+ sign = (@values[:flip] ? -1 : 1)
192
+ @app.rotate(sign * @values[:rotation])
193
+ end
194
+
195
+
196
+ # Compute the rendering parameters for drawing a shape.
197
+ def get_shape_values(some_options)
198
+ old_ops = @values.dup
199
+ merge_options(old_ops, some_options) if some_options
200
+ @app.fill *old_ops[:color]
201
+ return old_ops[:size], old_ops
202
+ end
203
+
204
+
205
+ # Square, circle, and ellipse are the primitive drawing
206
+ # methods, but hopefully triangles will be added soon.
207
+ def square(some_options=nil)
208
+ size, options = *get_shape_values(some_options)
209
+ @app.rect(0, 0, size, size)
210
+ end
211
+
212
+
213
+ def circle(some_options=nil)
214
+ size, options = *get_shape_values(some_options)
215
+ @app.ellipse(0, 0, size, size)
216
+ end
217
+
218
+ def triangle(some_options=nil)
219
+ rot = some_options[:rotation]
220
+ @app.rotate(rot) if rot
221
+ size, options = *get_shape_values(some_options)
222
+ @app.triangle(0, TRIANGLE_TOP * size, 0.5 * size, TRIANGLE_BOTTOM * size, -0.5 * size, TRIANGLE_BOTTOM * size)
223
+ @app.rotate(-rot) if rot
224
+ end
225
+
226
+
227
+ def ellipse(some_options={})
228
+ rot = some_options[:rotation]
229
+ @app.rotate(rot) if rot
230
+ size, options = *get_shape_values(some_options)
231
+ width = options[:width] || options[:size]
232
+ height = options[:height] || options[:size]
233
+ @app.oval(options[:x] || 0, options[:y] || 0, width, height)
234
+ @app.rotate(-rot) if rot
235
+ end
236
+ alias_method :oval, :ellipse
237
+
238
+ end
239
+
240
+ end
@@ -0,0 +1,3 @@
1
+ module Cf3
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,98 @@
1
+ #########################
2
+ # rubystar.rb
3
+ #########################
4
+ require 'cf3'
5
+
6
+ PHI = (1 + Math.sqrt(5)) / 2
7
+
8
+ def setup_the_sunstar
9
+ @hexa = ContextFree.define do
10
+ ############ Begin defining custom terminals, as a sharp and flat triangles
11
+ class << self
12
+ include Math
13
+ define_method(:sharp) do |some_options|
14
+ size, options = *self.get_shape_values(some_options)
15
+ rot = options[:rotation]
16
+ #f = (options[:flip])? -1 : 1
17
+ @app.rotate(rot) if rot
18
+ #@app.triangle(0, 0, size * cos(0), size * sin(0), size * cos(PI * f/5), size * sin(PI*f/5))
19
+ @app.triangle(0, 0, size * cos(0), size * sin(0), size * cos(PI/5), size * sin(PI/5))
20
+ @app.rotate(-rot) if rot
21
+ end
22
+ define_method(:flat) do |some_options|
23
+ size, options = *self.get_shape_values(some_options)
24
+ rot = options[:rotation]
25
+ f = (options[:flip])? -1 : 1 # NB custom flip adjustment
26
+ @app.triangle(0, 0, size * cos(0), size * sin(0), size/PHI * cos(PI * f/5 ), size/PHI * sin(PI * f/5))
27
+ @app.rotate(-rot) if rot
28
+ end
29
+ end
30
+ ########### End definition of custom terminals 'sharp and flat'
31
+
32
+ shape :tiling do
33
+ outer brightness: 1.0
34
+ end
35
+
36
+
37
+ shape :outer do
38
+ split do
39
+ 10.times do
40
+ sunstar y: sqrt((PHI)** - 0.125), rotation: 36
41
+ end
42
+ rewind
43
+ end
44
+ outer size: 1/PHI
45
+ end
46
+
47
+
48
+ shape :sunstar do
49
+ split do
50
+ sun brightness: 0.8
51
+ rewind
52
+ star brightness: 0.8, alpha: 0.8
53
+ rewind
54
+ end
55
+ end
56
+
57
+ shape :dart do
58
+ flat size: 1, color: [0.18, 0.6, 0.6]
59
+ flat size: 1, color: [0.18, 1.0, 1.0], flip: true
60
+ end
61
+
62
+ shape :kite do
63
+ sharp size: 1, color: [0, 0.6, 0.6]
64
+ sharp size: 1, color: [0, 1.0, 1.0], rotation: 180, flip: true
65
+ end
66
+
67
+ shape :star do
68
+ split do
69
+ 5.times do |i|
70
+ dart rotation: 72 * i
71
+ rewind
72
+ end
73
+ end
74
+ end
75
+
76
+ shape :sun do
77
+ split do
78
+ 5.times do |i|
79
+ kite rotation: 72 * i
80
+ rewind
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ def setup
88
+ size 800, 820
89
+ background 150, 20, 0
90
+ smooth
91
+ setup_the_sunstar
92
+ draw_it
93
+ end
94
+
95
+ def draw_it
96
+ @hexa.render :tiling, start_x: width * 0.75, start_y: height/2.64,
97
+ size: height/6
98
+ end
@@ -0,0 +1,79 @@
1
+ require 'cf3'
2
+
3
+ def setup_the_tiles
4
+ @tiles= ContextFree.define do
5
+ ############ Begin defining custom terminal, an wavy_triangle triangle
6
+ class << self
7
+ define_method(:wavy_triangle) do |some_options| # wavy_triangle triangle
8
+ size, options = *self.get_shape_values(some_options)
9
+ rot = options[:rotation]
10
+ disp = 0.32 # could introduce a rule option?
11
+ x0 = options[:x]
12
+ y0 = options[:y]
13
+ pts = Array.new(12)
14
+ pts[0] = PVector.new(x0, y0 - size/Math.sqrt(3)) # A
15
+ pts[1] = PVector.new(x0 - 0.5 * size, y0 + (Math.sqrt(3)*size)/6) # B
16
+ pts[2] = PVector.new(x0 + 0.5 * size, y0 + (Math.sqrt(3)*size)/6) # C
17
+ pts[3] = get_mid_point(pts[0], pts[1]) # Ab
18
+ pts[4] = get_mid_point(pts[1], pts[2]) # Bc
19
+ pts[5] = get_mid_point(pts[0], pts[2]) # Ca
20
+ pts[6] = get_mid_point(pts[0], pts[3]) # Aba
21
+ adjust_bezier(pts[6], PI/3, disp*size) # Aba
22
+ pts[7] = get_mid_point(pts[3], pts[1]) # Abb
23
+ adjust_bezier(pts[7], PI/3, -disp*size) # Abb
24
+ pts[8] = get_mid_point(pts[1], pts[4])
25
+ adjust_bezier(pts[8], PI/2, -disp*size)
26
+ pts[9] = get_mid_point(pts[4], pts[2])
27
+ adjust_bezier(pts[9], PI/2, disp*size)
28
+ pts[10] = get_mid_point(pts[2], pts[5])
29
+ adjust_bezier(pts[10], -PI/3, -disp*size)
30
+ pts[11] = get_mid_point(pts[5], pts[0])
31
+ adjust_bezier(pts[11], -PI/3, disp*size)
32
+ rotate(rot) if rot
33
+ begin_shape
34
+ vertex(pts[0].x, pts[0].y)
35
+ bezier_vertex(pts[0].x, pts[0].y, pts[6].x, pts[6].y, pts[3].x, pts[3].y)
36
+ bezier_vertex(pts[3].x, pts[3].y, pts[7].x, pts[7].y, pts[1].x, pts[1].y)
37
+ bezier_vertex(pts[1].x, pts[1].y, pts[8].x, pts[8].y, pts[4].x, pts[4].y)
38
+ bezier_vertex(pts[4].x, pts[4].y, pts[9].x, pts[9].y, pts[2].x, pts[2].y)
39
+ bezier_vertex(pts[2].x, pts[2].y, pts[10].x, pts[10].y, pts[5].x, pts[5].y)
40
+ bezier_vertex(pts[5].x, pts[5].y, pts[11].x, pts[11].y, pts[0].x, pts[0].y)
41
+ end_shape(CLOSE)
42
+ rotate(-rot) if rot
43
+ end
44
+
45
+ private
46
+ def adjust_bezier(base, theta, disp)
47
+ base.add(PVector.new(Math.cos(theta)*disp, Math.sin(theta)*disp))
48
+ end
49
+
50
+ def get_mid_point(a, b)
51
+ mid = PVector.add(a, b)
52
+ mid.div(2)
53
+ return mid
54
+ end
55
+ end
56
+
57
+ ########### End definition of custom terminal 'wavy_triangle' shape
58
+ shape :tiles do
59
+ 10.times do |i|
60
+ 10.times do |j|
61
+ wavy_triangle size: 1, x: (i + 0.5 * j%2), y: j * 0.9
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ def setup
69
+ size 400, 400
70
+ background 255
71
+ smooth
72
+ setup_the_tiles
73
+ draw_it
74
+ end
75
+
76
+ def draw_it
77
+ @tiles.render :tiles, start_x: -50, start_y: 20,
78
+ size: height/6, color: [0, 0.8, 0.8, 1]
79
+ end