ruby-processing 2.4.3 → 2.4.4

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 (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