ruby-processing 2.4.3 → 2.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/CHANGELOG +5 -0
  4. data/README.md +4 -2
  5. data/lib/ruby-processing/exporters/application_exporter.rb +1 -0
  6. data/lib/ruby-processing/version.rb +1 -1
  7. data/library/boids/boids.rb +14 -13
  8. data/library/vecmath/lib/vec.rb +20 -4
  9. data/samples/contributed/circle_collision.rb +92 -149
  10. data/samples/contributed/drawolver.rb +13 -27
  11. data/samples/contributed/elegant_ball.rb +158 -0
  12. data/samples/contributed/fern.rb +16 -3
  13. data/samples/contributed/flight_patterns.rb +16 -4
  14. data/samples/external_library/java_processing/{pbox2d → box2d_processing}/bumpy_surface_noise.rb +4 -9
  15. data/samples/external_library/java_processing/{pbox2d → box2d_processing}/data/java_args.txt +0 -0
  16. data/samples/external_library/java_processing/{pbox2d → box2d_processing}/library/custom_shape/custom_shape.rb +1 -1
  17. data/samples/external_library/java_processing/{pbox2d → box2d_processing}/library/particle_system/particle_system.rb +7 -10
  18. data/samples/external_library/java_processing/{pbox2d → box2d_processing}/library/surface/surface.rb +2 -2
  19. data/samples/external_library/java_processing/{pbox2d → box2d_processing}/liquidy.rb +7 -7
  20. data/samples/external_library/java_processing/{pbox2d → box2d_processing}/polygons.rb +4 -9
  21. data/samples/external_library/java_processing/custom/README.md +15 -0
  22. data/samples/external_library/java_processing/custom/Rakefile +28 -0
  23. data/samples/external_library/java_processing/custom/landscape.rb +58 -0
  24. data/samples/external_library/java_processing/custom/src/nn/Connection.java +47 -0
  25. data/samples/external_library/java_processing/custom/src/nn/HiddenNeuron.java +20 -0
  26. data/samples/external_library/java_processing/custom/src/nn/InputNeuron.java +23 -0
  27. data/samples/external_library/java_processing/custom/src/nn/Network.java +136 -0
  28. data/samples/external_library/java_processing/custom/src/nn/Neuron.java +79 -0
  29. data/samples/external_library/java_processing/custom/src/nn/OutputNeuron.java +7 -0
  30. data/samples/external_library/java_processing/custom/xor.rb +88 -0
  31. data/samples/external_library/ruby_gem/README +1 -1
  32. data/samples/external_library/ruby_gem/data/data.json +8 -0
  33. data/samples/external_library/ruby_gem/draw_test.rb +171 -0
  34. data/samples/processing_app/basics/form/icosahedra.rb +39 -0
  35. data/samples/processing_app/basics/form/library/icosahedron/icosahedron.rb +60 -0
  36. data/samples/processing_app/basics/form/toroid.rb +78 -92
  37. data/samples/processing_app/basics/transform/birds.rb +6 -12
  38. data/samples/processing_app/basics/transform/cubes_in_cube.rb +25 -22
  39. data/samples/processing_app/basics/transform/library/cube/cube.rb +16 -16
  40. data/samples/processing_app/basics/transform/rotate_push_pop.rb +1 -1
  41. data/samples/processing_app/demos/graphics/bezier_patch.rb +18 -31
  42. data/samples/processing_app/demos/graphics/trefoil.rb +15 -15
  43. data/samples/processing_app/library/vecmath/acceleration_with_vectors.rb +3 -3
  44. data/samples/processing_app/library/vecmath/hilbert_fractal.rb +2 -2
  45. data/samples/processing_app/library/vecmath/library/flock/flock.rb +18 -21
  46. data/samples/processing_app/library/vecmath/library/hilbert/hilbert.rb +11 -8
  47. data/samples/processing_app/library/vecmath/library/wiggler/wiggler.rb +7 -15
  48. data/samples/processing_app/library/vecmath/seeking_neural.rb +172 -0
  49. data/samples/processing_app/topics/animation/animated_sprite.rb +5 -8
  50. data/samples/processing_app/topics/animation/sequential.rb +2 -3
  51. data/samples/processing_app/topics/create_shapes/library/particle/particle_system.rb +7 -7
  52. data/samples/processing_app/topics/create_shapes/particle_system_pshape.rb +2 -2
  53. data/samples/processing_app/topics/create_shapes/wiggle_pshape.rb +2 -1
  54. data/samples/processing_app/topics/lsystems/koch.rb +1 -1
  55. data/samples/processing_app/topics/lsystems/library/koch/koch_fractal.rb +24 -23
  56. data/samples/processing_app/topics/motion/circle_collision.rb +117 -160
  57. data/samples/processing_app/topics/motion/library/cube/cube.rb +1 -1
  58. data/samples/processing_app/topics/motion/morph.rb +1 -1
  59. data/samples/processing_app/topics/motion/reflection1.rb +17 -16
  60. data/samples/processing_app/topics/shaders/conway.rb +2 -2
  61. data/samples/processing_app/topics/shaders/data/conway.glsl +10 -10
  62. data/samples/processing_app/topics/shaders/glsl_heightmap_noise.rb +9 -8
  63. data/samples/processing_app/topics/shaders/landscape.rb +1 -1
  64. data/samples/processing_app/topics/simulate/flocking.rb +1 -1
  65. data/samples/processing_app/topics/simulate/library/flock/flock.rb +62 -57
  66. data/samples/processing_app/topics/simulate/multiple_particle_systems.rb +8 -28
  67. data/samples/processing_app/topics/simulate/simple_particle_system.rb +9 -7
  68. data/samples/processing_app/topics/simulate/smoke_particle_system.rb +12 -11
  69. data/vendors/Rakefile +2 -2
  70. metadata +26 -21
  71. data/samples/contributed/pong.rb +0 -177
  72. data/samples/contributed/simple_buffer.rb +0 -44
  73. data/samples/external_library/java_processing/pbox2d/contact_test.rb +0 -23
  74. data/samples/processing_app/basics/form/icosahedra/icosahedra.rb +0 -72
  75. data/samples/processing_app/basics/form/icosahedra/icosahedron.rb +0 -116
  76. data/samples/processing_app/basics/form/icosahedra/shape_3D.rb +0 -25
