ruby-processing 2.5.0 → 2.5.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rpextras.jar +0 -0
  3. data/lib/ruby-processing/app.rb +26 -26
  4. data/lib/ruby-processing/exporters/base_exporter.rb +7 -5
  5. data/lib/ruby-processing/helper_methods.rb +78 -3
  6. data/lib/ruby-processing/version.rb +1 -1
  7. data/lib/templates/application/run.erb +4 -4
  8. data/samples/contributed/elegant_ball.rb +47 -37
  9. data/samples/contributed/tree.rb +1 -5
  10. data/samples/external_library/java_processing/custom/README.md +6 -5
  11. data/samples/external_library/java_processing/custom/Rakefile +17 -23
  12. data/samples/external_library/java_processing/custom/{src → ext}/nn/Connection.java +0 -0
  13. data/samples/external_library/java_processing/custom/{src → ext}/nn/HiddenNeuron.java +0 -0
  14. data/samples/external_library/java_processing/custom/{src → ext}/nn/InputNeuron.java +0 -0
  15. data/samples/external_library/java_processing/custom/{src → ext}/nn/Network.java +0 -0
  16. data/samples/external_library/java_processing/custom/{src → ext}/nn/Neuron.java +0 -0
  17. data/samples/external_library/java_processing/custom/{src → ext}/nn/OutputNeuron.java +0 -0
  18. data/samples/external_library/java_processing/custom/xor.rb +1 -1
  19. data/samples/external_library/ruby_gem/draw_test.rb +1 -1
  20. data/samples/processing_app/basics/arrays/array.rb +11 -11
  21. data/samples/processing_app/basics/color/blend_color.rb +17 -0
  22. data/samples/processing_app/basics/color/reading/reading.rb +20 -29
  23. data/samples/processing_app/basics/form/brick_tower.rb +11 -10
  24. data/samples/processing_app/basics/form/pie_chart.rb +4 -4
  25. data/samples/processing_app/basics/form/shape_transform.rb +17 -22
  26. data/samples/processing_app/basics/form/toroid.rb +16 -21
  27. data/samples/processing_app/basics/input/clock.rb +11 -10
  28. data/samples/processing_app/basics/math/sine_cosine.rb +6 -6
  29. data/samples/processing_app/basics/transform/bird.rb +9 -9
  30. data/samples/processing_app/basics/transform/birds.rb +1 -1
  31. data/samples/processing_app/basics/transform/library/bird/bird.rb +12 -12
  32. data/samples/processing_app/basics/transform/rotate.rb +10 -5
  33. data/samples/processing_app/basics/transform/triangle_flower.rb +3 -3
  34. data/samples/processing_app/library/fastmath/clock.rb +9 -7
  35. data/samples/processing_app/library/net/HTTPClient.rb +4 -2
  36. data/samples/processing_app/library/vecmath/arcball/arcball_box.rb +1 -1
  37. data/samples/processing_app/library/vecmath/arcball/arcball_radius.rb +1 -1
  38. data/samples/processing_app/library/vecmath/arcball/arcball_shape.rb +1 -1
  39. data/samples/processing_app/library/vecmath/vec2d/multiple_particle_systems.rb +1 -1
  40. data/samples/processing_app/library/vecmath/vec3d/retained_menger.rb +2 -2
  41. data/samples/processing_app/topics/create_shapes/particle_system_pshape.rb +1 -1
  42. data/samples/processing_app/topics/drawing/pulses.rb +3 -3
  43. data/samples/processing_app/topics/image_processing/linear_image.rb +1 -1
  44. data/samples/processing_app/topics/lsystems/csplant.rb +3 -3
  45. data/samples/processing_app/topics/motion/puff.rb +4 -4
  46. data/samples/processing_app/topics/shaders/glsl_heightmap_noise.rb +1 -1
  47. data/samples/processing_app/topics/shaders/landscape.rb +1 -1
  48. metadata +10 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a5b8a868675fde6dc0132b818e7587a83225334c
4
- data.tar.gz: e8df4b54e6a1cd9a20b8c0595e659540a3245f34
3
+ metadata.gz: 2afd84f1f31d6bb5a7646833da3198a47d36c288
4
+ data.tar.gz: eb51a2021e9294b6976de2b755e5b0ff90a81d9e
5
5
  SHA512:
