require_relative 'cluster_factory' require_relative 'point' module Geometry =begin rdoc A cluster of objects representing a Line of infinite length Supports two-point, slope-intercept, and point-slope initializer forms == Usage === Two-point constructors line = Geometry::Line[[0,0], [10,10]] line = Geometry::Line[Geometry::Point[0,0], Geometry::Point[10,10]] line = Geometry::Line[Vector[0,0], Vector[10,10]] === Slope-intercept constructors Geometry::Line[Rational(3,4), 5] # Slope = 3/4, Intercept = 5 Geometry::Line[0.75, 5] === Point-slope constructors Geometry::Line(Geometry::Point[0,0], 0.75) Geometry::Line(Vector[0,0], Rational(3,4)) === Special constructors (2D only) Geometry::Line.horizontal(y=0) Geometry::Line.vertical(x=0) =end ############ #/# Line #/# ############ class Line include ClusterFactory attr_writer :options def options @options = {} if !@options @options end # @overload [](Array, Array) # @return [TwoPointLine] # @overload [](Point, Point) # @return [TwoPointLine] # @overload [](Vector, Vector) # @return [TwoPointLine] # @overload [](y-intercept, slope) # @return [SlopeInterceptLine] # @overload [](point, slope) # @return [PointSlopeLine] def self.[](*args) if( 2 == args.size ) args.map! {|x| x.is_a?(Array) ? Point[*x] : x} # If both args are Points, create a TwoPointLine return TwoPointLine.new(*args) if args.all? {|x| x.is_a?(Vector)} # If only the first arg is a Point, create a PointSlopeLine return PointSlopeLine.new(*args) if args.first.is_a?(Vector) # Otherise, create a SlopeInterceptLine return SlopeInterceptLine.new(*args) else nil end end # @overload new(from, to) # @option options [Point] :from A starting {Point} # @option options [Point] :to An end {Point} # @return [TwoPointLine] # @overload new(start, end) # @option options [Point] :start A starting {Point} # @option options [Point] :end An end {Point} # @return [TwoPointLine] def self.new(options={}) from = options[:from] || options[:start] to = options[:end] || options[:to] if from and to TwoPointLine.new(from, to) else raise ArgumentError, "Start and end Points must be provided" end end def self.horizontal(y_intercept=0) SlopeInterceptLine.new(0, y_intercept) end def self.vertical(x_intercept=0) SlopeInterceptLine.new(1/0.0, x_intercept) end end ###################### #/# PointSlopeLine #/# ###################### # @private class PointSlopeLine < Line # @return [Number] the slope of the {Line} attr_reader :slope def initialize(point, slope) @point = Point[point] @slope = slope end def to_s 'Line(' + @slope.to_s + ',' + @point.to_s + ')' end end ########################## #/# SlopeInterceptLine #/# ########################## # @private class SlopeInterceptLine < Line # @return [Number] the slope of the {Line} attr_reader :slope def initialize(slope, intercept) @slope = slope @intercept = intercept end def horizontal? 0 == @slope end def vertical? (1/0.0) == @slope end def intercept(axis=:y) case axis when :x vertical? ? @intercept : (horizontal? ? nil : (-@intercept/@slope)) when :y vertical? ? nil : @intercept end end def to_s 'Line(' + @slope.to_s + ',' + @intercept.to_s + ')' end end #################### #/# TwoPointLine #/# #################### # @private class TwoPointLine < Line attr_reader :first, :last def initialize(point0, point1) @first, @last = [Point[point0], Point[point1]] end def inspect 'Line(' + @first.inspect + ', ' + @last.inspect + ')' end alias :to_s :inspect # @group Accessors # !@attribute [r[ slope # @return [Number] the slope of the {Line} def slope (last.y - first.y)/(last.x - first.x) end # @endgroup end end