@@ -35,23 +35,26 @@ end
35
35
  ###########################
36
36
  class Hilbert
37
37
  include Processing::Proxy
38
- ADJUSTMENT = [0, 0.5, 1.5, 3.5, 7.5, 15]
39
38
  attr_reader :grammar, :axiom, :production, :premis, :rule,
40
- :theta, :distance, :phi, :gen
39
+ :theta, :distance, :phi, :gen, :adj_array
41
40
 
42
- def initialize(len, gen = 1)
41
+ def initialize(size: 1.0, gen: 1)
43
42
  @axiom = "X" # AXIOM
44
43
  @rule = {"X" => "^<XF^<XFX-F^>>XFX&F+>>XFX-F>X->"} # RULE
45
44
  @gen = gen
46
45
  @grammar = Grammar.new(axiom, rule)
47
46
  @production = grammar.generate gen
48
- @distance = len/(pow(2, gen) - 1)
49
- @theta = Math::PI/180 * 90
50
- @phi = Math::PI/180 * 90
47
+ @distance = size / (gen**2 - 1)
48
+ @theta = radians 90
49
+ @phi = radians 90
50
+ @adj_array = [0, 0.5, 1.5, 3.5, 7.5, 15].map{|x| Vec3D.new(x * -1, x, x * -1)}
51
+
51
52
  end
52
53
 
53
54
  def render()
54
- translate( -distance * ADJUSTMENT[gen], distance * ADJUSTMENT[gen], -distance * ADJUSTMENT[gen])
55
+ #translate( -distance * ADJUSTMENT[gen], distance * ADJUSTMENT[gen], -distance * ADJUSTMENT[gen])
56
+ adj = adj_array[gen] * distance
57
+ translate(adj.x, adj.y, adj.z)
55
58
  fill(0, 75, 152)
56
59
  light_specular(204, 204, 204)
57
60
  specular(255, 255, 255)
@@ -81,4 +84,4 @@ class Hilbert
81
84
  end
82
85
  end
83
86
 
84
- end
87
+ end
@@ -10,12 +10,8 @@ class Wiggler
10
10
  @y = height/2
11
11
  @yoff = 0
12
12
  # The "original" locations of the vertices make up a circle
