class Physicist::Body

Attributes

dimensions[R]
overflow_time[RW]
position[R]
t0[R]
velocity[R]

Public Class Methods

new(position:, velocity:, t0:, dimensions:) click to toggle source
# File lib/physicist/body.rb, line 6
def initialize(position:, velocity:, t0:, dimensions:)
  @position = position
  @velocity = velocity
  @dimensions = dimensions
  @t0 = t0
end

Public Instance Methods

at(t, obstacles:[], fixed_timestep: false, planck_time: 0.00125) click to toggle source

def planck_time

0.00125

end

# File lib/physicist/body.rb, line 38
def at(t, obstacles:[], fixed_timestep: false, planck_time: 0.00125)
  if fixed_timestep
    # proceed in fixed timesteps...
    dt = t - t0
    acc = 0.0

    body = self
    while acc < dt + (overflow_time||0.0)
      acc += planck_time
      body = body.predict(t + acc, obstacles: obstacles)
    end

    # body.overflow_time = acc - (dt + (overflow_time||0.0))
    body
  else
    predict(t, obstacles: obstacles)
  end
end
epsilon() click to toggle source

some tiny number

# File lib/physicist/body.rb, line 30
def epsilon
  0.00000000001
end
friction() click to toggle source
# File lib/physicist/body.rb, line 25
def friction
  10.0
end
gravity() click to toggle source
# File lib/physicist/body.rb, line 21
def gravity
  30.0
end
height() click to toggle source
# File lib/physicist/body.rb, line 17
def height
  dimensions[1]
end
predict(t, obstacles: []) click to toggle source

predict without proceeding in fixed time-steps from last-updated

# File lib/physicist/body.rb, line 58
def predict(t, obstacles: [])
  x0, _   = *position
  vx0,vy0 = *velocity

  x_speed  = vx0.abs
  sign_x = vx0 > 0.0 ? 1.0 : (vx0 < -0.0 ? -1.0 : 0.0)

  dt = t - t0

  vy = vy0 + (gravity * dt)

  fric = friction * dt
  x_halted = false

  vx = if (3*fric) < x_speed
         vx0 + (fric * -sign_x)
       else
         x_halted = true
         x_stopping_distance = (vx0 ** 2) / (3 * friction)
         0
       end

  xt,yt,vxt,vyt = deduce_y_coordinate(vy,t,obstacles:obstacles) do |y,_vyt|
    if x_halted
      [x0 + (x_stopping_distance * sign_x), y, vx, _vyt]
    else
      deduce_x_coordinate(y,vx,t,obstacles:obstacles) do |x,_vxt|
        [x, y, _vxt, _vyt]
      end
    end
  end

  vxt = 0 if -epsilon < vx && vx < epsilon
  vyt = 0 if -epsilon < vy && vy < epsilon

  Body.new(
    position: [xt,yt],
    velocity: [vxt,vyt],
    dimensions: dimensions,
    t0: t
  )
end
width() click to toggle source
# File lib/physicist/body.rb, line 13
def width
  dimensions[0]
end

Private Instance Methods

deduce_x_coordinate(y,vx,t,obstacles:) { |x0| ... } click to toggle source
# File lib/physicist/body.rb, line 102
def deduce_x_coordinate(y,vx,t,obstacles:,&blk)
  x0,_ = *position
  dt = t - t0

  next_x_obstacle = next_obstacle_on_x_axis(y,vx,t,obstacles:obstacles)
  if next_x_obstacle
    ox,_ = *next_x_obstacle.position
    ow,_ = *next_x_obstacle.dimensions

    distance_to_next_x_obstacle =
      if vx > 0
        ((x0+3*(width/4.0)) - ox).abs
      elsif vx < 0
        ((x0+(width/4.0)) - (ox+ow)).abs
      end

    distance_travelled_in_x_axis_if_no_obstacles = vx * dt

    if distance_travelled_in_x_axis_if_no_obstacles.abs < distance_to_next_x_obstacle
      yield [x0 + (vx*dt), vx]
    else
      if vx > 0
        yield [next_x_obstacle.position[0]-3*(width/4.0)-epsilon, 0]
      else
        yield [next_x_obstacle.position[0]+next_x_obstacle.dimensions[0]-(width/4.0)+epsilon, 0]
      end
    end
  else
    yield [x0 + (vx*dt), vx]
  end