6
- metadata.gz: 452ebce774bac88364da71c0800ed75cabb3017282d97adf2a0cf71b421c121e162a145d56c92974e12eb7b82358d204454c599cd86dee2f06418320cac90144
7
- data.tar.gz: e409850694f47b0b6acacd3f46d9fad87bfac2a2dca2a0ae238443828c14a7c5097f70157799f3a4501f6606cc9fcd6b4d6841161bada1985365996945ecf876
6
+ metadata.gz: f4fa0f3ac500132fcd125e67adc181de0706464431809fe96369d73432b2379b40a9edfef07a1dc19dfa030eafa77be5757920e22877088ab51ef8256d108ebe
7
+ data.tar.gz: 395804d382878458e4e9e2dc001c35cc1c6d81367ab4604a850ed636c1b334c2401e3c1cc6c68963178cc597c4422483dd51ce50665156cb310c7ddf673caf18
data/lib/rpextras.jar CHANGED
Binary file
@@ -53,22 +53,23 @@ module Processing
53
53
  if methods_to_alias.keys.include?(method_name)
54
54
  alias_method methods_to_alias[method_name], method_name
55
55
  end
56
- end
57
-
58
-
59
- # Class methods that we should make available in the instance.
60
- [:map, :pow, :norm, :lerp, :second, :minute, :hour, :day, :month, :year,
61
- :sq, :constrain, :dist, :blend_color, :degrees, :radians, :mag, :println,
62
- :hex, :min, :max, :abs, :binary, :ceil, :nf, :nfc, :nfp, :nfs,
63
- :norm, :round, :trim, :unbinary, :unhex ].each do |meth|
56
+ end
57
+
58
+ # Some class methods made available in the instance.
59
+ [:day, :month, :year, :sq, :mag, :println, :hex, :abs, :binary, :ceil,
60
+ :nf, :nfc, :nfp, :nfs, :round, :trim, :unbinary, :unhex ].each do |meth|
64
61
  method = <<-EOS
65
62
  def #{meth}(*args)
66
- self.class.#{meth}(*args)
63
+ self.class.#{meth}(*args)
67
64
  end
68
65
  EOS
69
66
  eval method
70
67
  end
71
-
68
+
69
+ # Above block deprecated from processing-2.5.1, you should in general prefer
70
+ # ruby alternatives (eg t = Time.now and t.sec to second):-
71
+ # constrain, dist, map, norm, lerp and blend_color are implemented directly
72
+
72
73
  # Handy getters and setters on the class go here:
73
74
  def self.sketch_class; @sketch_class; end
74
75
  @@full_screen = false
@@ -83,10 +84,6 @@ module Processing
83
84
  @sketch_class = subclass
84
85
  end
85
86
 
86
- def self.has_slider(*args) #:nodoc:
87
- raise "has_slider has been replaced with a nicer control_panel library. Check it out."
88
- end
89
-
90
87
  @@library_loader = LibraryLoader.new
91
88
  class << self
92
89
  def load_libraries(*args)
@@ -109,14 +106,15 @@ module Processing
109
106
 
110
107
  def library_loaded?(library_name)
111
108
  self.class.library_loaded?(library_name)
112
- end
113
-
114
- # When you make a new sketch, you pass in (optionally),
115
- # a width, height, x, y, title, and whether or not you want to
116
- # run in full-screen.
117
- #
118
- # This is a little different than Processing where height
119
- # and width are declared inside the setup method instead.
109
+ end
110
+
111
+ # It is 'NOT' usually necessary to directly pass options to a sketch, it
112
+ # gets done automatically for you. Since processing-2.0 you should prefer
113
+ # setting the sketch width and height and renderer using the size method,
114
+ # in the sketch (as with vanilla processing), which should be the first
115
+ # argument in setup. Sensible options to pass are x and y to locate sketch
116
+ # on the screen, or full_screen: true (prefer new hash syntax)
117
+
120
118
  def initialize(options={})
121
119
  super()
122
120
  $app = self
@@ -132,8 +130,11 @@ module Processing
132
130
  close
133
131
  end
134
132
 
