cf3 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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