end
deduce_y_coordinate(vy,t,obstacles:) { |y0| ... } click to toggle source
# File lib/physicist/body.rb, line 134
def deduce_y_coordinate(vy,t,obstacles:,&blk)
  _,y0   = *position
  dt = t - t0

  next_y_obstacle = next_obstacle_on_y_axis(vy,t,obstacles:obstacles)

  if next_y_obstacle
    distance_to_next_y_obstacle =
      if vy > 0
        ((y0+height) - (next_y_obstacle.position[1] - epsilon)).abs
      else
        (y0 - (next_y_obstacle.position[1] + next_y_obstacle.dimensions[1])).abs
      end
    distance_travelled_in_y_axis_if_no_obstacles = (vy * dt)

    if distance_travelled_in_y_axis_if_no_obstacles.abs < distance_to_next_y_obstacle
      yield [y0 + (vy * dt), vy ]
    else
      if vy > 0
        yield [next_y_obstacle.position[1] - (height) - epsilon, 0]
      else
        yield [next_y_obstacle.position[1] + next_y_obstacle.dimensions[1] + epsilon, 0]
      end
    end
  else
    yield [y0 + (vy * dt), vy]
  end
end
next_obstacle_on_x_axis(y,vx,t,obstacles:) click to toggle source
# File lib/physicist/body.rb, line 163
def next_obstacle_on_x_axis(y,vx,t,obstacles:)
  x0,_ = *position

  w = (width/2.0)
  x0 += w/2.0

  obstacles_along_axis = obstacles.select do |obstacle|
    _,oy = *obstacle.position
    _,oh = *obstacle.dimensions

    oy <= y + height && y <= oy + oh - (2*epsilon)
  end

  obstacles_in_direction_of_movement =
    if vx > 0
      obstacles_along_axis.select do |obstacle|
        ox,_ = *obstacle.position

        ox >= x0 + w
      end
    elsif vx < 0
      obstacles_along_axis.select do |obstacle|
        ox,_ = *obstacle.position
        ow,_ = *obstacle.dimensions
        x0 >= ox + ow
      end
    else
      []
    end

  if obstacles_in_direction_of_movement.any?
    obstacles_in_direction_of_movement.min_by do |obstacle|
      ox,_ = *obstacle.position
      (x0 - ox).abs
    end
  else
    nil
  end
end
next_obstacle_on_y_axis(vy,t,obstacles:) click to toggle source
# File lib/physicist/body.rb, line 203
def next_obstacle_on_y_axis(vy,t,obstacles:)
  x0,y0 = *position

  w = width/2.0
  x0 += w/2.0

  obstacles_along_axis = obstacles.select do |obstacle|
    ox,_ = *obstacle.position
    ow,_ = *obstacle.dimensions
    ox <= x0 + w && x0 <= ox + ow
  end

  obstacles_in_direction_of_movement =
    if vy > 0
      obstacles_along_axis.select do |obstacle|
        _,oy = *obstacle.position

        oy >= y0 + height - epsilon
      end
    elsif vy < 0
      obstacles_along_axis.select do |obstacle|
        _,oy = *obstacle.position
        _,oh = *obstacle.dimensions

        oy + oh <= y0 + epsilon
      end
    else
      []
    end

  if obstacles_in_direction_of_movement
    obstacles_in_direction_of_movement.min_by do |obstacle|
      _,oy = *obstacle.position

      # distance to me
      (y0 - oy).abs
    end
  else
    nil
  end
end