Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 238 lines (196 sloc) 7.258 kB
06eec38 @jashkenas commenting the library
authored
1 # A Context-Free library for Ruby-Processing, inspired by
2 # contextfreeart.org
5d734f7 @jashkenas first commit of the split-out context_free library
authored
3
4 module Processing
ed79110 @jashkenas merging monkstone and whitespace
authored
5
5d734f7 @jashkenas first commit of the split-out context_free library
authored
6 class ContextFree
ed79110 @jashkenas merging monkstone and whitespace
authored
7
9f822bd @jashkenas replaced map with vine
authored
8 include Processing::Proxy
ed79110 @jashkenas merging monkstone and whitespace
authored
9
5d734f7 @jashkenas first commit of the split-out context_free library
authored
10 attr_accessor :rules, :app
ed79110 @jashkenas merging monkstone and whitespace
authored
11
4c94f7f Committer: Martin Prout <martin_p@lineone.net>
Martin Prout authored
12 AVAILABLE_OPTIONS = [:x, :y, :rotation, :size, :flip, :color, :hue, :saturation, :brightness, :alpha]
13 HSB_ORDER = {:hue => 0, :saturation => 1, :brightness => 2, :alpha => 3}
eda84af @jashkenas fixing triangles to the unit circle, thanks to monkstone's new consta…
authored
14 TRIANGLE_TOP = -1 / Math.sqrt(3)
15 TRIANGLE_BOTTOM = Math.sqrt(3) / 6
ed79110 @jashkenas merging monkstone and whitespace
authored
16
9f822bd @jashkenas replaced map with vine
authored
17 # Define a context-free system. Use this method to create a ContextFree
18 # object. Call render() on it to make it draw.
19 def self.define(&block)
20 cf = ContextFree.new
21 cf.instance_eval &block
22 cf
23 end
ed79110 @jashkenas merging monkstone and whitespace
authored
24
25
06eec38 @jashkenas commenting the library
authored
26 # Initialize a bare ContextFree object with empty recursion stacks.
162db0a @jashkenas cleaning up context saving and restoring, but for Java2D only. P2D is…
authored
27 def initialize
28 @app = $app
29 @graphics = $app.g
30 @finished = false
31 @rules = {}
5d734f7 @jashkenas first commit of the split-out context_free library
authored
32 @rewind_stack = []
33 @matrix_stack = []
34 end
ed79110 @jashkenas merging monkstone and whitespace
authored
35
36
06eec38 @jashkenas commenting the library
authored
37 # Create an accessor for the current value of every option. We use a values
38 # object so that all the state can be saved and restored as a unit.
5d734f7 @jashkenas first commit of the split-out context_free library
authored
39 AVAILABLE_OPTIONS.each do |option_name|
40 define_method option_name do
41 @values[option_name]
42 end
43 end
ed79110 @jashkenas merging monkstone and whitespace
authored
44
45
46 # Here's the first serious method: A Rule has an
47 # identifying name, a probability, and is associated with
48 # a block of code. These code blocks are saved, and indexed
5d734f7 @jashkenas first commit of the split-out context_free library
authored
49 # by name in a hash, to be run later, when needed.
ed79110 @jashkenas merging monkstone and whitespace
authored
50 # The method then dynamically defines a method of the same
5d734f7 @jashkenas first commit of the split-out context_free library
authored
51 # name here, in order to determine which rule to run.
52 def rule(rule_name, prob=1, &proc)
53 @rules[rule_name] ||= {:procs => [], :total => 0}
54 total = @rules[rule_name][:total]
55 @rules[rule_name][:procs] << [(total...(prob+total)), proc]
56 @rules[rule_name][:total] += prob
57 unless ContextFree.method_defined? rule_name
58 self.class.class_eval do
59 eval <<-METH
60 def #{rule_name}(options)
61 merge_options(@values, options)
62 pick = determine_rule(#{rule_name.inspect})
9f822bd @jashkenas replaced map with vine
authored
63 @finished = true if @values[:size] < @values[:stop_size]
5d734f7 @jashkenas first commit of the split-out context_free library
authored
64 unless @finished
65 get_ready_to_draw
66 pick[1].call(options)
67 end
68 end
69 METH
70 end
71 end
72 end
ed79110 @jashkenas merging monkstone and whitespace
authored
73
74
5d734f7 @jashkenas first commit of the split-out context_free library
authored
75 # Rule choice is random, based on the assigned probabilities.
76 def determine_rule(rule_name)
77 rule = @rules[rule_name]
78 chance = rand * rule[:total]
79 pick = @rules[rule_name][:procs].select {|the_proc| the_proc[0].include?(chance) }
80 return pick.flatten
81 end
ed79110 @jashkenas merging monkstone and whitespace
authored
82
83
5d734f7 @jashkenas first commit of the split-out context_free library
authored
84 # At each step of the way, any of the options may change, slightly.
85 # Many of them have different strategies for being merged.
86 def merge_options(old_ops, new_ops)
87 return unless new_ops
88 # Do size first
89 old_ops[:size] *= new_ops[:size] if new_ops[:size]
90 new_ops.each do |key, value|
91 case key
92 when :size
93 when :x, :y
94 old_ops[key] = value * old_ops[:size]
95 when :rotation
96 old_ops[key] = value * (Math::PI / 180.0)
4c94f7f Committer: Martin Prout <martin_p@lineone.net>
Martin Prout authored
97 when :hue, :saturation, :brightness, :alpha
5d734f7 @jashkenas first commit of the split-out context_free library
authored
98 adjusted = old_ops[:color].dup
99 adjusted[HSB_ORDER[key]] *= value
100 old_ops[:color] = adjusted
101 when :flip
102 old_ops[key] = !old_ops[key]
103 when :width, :height
104 old_ops[key] *= value
105 when :color
106 old_ops[key] = value
107 else # Used a key that we don't know about or trying to set
108 merge_unknown_key(key, value, old_ops)
109 end
110 end
111 end
ed79110 @jashkenas merging monkstone and whitespace
authored
112
113
114 # Using an unknown key let's you set arbitrary values,
5d734f7 @jashkenas first commit of the split-out context_free library
authored
115 # to keep track of for your own ends.
116 def merge_unknown_key(key, value, old_ops)
117 key_s = key.to_s
118 if key_s.match(/^set/)
119 key_sym = key_s.sub('set_', '').to_sym
120 if key_s.match(/(brightness|hue|saturation)/)
121 adjusted = old_ops[:color].dup
122 adjusted[HSB_ORDER[key_sym]] = value
123 old_ops[:color] = adjusted
124 else
125 old_ops[key_sym] = value
126 end
127 end
128 end
ed79110 @jashkenas merging monkstone and whitespace
authored
129
130
131 # Doing a 'split' saves the context, and proceeds from there,
5d734f7 @jashkenas first commit of the split-out context_free library
authored
132 # allowing you to rewind to where you split from at any moment.
133 def split(options=nil, &block)
134 save_context
135 merge_options(@values, options) if options
136 yield
137 restore_context
138 end
ed79110 @jashkenas merging monkstone and whitespace
authored
139
140
141 # Saving the context means the values plus the coordinate matrix.
5d734f7 @jashkenas first commit of the split-out context_free library
authored
142 def save_context
143 @rewind_stack.push @values.dup
162db0a @jashkenas cleaning up context saving and restoring, but for Java2D only. P2D is…
authored
144 @matrix_stack << @graphics.get_matrix
5d734f7 @jashkenas first commit of the split-out context_free library
authored
145 end
ed79110 @jashkenas merging monkstone and whitespace
authored
146
147
06eec38 @jashkenas commenting the library
authored
148 # Restore the values and the coordinate matrix as the recursion unwinds.
5d734f7 @jashkenas first commit of the split-out context_free library
authored
149 def restore_context
ed79110 @jashkenas merging monkstone and whitespace
authored
150 @values = @rewind_stack.pop
162db0a @jashkenas cleaning up context saving and restoring, but for Java2D only. P2D is…
authored
151 @graphics.set_matrix @matrix_stack.pop
5d734f7 @jashkenas first commit of the split-out context_free library
authored
152 end
ed79110 @jashkenas merging monkstone and whitespace
authored
153
154
06eec38 @jashkenas commenting the library
authored
155 # Rewinding goes back one step.
5d734f7 @jashkenas first commit of the split-out context_free library
authored
156 def rewind
157 @finished = false
158 restore_context
159 save_context
160 end
ed79110 @jashkenas merging monkstone and whitespace
authored
161
162
163 # Render the is method that kicks it all off, initializing the options
5d734f7 @jashkenas first commit of the split-out context_free library
authored
164 # and calling the first rule.
9f822bd @jashkenas replaced map with vine
authored
165 def render(rule_name, starting_values={})
ed79110 @jashkenas merging monkstone and whitespace
authored
166 @values = {:x => 0, :y => 0,
167 :rotation => 0, :flip => false,
5d734f7 @jashkenas first commit of the split-out context_free library
authored
168 :size => 20, :width => 20, :height => 20,
3045a86 @jashkenas merging in monkstone's triangle patch and additional samples
authored
169 :start_x => width/2, :start_y => height/2,
4c94f7f Committer: Martin Prout <martin_p@lineone.net>
Martin Prout authored
170 :color => [0.5, 0.5, 0.5, 1],
9f822bd @jashkenas replaced map with vine
authored
171 :stop_size => 1.5}
172 @values.merge!(starting_values)
5d734f7 @jashkenas first commit of the split-out context_free library
authored
173 @finished = false
174 @app.reset_matrix
9f822bd @jashkenas replaced map with vine
authored
175 @app.rect_mode CENTER
176 @app.ellipse_mode CENTER
5d734f7 @jashkenas first commit of the split-out context_free library
authored
177 @app.no_stroke
b915cba Committer: Martin Prout <martin_p@lineone.net>
Martin Prout authored
178 @app.color_mode HSB, 1.0
5d734f7 @jashkenas first commit of the split-out context_free library
authored
179 @app.translate @values[:start_x], @values[:start_y]
180 self.send(rule_name, {})
181 end
ed79110 @jashkenas merging monkstone and whitespace
authored
182
183
06eec38 @jashkenas commenting the library
authored
184 # Before actually drawing the next step, we need to move to the appropriate
185 # location.
5d734f7 @jashkenas first commit of the split-out context_free library
authored
186 def get_ready_to_draw
187 @app.translate(@values[:x], @values[:y])
188 sign = (@values[:flip] ? -1 : 1)
189 @app.rotate(sign * @values[:rotation])
190 end
ed79110 @jashkenas merging monkstone and whitespace
authored
191
192
06eec38 @jashkenas commenting the library
authored
193 # Compute the rendering parameters for drawing a shape.
5d734f7 @jashkenas first commit of the split-out context_free library
authored
194 def get_shape_values(some_options)
195 old_ops = @values.dup
196 merge_options(old_ops, some_options) if some_options
197 @app.fill *old_ops[:color]
198 return old_ops[:size], old_ops
199 end
ed79110 @jashkenas merging monkstone and whitespace
authored
200
201
06eec38 @jashkenas commenting the library
authored
202 # Square, circle, and ellipse are the primitive drawing
5d734f7 @jashkenas first commit of the split-out context_free library
authored
203 # methods, but hopefully triangles will be added soon.
204 def square(some_options=nil)
205 size, options = *get_shape_values(some_options)
9f822bd @jashkenas replaced map with vine
authored
206 @app.rect(0, 0, size, size)
5d734f7 @jashkenas first commit of the split-out context_free library
authored
207 end
ed79110 @jashkenas merging monkstone and whitespace
authored
208
209
5d734f7 @jashkenas first commit of the split-out context_free library
authored
210 def circle(some_options=nil)
211 size, options = *get_shape_values(some_options)
9f822bd @jashkenas replaced map with vine
authored
212 @app.ellipse(0, 0, size, size)
5d734f7 @jashkenas first commit of the split-out context_free library
authored
213 end
502f8d4 y scale triangle adjustment now calculted
tux authored
214
215 def triangle(some_options=nil)
16aed96 @jashkenas sierpinski in the house
authored
216 rot = some_options[:rotation]
217 @app.rotate(rot) if rot
502f8d4 y scale triangle adjustment now calculted
tux authored
218 size, options = *get_shape_values(some_options)
eda84af @jashkenas fixing triangles to the unit circle, thanks to monkstone's new consta…
authored
219 @app.triangle(0, TRIANGLE_TOP * size, 0.5 * size, TRIANGLE_BOTTOM * size, -0.5 * size, TRIANGLE_BOTTOM * size)
16aed96 @jashkenas sierpinski in the house
authored
220 @app.rotate(-rot) if rot
502f8d4 y scale triangle adjustment now calculted
tux authored
221 end
ed79110 @jashkenas merging monkstone and whitespace
authored
222
223
9f822bd @jashkenas replaced map with vine
authored
224 def ellipse(some_options={})
5d734f7 @jashkenas first commit of the split-out context_free library
authored
225 rot = some_options[:rotation]
226 @app.rotate(rot) if rot
227 size, options = *get_shape_values(some_options)
228 width = options[:width] || options[:size]
229 height = options[:height] || options[:size]
9f822bd @jashkenas replaced map with vine
authored
230 @app.oval(options[:x] || 0, options[:y] || 0, width, height)
5d734f7 @jashkenas first commit of the split-out context_free library
authored
231 @app.rotate(-rot) if rot
232 end
06eec38 @jashkenas commenting the library
authored
233 alias_method :oval, :ellipse
ed79110 @jashkenas merging monkstone and whitespace
authored
234
5d734f7 @jashkenas first commit of the split-out context_free library
authored
235 end
ed79110 @jashkenas merging monkstone and whitespace
authored
236
502f8d4 y scale triangle adjustment now calculted
tux authored
237 end
Something went wrong with that request. Please try again.