135
- # for the list of all available args, see
136
- # http://processing.org/reference/
133
+ # For the list of all available args, see:-
134
+ # http://processing.org/reference/, however not all convenience functions
135
+ # are implemented in ruby-processing (you should in general prefer ruby
136
+ # alternatives when available and methods using java reflection, are best
137
+ # avoided entirely)
137
138
 
138
139
  args = []
139
140
  @width, @height = options[:width], options[:height]
@@ -144,7 +145,7 @@ module Processing
144
145
  @render_mode ||= JAVA2D
145
146
  x = options[:x] || 0
146
147
  y = options[:y] || 0
147
- args << "--location=#{x}, #{y}"
148
+ args << "--location=#{x},#{y}" # important no spaces here
148
149
  title = options[:title] || File.basename(SKETCH_PATH).sub(/(\.rb)$/, '').titleize
149
150
  args << title
150
151
  PApplet.run_sketch(args, self)
@@ -183,7 +184,6 @@ module Processing
183
184
  self.frame.dispose
184
185
  end
185
186
 
186
-
187
187
  private
188
188
 
189
189
  # Mix the Processing::Proxy into any inner classes defined for the
@@ -86,11 +86,13 @@ module Processing
86
86
  matchdata = code.match(/^.*[^::\.\w](require_relative|require|load)\b.*$/)
87
87
  break unless matchdata
88
88
  line = matchdata[0].gsub('__FILE__', "'#{@main_file_path}'")
89
- line = line.gsub(/\b(require_relative|require|load)\b/, 'partial_paths << ')
90
- eval(line)
91
- where = "{#{local_dir}/,}{#{partial_paths.join(',')}}"
92
- where += '.{rb,jar}' unless line =~ /\.[^.]+$/
93
- requirements += Dir[where]
89
+ if (line =~ /\b(require_relative|require|load)\b/)
90
+ ln = line.gsub(/\b(require_relative|require|load)\b/, '')
91
+ partial_paths << ln
92
+ where = "{#{local_dir}/,}{#{partial_paths.join(',')}}"
93
+ where += '.{rb,jar}' unless line =~ /\.[^.]+$/
94
+ requirements += Dir[where]
95
+ end
94
96
  code = matchdata.post_match
95
97
  end
96
98
  requirements
@@ -60,9 +60,86 @@ module Processing
60
60
  if block_given?
61
61
  Thread.new *args, &block
62
62
  else
63
- raise ArgumentError, 'thread must be called with a block' , caller
63
+ fail ArgumentError, 'thread must be called with a block' , caller
64
64
  end
65
65
  end
66
+
67
+ # explicitly provide 'processing.org' map instance method
68
+ def map(value, start1, stop1, start2, stop2)
69
+ start2 + (stop2 - start2) * ((value - start1).to_f / (stop1 - start1))
70
+ end
71
+
72
+ # explicitly provide 'processing.org' norm instance method
73
+ def norm(value, start, stop)
74
+ (value - start).to_f / (stop - start)
75
+ end
76
+
77
+ # explicitly provide 'processing.org' lerp instance method
78
+ def lerp(start, stop, amt)
79
+ start + (stop - start) * amt
80
+ end
81
+
82
+ # explicitly provide 'processing.org' min instance method
83
+ # to return a float:- a, b and c need to be floats
84
+
85
+ def min(*args)
86
+ args.min { |a,b| a <=> b }
87
+ end
88
+
89
+ # explicitly provide 'processing.org' max instance method
90
+ # to return a float:- a, b and c need to be floats
91
+
92
+ def max(*args)
93
+ args.max { |a, b| a <=> b }
94
+ end
95
+
96
+ # explicitly provide 'processing.org' dist instance method
97
+ def dist(*args)
98
+ alen = args.length
99
+ return Math.hypot(args[0] - args[2], args[1] - args[3]) if alen == 4
100
+ return Math.sqrt((args[0] - args[3]) * (args[0] - args[3]) +
101
+ (args[1] - args[4]) * (args[1] - args[4]) +
102
+ (args[2] - args[5]) * (args[2] - args[5])) if alen == 6
103
+ fail ArgumentError, 'takes 4 or 6 parameters'
104
+ end
105
+
106
+ # explicitly provide 'processing.org' constrain instance method
107
+ # to return a float:- amt, low and high need to be floats
108
+ def constrain(amt, low, high)
109
+ (amt < low) ? low : ((amt > high) ? high : amt)
110
+ end
111
+
112
+ # explicitly provide 'processing.org' pow instance method
113
+ def pow(x, exp)
114
+ # warn 'pow(x, exp) is deprecated use x**exp to avoid this warning'
115
+ x**exp
116
+ end
117
+
118
+ # explicitly provide 'processing.org' radians instance method
119
+ def radians(theta)
120
+ # warn 'radians(theta) is deprecated use theta.radians to avoid this warning'
121
+ theta.radians
122
+ end
123
+
124
+ def hour
125
+ # warn 'deprecated use t = Time.now and t.hour'
126
+ PApplet.hour
127
+ end
128
+
129
+ def second
130
+ # warn 'deprecated use t = Time.now and t.sec'
131
+ PApplet.second
132
+ end
133
+
134
+ def minute
135
+ # warn 'deprecated use t = Time.now and t.min'
136
+ PApplet.minute
137
+ end
138
+
139
+ # Uses PImage class method under hood
140
+ def blend_color(c1, c2, mode)
141
+ PImage.blendColor(c1, c2, mode)
142
+ end
66
143
 