13
- @original = []
14
- (0 ... TAU).step(0.2) do |a|
15
- v = Vec2D.from_angle(a)
16
- v *= 100
17
- original << v
18
- end
13
+
14
+ @original = (0 ... 5 * TAU).map{|a| Vec2D.from_angle(a * 0.2) * 100}
19
15
 
20
16
  # Now make the PShape with those vertices
21
17
  @s = create_shape
@@ -23,22 +19,18 @@ class Wiggler
23
19
  s.fill(127)
24
20
  s.stroke(0)
25
21
  s.stroke_weight(2)
26
- original.each do |v|
27
- s.vertex(v.x, v.y)
28
- end
22
+ original.map{ |v| s.vertex(v.x, v.y)}
29
23
  s.end_shape(CLOSE)
30
24
  end
31
25
 
32
26
  def wiggle
33
27
  @xoff = 0
34
28
  # Apply an offset to each vertex
35
- (0 ... s.get_vertex_count).each do |i|
29
+ rad = -> (pos){(Vec2D.from_angle(TAU * noise(xoff, yoff)) * 4) + pos}
30
+
31
+ original.each_with_index do |pos, i|
36
32
  # Calculate a new vertex location based on noise around "original" location
37
- pos = original[i]
38
- a = TAU*noise(xoff,yoff)
39
- r = Vec2D.from_angle(a)
40
- r *= 4
41
- r += pos
33
+ r = rad.call(pos)
42
34
  # Set the location of each vertex to the new one
43
35
  s.set_vertex(i, r.x, r.y)
44
36
  # increment perlin noise x value
