docs: def _make_hat(self, v1, v2, acceleration, distance, min_time):
          """Make a hat that looks like this:
  
              ______ vm
          v1 /|   | \
            d1| dm|d2\ v2
              |   |
            t1  tm t2
  
          Such that the area under the graph (d1+d2+d3) is distance and
          t1+t2+t3 >= min_time
          """
          if min_time > 0:
              # We are trying to meet time constraints
              # Solve quadratic to give vm
              b = v1 + v2 + min_time * acceleration
              c = distance * acceleration + (v1*v1 + v2*v2) / 2
              op = b*b - 4 * c
              if np.isclose(op, 0):
                  # Might have a negative number as rounding error...
                  op = 0
              elif op < 0:
                  # Can't do this, set something massive to fail vm check...
                  op = 10000000000
  
              def get_times(vm):
                  t1 = (vm - v1) / acceleration
                  t2 = (vm - v2) / acceleration
                  tm = min_time - t1 - t2
                  assert -self.max_velocity <= vm <= self.max_velocity
                  assert t1 >= 0 and t2 >= 0 and tm >= 0
                  return t1, tm, t2
  
              try:
                  # Try negative root
                  vm = (b - np.sqrt(op)) / 2
                  t1, tm, t2 = get_times(vm)
              except AssertionError:
                  try:
                      # Try positive root
                      vm = (b + np.sqrt(op)) / 2
                      t1, tm, t2 = get_times(vm)
                  except AssertionError:
                      # If vm is out of range or any segment takes negative time,
                      # we can't do it in min_time, so act as if unconstrained
                      t1, tm, t2, vm = self._calculate_hat_params(
                          v1, v2, acceleration, distance)
          else:
              t1, tm, t2, vm = self._calculate_hat_params(
                  v1, v2, acceleration, distance)
  
          # If middle segment needs to be negative time then we need to cap
          # vm and spend no time at vm
          if tm < 0:
              # Solve the quadratic to work out how long to spend accelerating
              vm = np.sqrt(
                  (2 * acceleration * distance + v1 * v1 + v2 * v2) / 2)
              if acceleration < 0:
                  vm = -vm
              t1 = self.acceleration_time(v1, vm)
              t2 = self.acceleration_time(vm, v2)
              tm = 0
  
          # Yield the result
          yield t1, vm
          yield tm, vm
          yield t2, v2