67
144
  # There's just so many functions in Processing,
68
145
  # Here's a convenient way to look for them.
@@ -86,8 +163,6 @@ module Processing
86
163
  field.set_value(java_self, path || SKETCH_ROOT)
87
164
  end
88
165
 
89
-
90
-
91
166
  # Fix java conversion problems getting the last key
92
167
  # If it's ASCII, return the character, otherwise the integer
93
168
  def key
@@ -1,3 +1,3 @@
1
1
  module RubyProcessing
2
- VERSION = '2.5.0'
2
+ VERSION = '2.5.1'
3
3
  end
@@ -1,5 +1,5 @@
1
1
  #!/bin/sh
2
- APPDIR=$(dirname '$0')
3
- export EXPORTED='true'
4
- cd '$APPDIR/lib'
5
- java -cp "<%= @linux_class_path %>" org.jruby.Main ../lib/ruby-processing/runners/run.rb <%= @main_file %>
2
+ APPDIR=$(dirname "$0")
3
+ export EXPORTED="true"
4
+ cd "$APPDIR/lib"
5
+ java -cp "<%= @linux_class_path %>" org.jruby.Main ruby-processing/runners/run.rb <%= @main_file %>
@@ -3,17 +3,22 @@
3
3
  # Ben Notorianni aka lazydog
4
4
  #
5
5
 
6
+ # elegant.rb
7
+
8
+
6
9
  load_library :vecmath
7
10
 
8
- attr_reader :renderer
11
+ attr_reader :start_t, :renderer
9
12
 
10
- def setup
13
+ def setup()
11
14
  size(800, 800, P3D)
12
15
  @renderer = AppRender.new(self)
13
16
  color_mode(RGB, 1)
14
17
  end
15
18
 
16
- def draw
19
+
20
+
21
+ def draw()
17
22
  #background(0.25)
18
23
  background(0)
19
24
  # Move the origin so that the scene is centered on the screen.
@@ -23,7 +28,7 @@ def draw
23
28
  # Rotate the local coordinate system.
24
29
  smooth_rotation(5.0, 6.7, 7.3)
25
30
  # Draw the inner object.
26
- no_stroke
31
+ no_stroke()
27
32
  fill(smooth_colour(10.0, 12.0, 7.0))
28
33
  draw_icosahedron(5, 60.0, false)
29
34
  # Rotate the local coordinate system again.
@@ -37,18 +42,21 @@ end
37
42
  def setup_lights
38
43
  ambient_light(0.025, 0.025, 0.025)
39
44
  directional_light(0.2, 0.2, 0.2, -1, -1, -1)
40
- spot_light(1.0, 1.0, 1.0, -200, 0, 300, 1, 0, -1, PI/4, 20)
45
+ spot_light(1.0, 1.0, 1.0, -200, 0, 300, 1, 0, -1, Math::PI/4, 20)
41
46
  end
42
47
 
43
48
  ##
44
49
  # Generate a vector whose components change smoothly over time in the range [ 0, 1 ].
