Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Created November 25, 2016 09:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshCheek/86d36548336d5f2bb0b0d1e01c0a28a2 to your computer and use it in GitHub Desktop.
Save JoshCheek/86d36548336d5f2bb0b0d1e01c0a28a2 to your computer and use it in GitHub Desktop.
Given a point, a line, and a distance, calculate the two points on that line that are that distance away.
# encoding: utf-8
# Given a point, a line, and a distance, calculate the two points on that line that are that distance away.
# Relevant equations
# eqn for line (m here is our gslope, b is the y-intercept)
# y = mx + b
# pythagorean theorem from origin (r, the radius, will be our gdist, the distance between the points)
# r**2 = x**2 + y**2
# Make floats less irritating to look at
class Float
def inspect
sprintf('%f', self).gsub(/\.?0+$/, '') # => "6", "2", "6", "8", "1.333333", "12", "10", "0", "-6", "10", "10", "10", "12", "10", "0", "-6", "10", "12", "10", "0", "-6", "10", "12", "10", "0", "-6", "10", "12", "10", "0", "-6", "10", "6", "8", "12", "10", "0", "-6", "6", "8", "12", "10", "0", "-6", "6", "8", "6", "8", "12", "10", "0", "-6", "6", "8", "12", "10", "0", "-6", "6", "8", "12", "10", "0", "-6"
end # => :inspect
end # => :inspect
# make math functions more convenient
include Math # => Object
alias √ sqrt
# GIVENS (indicated by starting the var name w/ a g)
gx1, gy1 = 6.0, 2.0 # => [6, 2]
g∆x, g∆y = 6.0, 8.0 # => [6, 8]
gslope = g∆y / g∆x # => 1.333333
gdist = 10 # => 10
gx2, gy2 = 12, 10 # => [12, 10]
gx3, gy3 = 0, -6 # => [0, -6]
# An assertion function to explode if we get it wrong
define_method :assert do |named_values|
named_values.each do |name, calculated| # => {:gx2=>12, :gy2=>10, :gx3=>0, :gy3=>-6, :gdist=>10}, {:"g\u2206x"=>6, :"g\u2206y"=>8, :gx2=>12, :gy2=>10, :gx3=>0, :gy3=>-6}
expected = binding.local_variable_get name # => 12, 10, 0, -6, 10, 6, 8, 12, 10, 0, -6
next if calculated == expected # => true, true, true, true, true, true, true, true, true, true, true
raise "Expected #{name} = #{expected.inspect} but it calculated to #{calculated.inspect}"
end # => {:gx2=>12, :gy2=>10, :gx3=>0, :gy3=>-6, :gdist=>10}, {:"g\u2206x"=>6, :"g\u2206y"=>8, :gx2=>12, :gy2=>10, :gx3=>0, :gy3=>-6}
end # => :assert
# Show we can find calculate the distance
lambda do
assert(
gx2: x2 = gx1 + g∆x, # => 12
gy2: y2 = gy1 + g∆y, # => 10
gx3: x3 = gx1 - g∆x, # => 0
gy3: y3 = gy1 - g∆y, # => -6
gdist: √( (x2 - gx1)**2 + (y2 - gy1)**2 ), # => 10
gdist: √( (x3 - gx1)**2 + (y3 - gy1)**2 ), # => 10
gdist: √( (g∆x)**2 + (g∆y)**2 ), # => 10
) # => {:gx2=>12, :gy2=>10, :gx3=>0, :gy3=>-6, :gdist=>10}
end.call # => {:gx2=>12, :gy2=>10, :gx3=>0, :gy3=>-6, :gdist=>10}
# Show we can find (x2, y2) and (x3, y3)
lambda do
# Points on that line will satisfy
# y = gslope * x + b
# The points will be gdist from (gx1, gy1),
# thus they make a circle around that point
# We'll translate (x1, y1) to the origin to solve
# Thus there is a circle of possible values given by
# gdist**2 = x**2 + y**2
# Solve for x:
# gdist**2 = x**2 + (gslope * x)**2
# = x**2 + gslope**2 * x**2
# = x**2 (1 + gslope**2)
# x**2 = gdist**2 / (1 + gslope**2)
# x = ±√(gdist**2 / (1 + gslope**2))
# Since we translated x1 to the origin, the x we found is ∆x
# x = (x2 - x1)
# = ∆x
# Thus
# ∆x = ±√(gdist**2 / (1 + gslope**2))
# And the rest is trivial
assert(
g∆x: ∆x = √(gdist**2 / (gslope**2 + 1)), # => 6
g∆y: ∆y = gslope * ∆x, # => 8
gx2: gx1 + ∆x, # => 12
gy2: gy1 + ∆y, # => 10
gx3: gx1 - ∆x, # => 0
gy3: gy1 - ∆y, # => -6
) # => {:"g\u2206x"=>6, :"g\u2206y"=>8, :gx2=>12, :gy2=>10, :gx3=>0, :gy3=>-6}
end.call # => {:"g\u2206x"=>6, :"g\u2206y"=>8, :gx2=>12, :gy2=>10, :gx3=>0, :gy3=>-6}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment