class Runby::PaceCalculator

Encapsulates the algorithms used to calculate target paces.

Constants

DATA_POINTS_COUNT

The number of data points plotted on our line of 5K times.

We take 5K times from 14:00 to 42:00 with a sample rate
of 30 seconds, and out pops 57.
MIDPOINT_X

The midpoint along the X axis of our pace data “graph”

Attributes

fastest_pace_km[R]

The fastest pace within the run type data set.

Given a personal record of 14 minutes for a 5K race,
this is the prescribed pace for this run type.
midpoint_radius_divisor[R]

For maximum flexibility, we assume the radius of the curve

of the pace data to be equal to the X axis midpoint, a perfect circle.
Use the midpoint_radius_divisor to reduce the height of the curve
until it matches that of the data. (See #curve_minutes)
slowest_pace_km[R]

The slowest pace for the run type data set.

Given a personal record of 42 minutes for a 5K race,
this is the prescribed pace for this run type.

Public Class Methods

new(golden_pace_set, midpoint_radius_divisor) click to toggle source
# File lib/runby_pace/pace_calculator.rb, line 30
def initialize(golden_pace_set, midpoint_radius_divisor)
  @fastest_pace_km = golden_pace_set.fastest
  @slowest_pace_km = golden_pace_set.slowest
  @midpoint_radius_divisor = midpoint_radius_divisor
end

Public Instance Methods

calc(five_k_time, distance_units = :km) click to toggle source

Calculate the prescribed pace for the given 5K time @return [Pace]

# File lib/runby_pace/pace_calculator.rb, line 43
def calc(five_k_time, distance_units = :km)
  five_k_time = Runby.sanitize(five_k_time).as(RunbyTime)
  distance_units = Runby.sanitize(distance_units).as(DistanceUnit)

  minutes_per_unit = calculate_minutes_per_unit(distance_units, five_k_time)
  build_pace minutes_per_unit, distance_units
end
slope() click to toggle source

Calculate the slope of the line between the fastest and slowest paces

# File lib/runby_pace/pace_calculator.rb, line 37
def slope
  (@slowest_pace_km.time.total_minutes - @fastest_pace_km.time.total_minutes) / (DATA_POINTS_COUNT - 1)
end

Private Instance Methods

build_pace(minutes_per_unit, distance_units) click to toggle source
# File lib/runby_pace/pace_calculator.rb, line 53
def build_pace(minutes_per_unit, distance_units)
  time = RunbyTime.from_minutes(minutes_per_unit)
  distance = Distance.new distance_units, 1
  Pace.new time, distance
end
calculate_minutes_per_unit(distance_units, five_k_time) click to toggle source
# File lib/runby_pace/pace_calculator.rb, line 59
def calculate_minutes_per_unit(distance_units, five_k_time)
  x2 = ((five_k_time.total_minutes * 2) - (MIDPOINT_X - 1)) - 1
  minutes_per_km = slope * x2 + @fastest_pace_km.time.total_minutes + curve_minutes(x2)
  minutes_per_km * distance_units.conversion_factor
end
curve_minutes(x_axis) click to toggle source

Since the paces for each 5K time do not progress in a straight line

when plotted on a graph, but rather a curve with its highest point near
the center, we must add some seconds to the calculated time depending on
where we are on the line.

Paces near the start and end of the line need little to no time added,

whereas paces near the middle need the most time added.

The default curve radius is the same as the midpoint of the X axis,

forming a circle. Use #midpoint_radius_divisor to reduce it's size.
# File lib/runby_pace/pace_calculator.rb, line 73
def curve_minutes(x_axis)
  return 0 if @midpoint_radius_divisor.zero?
  midpoint_reduction = x_axis
  midpoint = MIDPOINT_X
  if midpoint_reduction > midpoint
    midpoint_reduction = midpoint - (midpoint_reduction - midpoint)
    midpoint_reduction = 0 if midpoint_reduction.negative?
  end
  # TODO: Use an actual curve instead of a triangle to calculate the number of minutes to add.
  midpoint_reduction / @midpoint_radius_divisor / 60
end