45
- # Each component uses a Math.sin function to map the current time in milliseconds somewhere
50
+ # Each component uses a Math.sin() function to map the current time in milliseconds somewhere
46
51
  # in the range [ 0, 1 ].A 'speed' factor is specified for each component.
47
52
  #
48
53
  def smooth_vector(s1, s2, s3)
49
- mills = millis * 0.00003
50
- rot = ->(a){0.5 * Math.sin(mills * a) + 0.5}
51
- Vec3D.new(rot.call(s1), rot.call(s2), rot.call(s3))
54
+ mills = millis * 0.00003 ## Lazydogs factor
55
+ # mills = millis * 0.0000001 ## worked for me a bit slower!!
56
+ x = 0.5 * Math.sin(mills * s1) + 0.5
57
+ y = 0.5 * Math.sin(mills * s2) + 0.5
58
+ z = 0.5 * Math.sin(mills * s3) + 0.5
59
+ Vec3D.new(x, y, z)
52
60
  end
53
61
 
54
62
  ##
@@ -62,13 +70,13 @@ end
62
70
 
63
71
  ##
64
72
  # Rotate the current coordinate system.
65
- # Uses smooth_vector to smoothly animate the rotation.
73
+ # Uses smooth_vector() to smoothly animate the rotation.
66
74
  #
67
75
  def smooth_rotation(s1, s2, s3)
68
76
  r1 = smooth_vector(s1, s2, s3)
69
- rotate_x(2.0 * PI * r1.x)
70
- rotate_y(2.0 * PI * r1.y)
71
- rotate_x(2.0 * PI * r1.z)
77
+ rotate_x(2.0 * Math::PI * r1.x)
78
+ rotate_y(2.0 * Math::PI * r1.y)
79
+ rotate_x(2.0 * Math::PI * r1.z)
72
80
  end
73
81
 
74
82
  ##
@@ -82,23 +90,25 @@ def draw_icosahedron(depth, r, spherical)
82
90
  gr = (1.0 + Math.sqrt(5.0)) / 2.0
83
91
  h = r / Math.sqrt(1.0 + gr * gr)
84
92
  v =
