Script taken from here. It calculates CNC miter pipe trajectories. See this for more reference.

#!/usr/bin/env ruby

# unit conversion constants
# internally, we compute everything in points == 1/72 inch
CM_PER_IN = 2.54
IN_PER_CM = 1.0 / CM_PER_IN
IN_PER_FT = 12.0
PTS_PER_IN = 72.0
CENTI = 100.0
MILLI = 1000.0
LENGTH_CONVERSION = {'in' =>                              PTS_PER_IN,
                     'ft' =>                  IN_PER_FT * PTS_PER_IN,
                     'mm' =>  CENTI / MILLI * IN_PER_CM * PTS_PER_IN,
                     'cm' =>                  IN_PER_CM * PTS_PER_IN,
                      'm' =>    CENTI / 1.0 * IN_PER_CM * PTS_PER_IN}
SUPPORTED = LENGTH_CONVERSION.keys.join ', '

EPSILON = 1.0e-3
TWOPI = 2.0 * Math::PI

PAPER_WIDTH  =  8.5 * LENGTH_CONVERSION['in']
PAPER_HEIGHT = 11.0 * LENGTH_CONVERSION['in']

MARGIN = 1 * LENGTH_CONVERSION['in']

def usage!
  $stderr.puts <<-Usage
    Usage: pipemiter [d1] [d2] [angle] [units]

    Where d1 is the larger outer diameter, d2 is smaller outer diameter,
    angle is the join angle in degrees, and units is one of
    {#{SUPPORTED}}.  Default unit is inches.

    Alternatively, use no arguments for interactive mode.

    Output is written as an encapsulated postscript document
    to standard out.
  Usage
  exit
end

if ARGV.include? '-h'
  usage!
elsif ARGV.size == 3 or ARGV.size == 4
  # command line mode
  d1_input = ARGV[0].to_f
  d2_input = ARGV[1].to_f
  angle_input = ARGV[2].to_f
  units_input = ARGV[3] || 'in'
else
  $stderr.print "Preferred unit (one of #{SUPPORTED}): "
  units_input = $stdin.readline
  $stderr.print "Outer Diameter of larger pipe: "
  d1_input = $stdin.readline.to_f
  $stderr.print "Outer Diameter of smaller pipe: "
  d2_input = $stdin.readline.to_f
  $stderr.print "Angle of joint (degrees): "
  angle_input = $stdin.readline.to_f
end

units = units_input.strip.downcase

unless LENGTH_CONVERSION.has_key? units
  $stderr.puts "Error: unsupported unit #{units_input}"
  usage!
end

d1_pts = LENGTH_CONVERSION[ units ] * d1_input
r1_pts = d1_pts / 2.0
d2_pts = LENGTH_CONVERSION[ units ] * d2_input
r2_pts = d2_pts / 2.0

unless d1_pts >= d2_pts
  $stderr.puts "Error: d1 must be greater than or equal to d2"
  usage!
end

# The circumference of the smaller pipe
c2_pts = TWOPI * r2_pts

if MARGIN + c2_pts + MARGIN > PAPER_HEIGHT
  $stderr.puts "Error: curve cannot be rendered on standard paper"
  usage!
end


angle_r = TWOPI * angle_input / 360.0

if angle_r <= 0.0 or angle_r >= 360.0
  $stderr.puts "Error: invalid angle (no parallel pipes allowed)."
  usage!
end

# Cool, all of the numbers look sane.

# We could do this the analytical way,
# but this is quick-n-dirty.

# The squared radius of the larger pipe
r1_sqr = r1_pts * r1_pts
r2_sqr = r2_pts * r2_pts

# The sine of the joint angle
sin_angle = Math::sin( angle_r )
cos_angle = Math::cos( angle_r )



xmin = Math::sqrt( r1_sqr - r2_sqr ) / cos_angle
ymin = 0
xmax = ( Math::sqrt( r1_sqr ) + r2_pts * sin_angle ) / cos_angle
ymax = c2_pts

width = xmax - xmin
height = ymax - ymin

puts <<-EPS_HEADER
  %!PS-Adobe EPSF-3.0
  %%BoundingBox: #{MARGIN} #{MARGIN} #{MARGIN+width} #{MARGIN+height}
  %%EndComments
  % www.cheaphack.net
  newpath
  1 setlinewidth
EPS_HEADER


cmd = "moveto"

# Make a loop around the smaller pipe
theta = 0.0
while theta < TWOPI
  # distance along the circumference of the smaller pipe
  d = c2_pts * theta / TWOPI

  # Any point with this angle shares these y,z coordinates
  y = r2_pts * Math::sin(theta)
  z = r2_pts * Math::cos(theta)

  x = ( Math::sqrt(r1_sqr - z*z) + y*sin_angle ) / cos_angle

  # Emit a line segment
  puts "#{MARGIN + x - xmin} #{MARGIN + d - ymin} #{cmd}"

  # Get ready for next iteration
  cmd = "lineto"
  theta += EPSILON
end

puts <<-EPS_TAILER
  stroke
  newpath 1 setlinewidth #{MARGIN} #{MARGIN} moveto #{width} 0 rlineto stroke
  newpath 1 setlinewidth #{MARGIN} #{MARGIN+height} moveto #{width} 0 rlineto stroke
  /Times-roman findfont 12 scalefont setfont #{MARGIN + width - 12} #{MARGIN + height/2} moveto 90 rotate (Keep) show
  showpage
  %%EOF
EPS_TAILER
Cookies help us deliver our services. By using our services, you agree to our use of cookies.