@@ -0,0 +1,172 @@
1
+ # Based on SeekingNeural example by Daniel Shiffman
2
+ # The Nature of Code
3
+ # http://natureofcode.com
4
+
5
+ load_library :vecmath
6
+
7
+
8
+ module SeekingNeural
9
+ class Perceptron
10
+ # Perceptron is created with n weights and learning constant
11
+ def initialize(n, c)
12
+ @weights = Array.new(n){ rand(0 .. 1.0) }
13
+ @c = c
14
+ end
15
+
16
+ # Function to train the Perceptron
17
+ # Weights are adjusted based on vehicle's error
18
+ def train(forces, error)
19
+ trained = @weights.zip(forces.map{|f| f.to_a}
20
+ .map{|a, b| (a * error.x + b * error.y) * @c})
21
+ .map {|w, c| constrain(w + c, 0.0, 1.0)}
22
+ @weights = trained
23
+ end
24
+
25
+ # Give me a steering result
26
+ def feedforward(forces)
27
+ # Sum all values
28
+ forces.zip(@weights).map{|a, b| a * b}.inject(Vec2D.new, :+)
29
+ end
30
+ end
31
+
32
+ # Seek
33
+ # Daniel Shiffman <http://www.shiffman.net>
34
+
35
+ class Vehicle
36
+
37
+ MAX_SPEED = 4
38
+ MAX_FORCE = 0.1
39
+ attr_reader :brain, :sz, :location, :targets, :desired
40
+ attr_reader :maxforce_squared, :maxspeed_squared
41
+
42
+ def initialize(n, x, y)
43
+ @brain = Perceptron.new(n, 0.001)
44
+ @acceleration = Vec2D.new
45
+ @velocity = Vec2D.new
46
+ @location = Vec2D.new(x, y)
47
+ @sz = 6.0
48
+ @maxspeed_squared = MAX_SPEED * MAX_SPEED
49
+ @maxforce_squared = MAX_FORCE * MAX_FORCE
50
+ end
51
+
52
+ # Method to update location
53
+ def update(width, height)
54
+ # Update velocity
55
+ @velocity += @acceleration
56
+ # Limit speed
57
+ @velocity.set_mag(MAX_SPEED) {@velocity.mag_squared > maxspeed_squared}
58
+ @location += @velocity
59
+ # Reset acceleration to 0 each cycle
60
+ @acceleration *= 0
61
+
62
+ @location.x = constrain(location.x, 0, width)
63
+ @location.y = constrain(location.y, 0, height)
64
+ end
65
+
66
+ def apply_force(force)
67
+ # We could add mass here if we want A = F / M
68
+ @acceleration += force
69
+ end
70
+
71
+ # Here is where the brain processes everything
72
+ def steer(targets, desired)
73
+ # Steer towards all targets
74
+ forces = targets.map{|target| seek(target) }
75
+
76
+ # That array of forces is the input to the brain
77
+ result = brain.feedforward(forces)
78
+
79
+ # Use the result to steer the vehicle
80
+ apply_force(result)
81
+
82
+ # Train the brain according to the error
83
+ error = desired - location
84
+ brain.train(forces, error)
85
+ end
86
+
87
+ # A method that calculates a steering force towards a target
88
+ # STEER = DESIRED MINUS VELOCITY
89
+ def seek(target)
90
+ desired = target - location # A vector pointing from the location to the target
91
+
92
+ # Normalize desired and scale to the maximum speed
93
+ desired.normalize!
94
+ desired *= MAX_SPEED
95
+ # Steering = Desired minus velocity
96
+ steer = desired - @velocity
97
+ steer.set_mag(MAX_FORCE) {steer.mag_squared > maxforce_squared} # Limit to a maximum steering force
98
+ steer
99
+ end
100
+
101
+ def display
102
+
103
+ # Draw a triangle rotated in the direction of velocity
104
+ theta = @velocity.heading + Math::PI / 2
105
+ fill(175)
106
+ stroke(0)
107
+ stroke_weight(1)
108
+ push_matrix
109
+ translate(location.x, location.y)
110
+ rotate(theta)
111
+ begin_shape
112
+ vertex(0, -sz)
113
+ vertex(-sz * 0.5, sz)
114
+ vertex(sz * 0.5, sz)
115
+ end_shape(CLOSE)
116
+ pop_matrix
117
+ end
118
+ end
119
+ end
120
+
121
+ include SeekingNeural
122
+
123
+ # A Vehicle controlled by a Perceptron
124
+ attr_reader :targets, :desired, :v
125
+
126
+
127
+ def setup
128
+ size(640, 360)
129
+ # The Vehicle's desired location
130
+ @desired = Vec2D.new(width/2, height/2)
131
+
132
+ # Create a list of targets
133
+ make_targets
134
+
135
+ # Create the Vehicle (it has to know about the number of targets
136
+ # in order to configure its brain)
137
+ @v = Vehicle.new(targets.size, rand(width), rand(height))
138
+ end
139
+
140
+ # Make a random ArrayList of targets to steer towards
141
+ def make_targets
142
+ @targets = Array.new(8) { Vec2D.new(rand(width), rand(height)) }
143
+ end
144
+
145
+ def draw
146
+ background(255)
147
+
148
+ # Draw a circle to show the Vehicle's goal
149
+ stroke(0)
150
+ stroke_weight(2)
151
+ fill(0, 100)
152
+ ellipse(desired.x, desired.y, 36, 36)
153
+
154
+ # Draw the targets
155
+ targets.each do |target|
156
+ no_fill
157
+ stroke(0)
158
+ stroke_weight(2)
159
+ ellipse(target.x, target.y, 16, 16)
160
+ line(target.x, target.y - 16, target.x, target.y + 16)
161
+ line(target.x - 16, target.y, target.x + 16, target.y)
162
+ end
163
+
164
+ # Update the Vehicle
165
+ v.steer(targets, desired)
166
+ v.update(width, height)
167
+ v.display
168
+ end
169
+
170
+ def mouse_pressed
171
+ make_targets
172
+ end
@@ -15,7 +15,7 @@ attr_reader :xpos, :ypos, :animation1, :animation2
15
15
  def setup
16
16
  size(640, 360)
17
17
  background(255, 204, 0)
18
- frameRate(24)
18
+ frame_rate(24)
19
19
  @animation1 = Animation.new("PT_Shifty_", 38)
20
20
  @animation2 = Animation.new("PT_Teddy_", 60)
21
21
  @ypos = height * 0.25
@@ -35,24 +35,21 @@ def draw
35
35
  end
36
36
  end
37
37
 
38
- class Animation < Array
38
+ class Animation
39
39
  attr_reader :images, :image_count, :frame
40
40
 
41
41
  def initialize(image_prefix, count)
42
42
  @image_count = count
43
43
  @frame = 0
44
- image_count.times do |i|
45
- # using ruby string format in preference to processing nf function
46
- self << load_image("%s%04d%s" % [image_prefix, i, ".gif"])
47
- end
44
+ @images = (0 ... image_count).map{|i| load_image("%s%04d%s" % [image_prefix, i, ".gif"])}
48
45
  end
49
46
 
50
47
  def display(xpos, ypos)
51
48
  @frame = (frame + 1) % image_count
52
- image(self[frame], xpos, ypos)
49
+ image(images[frame], xpos, ypos)
53
50
  end
54
51
 
55
52
  def get_width
56
- self[0].width
53
+ images[0].width
57
54
  end
58
55
  end
@@ -6,7 +6,6 @@
6
6
  # Twelve images are loaded and each is displayed individually in a loop.
7
7
  #
8
8
 
9
-
10
9
  NUM_FRAMES = 12 # The number of frames in the animation
11
10
  attr_reader :frame, :images
12
11
 
@@ -35,8 +34,8 @@ def draw
35
34
  offset = 0
36
35
  (-100 ... width).step(images[0].width) do |i|
37
36
  image(images[(frame+offset) % NUM_FRAMES], i, -20)
38
- offset+=2
37
+ offset += 2
39
38
  image(images[(frame+offset) % NUM_FRAMES], i, height/2)
40
- offset+=2
39
+ offset += 2
41
40
  end
42
41
  end
@@ -42,7 +42,7 @@ end
42
42
  class Particle
43
43
  include Processing::Proxy
44
44
 
45
- GRAVITY = PVector.new(0, 0.1)
45
+ GRAVITY = Vec2D.new(0, 0.1)
46
46
 
47
47
  attr_reader :center, :velocity, :lifespan, :s_shape, :part_size,
48
48
  :width, :height, :sprite
@@ -64,7 +64,7 @@ class Particle
64
64
  s_shape.end_shape
65
65
 
66
66
  # Initialize center vector
67
- @center = PVector.new
67
+ @center = Vec2D.new
68
68
 
69
69
  # Set the particle starting location
70
70
  rebirth(width/2, height/2)
@@ -74,8 +74,8 @@ class Particle
74
74
  theta = rand(-PI .. PI)
75
75
  speed = rand(0.5 .. 4)
76
76
  # A velocity with random angle and magnitude
77
- @velocity = PVector.from_angle(theta)
78
- @velocity.mult(speed)
77
+ @velocity = Vec2D.from_angle(theta)
78
+ @velocity *= speed
79
79
  # Set lifespan
80
80
  @lifespan = 255
81
81
  # Set location using translate
@@ -83,7 +83,7 @@ class Particle
83
83
  s_shape.translate(x, y)
84
84
 
85
85
  # Update center vector
86
- @center.set(x, y, 0)
86
+ @center.x, @center.y = x, y
87
87
  end
88
88
 
89
89
  # Is it off the screen, or its lifespan is over?
@@ -96,12 +96,12 @@ class Particle
96
96
  # Decrease life
97
97
  @lifespan = lifespan - 1
98
98
  # Apply gravity
99
- velocity.add(GRAVITY)
99
+ @velocity += GRAVITY
100
100
  s_shape.setTint(color(255, lifespan))
101
101
  # Move the particle according to its velocity
102
102
  s_shape.translate(velocity.x, velocity.y)
103
103
  # and also update the center
104
- center.add(velocity)
104
+ @center += velocity
105
105
  end
106
106
  end
107
107
 
@@ -4,7 +4,7 @@
4
4
  # For guts of implementation see 'particle' library
5
5
  #
6
6
 
7
- load_library 'particle'
7
+ load_libraries :vecmath, :particle
8
8
 
9
9
  # Particle System object and image
10
10
  attr_reader :ps
@@ -32,7 +32,7 @@ def draw
32
32
  # Display frame rate
33
33
  fill(255, 0, 255)
34
34
  text_size(16)
35
- text("Frame rate: #{frame_rate.round(1)}", 10, 20)
35
+ text("Frame rate: #{format('%.2f', frame_rate)}", 10, 20)
36
36
 
37
37
  end
38
38
 
@@ -1,6 +1,7 @@
1
1
  #
2
2
  # WigglePShape.
3
- #
3
+ # wiggler library uses PVector, see vecmath library
4
+ # examples to see alternative using Vec2D
4
5
  # How to move the individual vertices of a PShape
5
6
  #
6
7
 
@@ -6,7 +6,7 @@
6
6
  # Each recursive level is drawn in sequence.
7
7
  #
8
8
 
9
- load_library 'koch'
9
+ load_libraries :koch, :vecmath
10
10
 
11
11
  attr_reader :k
12
12
 
@@ -7,8 +7,8 @@ class KochFractal
7
7
  attr_reader :start, :endk, :lines, :count
8
8
 
9
9
  def initialize width, height
10
- @start = PVector.new(0, height - 20)
11
- @endk = PVector.new(width, height - 20)
10
+ @start = Vec2D.new(0, height - 20)
11
+ @endk = Vec2D.new(width, height - 20)
12
12
  restart
13
13
  end
14
14
 
@@ -22,7 +22,7 @@ class KochFractal
22
22
  def restart
23
23
  @count = 0 # Reset count
24
24
  @lines = [] # Empty the array list
25
- lines << KochLine.new(start, endk) # Add the initial line (from one end PVector to the other)
25
+ lines << KochLine.new(start, endk) # Add the initial line (from one end Vector to the other)
26
26
  end
27
27
 
28
28
  def get_count
@@ -47,13 +47,13 @@ class KochFractal
47
47
  def iterate(before)
48
48
  now = [] # Create empty list
49
49
  before.each do |line|
50
- # Calculate 5 koch PVectors (done for us by the line object)
50
+ # Calculate 5 koch Vectors (done for us by the line object)
51
51
  a = line.start
52
52
  b = line.kochleft
53
53
  c = line.kochmiddle
54
54
  d = line.kochright
55
55
  e = line.endk
56
- # Make line segments between all the PVectors and add them
56
+ # Make line segments between all the Vectors and add them
57
57
  # Note how we can chain '<<' in ruby, could all be in one line.
58
58
  now << KochLine.new(a,b) << KochLine.new(b,c)
59
59
  now << KochLine.new(c,d) << KochLine.new(d,e)
@@ -68,18 +68,18 @@ end
68
68
 
69
69
  # Koch Curve
70
70
  # A class to describe one line segment in the fractal
71
- # Includes methods to calculate mid PVectors along the line according to the Koch algorithm
71
+ # Includes methods to calculate mid Vectors along the line according to the Koch algorithm
72
72
 
73
73
  class KochLine
74
74
  include Processing::Proxy
75
75
  # Two PVectors,
76
- # a is the "left" PVector and
77
- # b is the "right PVector
76
+ # a is the "left" Vector and
77
+ # b is the "right Vector
78
78
  attr_reader :start, :endk
79
79
 
80
80
  def initialize(start, endk)
81
- @start = start.get
82
- @endk = endk.get
81
+ @start = start.dup
82
+ @endk = endk.dup
83
83
  end
84
84
 
85
85
  def display
@@ -89,28 +89,29 @@ class KochLine
89
89
 
90
90
  # This is easy, just 1/3 of the way
91
91
  def kochleft
92
- v = PVector.sub(endk, start)
93
- v.div(3)
94
- v.add(start)
92
+ v = endk - start
93
+ v /= 3.0
94
+ v += start
95
95
  return v
96
96
  end
97
97
 
98
- # More complicated, have to use a little trig to figure out where this PVector is!
98
+ # More complicated, have to use a little trig to figure out where this Vector is!
99
99
  def kochmiddle
100
- v = PVector.sub(endk, start)
101
- v.div(3)
102
- p = start.get
103
- p.add(v)
104
- v.rotate(-radians(60))
105
- p.add(v)
100
+ v = endk - start
101
+ v /= 3.0
102
+ p = start.dup
103
+ p += v
104
+ rot = radians(-60)
105
+ r = Vec2D.new((v.x * cos(rot)) - v.y * sin(rot), (v.x * sin(rot)) + (v.y * cos(rot)))
106
+ p += r
106
107
  return p
107
108
  end
108
109
 
109
110
  # Easy, just 2/3 of the way
110
111
  def kochright
111
- v = PVector.sub(start, endk)
112
- v.div(3)
113
- v.add(endk)
112
+ v = start - endk
113
+ v /= 3.0
114
+ v += endk
114
115
  return v
115
116
  end
116
117
  end