85
- [
86
- Vec3D.new(0, -h, h * r), Vec3D.new(0, -h, -h * r), Vec3D.new(0, h, -h * r), Vec3D.new(0, h, h * r),
87
- Vec3D.new(h, -h * r, 0), Vec3D.new(h, h * r, 0), Vec3D.new(-h, h * r, 0), Vec3D.new(-h, -h * r, 0),
88
- Vec3D.new(-h * r, 0, h), Vec3D.new(-h * r, 0, -h), Vec3D.new(h * r, 0, -h), Vec3D.new(h * r, 0, h)
93
+ [
94
+ Vec3D.new(0, -h, h*gr), Vec3D.new(0, -h, -h*gr), Vec3D.new(0, h, -h*gr), Vec3D.new(0, h, h*gr),
95
+ Vec3D.new(h, -h*gr, 0), Vec3D.new(h, h*gr, 0), Vec3D.new(-h, h*gr, 0), Vec3D.new(-h, -h*gr, 0),
96
+ Vec3D.new(-h*gr, 0, h), Vec3D.new(-h*gr, 0, -h), Vec3D.new(h*gr, 0, -h), Vec3D.new(h*gr, 0, h)
89
97
  ]
90
-
98
+
91
99
  # Draw the 20 triangular faces of the icosahedron.
92
- r = 0.0 unless spherical
93
-
100
+ unless spherical then
101
+ r = 0.0
102
+ end
103
+
94
104
  begin_shape(TRIANGLES)
95
-
96
- draw_triangle(depth, r, v[0], v[7], v[4])
105
+
106
+ draw_triangle(depth, r, v[0], v[7],v[4])
97
107
  draw_triangle(depth, r, v[0], v[4], v[11])
98
108
  draw_triangle(depth, r, v[0], v[11], v[3])
99
109
  draw_triangle(depth, r, v[0], v[3], v[8])
100
110
  draw_triangle(depth, r, v[0], v[8], v[7])
101
-
111
+
102
112
  draw_triangle(depth, r, v[1], v[4], v[7])
103
113
  draw_triangle(depth, r, v[1], v[10], v[4])
104
114
  draw_triangle(depth, r, v[10], v[11], v[4])
@@ -109,14 +119,14 @@ def draw_icosahedron(depth, r, spherical)
109
119
  draw_triangle(depth, r, v[8], v[9], v[6])
110
120
  draw_triangle(depth, r, v[9], v[7], v[8])
111
121
  draw_triangle(depth, r, v[7], v[1], v[9])
112
-
122
+
113
123
  draw_triangle(depth, r, v[2], v[1], v[9])
114
124
  draw_triangle(depth, r, v[2], v[10], v[1])
115
125
  draw_triangle(depth, r, v[2], v[5], v[10])
116
126
  draw_triangle(depth, r, v[2], v[6], v[5])
117
127
  draw_triangle(depth, r, v[2], v[9], v[6])
118
-
119
- end_shape
128
+
129
+ end_shape()
120
130
  end
121
131
 
122
132
  ##
@@ -124,8 +134,8 @@ end
124
134
  # If depth is 1 then draw the triangle otherwise subdivide first.
125
135
  #
126
136
  def draw_triangle(depth, r, p1, p2, p3)
127
-
128
- if (depth == 1)
137
+
138
+ if (depth == 1) then
129
139
  p1.to_vertex(renderer)
130
140
  p2.to_vertex(renderer)
131
141
  p3.to_vertex(renderer)
@@ -134,15 +144,15 @@ def draw_triangle(depth, r, p1, p2, p3)
134
144
  v1 = (p1 + p2) * 0.5
135
145
  v2 = (p2 + p3) * 0.5
136
146
  v3 = (p3 + p1) * 0.5
137
- unless (r == 0.0)
138
- # Project the verticies out onto the sphere with radius r.
139
- v1.normalize!
140
- v1 *= r
141
- v2.normalize!
142
- v2 *= r
143
- v3.normalize!
144
- v3 *= r
145
- end
147
+ unless (r == 0.0) then
148
+ # Project the verticies out onto the sphere with radius r.
149
+ v1.normalize!
150
+ v1 *= r
151
+ v2.normalize!
152
+ v2 *= r
153
+ v3.normalize!
154
+ v3 *= r
155
+ end
146
156
  ## Generate the next level of detail
147
157
  depth -= 1
148
158
  draw_triangle(depth, r, p1, v1, v3)
@@ -12,10 +12,6 @@ def setup
12
12
  @frame_time = nil
13
13
  end
14
14
 
15
- def radians(x)
16
- return x * (Math::PI / 180)
17
- end
18
-
19
15
  def draw
20
16
  t = Time.now
21
17
  if @frame_time
@@ -29,7 +25,7 @@ def draw
29
25
  # Let's pick an angle 0 to 90 degrees based on the mouse position
30
26
  a = (@x / width) * 90
31
27
  # Convert it to radians
32
- @theta = radians(a)
28
+ @theta = a.radians
33
29
  # Start the tree from the bottom of the screen
34
30
  translate(width / 2, height)
35
31
  # Draw a line 60 pixels
@@ -1,15 +1,16 @@
1
1
  Building the custom nn Library
2
2
  ==============================
3
3
  This is an example of a java library that you can build yourself with the included Rakefile.
4
- Ideally you will both have ant and jruby installed (you should also have a jdk-7+ installed).
4
+ You need a jdk, a system version of jruby and the ruby-compiler gem to build the nn library
5
+ (see ext dir).
6
+ Compiling and building the jar is dead easy just issue the following command in a console.
5
7
 
6
- If you have done all of that compiling and building jar is dead easy just issue the following command in a console.
7
-
8
- `jruby -S rake`
8
+ `jruby -S rake compile`
9
9
 
10
10
  you may also need to set JAVA_HOME to point to your jdk eg
11
11
 
12
- `export JAVA_HOME=/opt/jdk1.7.0_51` for linux users
12
+ `export JAVA_HOME=/opt/jdk1.7.0_65` for linux users
13
13
 
14
+ You can even run the sketch using `rake`, with the `:run` task `rake run` and tidy up when your are done `rake clean`
14
15
 
15
16
  This example was taken from [The Nature of Code](https://github.com/shiffman/The-Nature-of-Code) by Dan Shiffman, except the example has been rubified (and even the java code was doctored to be more up to date, and to prefer double to float).