geometry 3 → 4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +10 -1
 - data/Rakefile +11 -0
 - data/geometry.gemspec +1 -1
 - data/lib/geometry.rb +8 -1
 - data/lib/geometry/arc.rb +31 -0
 - data/lib/geometry/circle.rb +1 -1
 - data/lib/geometry/edge.rb +1 -1
 - data/lib/geometry/line.rb +2 -2
 - data/lib/geometry/point.rb +67 -7
 - data/lib/geometry/point_zero.rb +69 -0
 - data/lib/geometry/polygon.rb +1 -0
 - data/lib/geometry/rectangle.rb +38 -10
 - data/lib/geometry/rotation.rb +46 -0
 - data/lib/geometry/size_zero.rb +69 -0
 - data/lib/geometry/square.rb +103 -0
 - data/lib/geometry/transformation.rb +106 -0
 - data/lib/geometry/vector.rb +14 -0
 - data/test/{test_geometry.rb → geometry.rb} +6 -5
 - data/test/geometry/arc.rb +13 -0
 - data/test/geometry/circle.rb +6 -6
 - data/test/geometry/edge.rb +15 -15
 - data/test/{test_line.rb → geometry/line.rb} +18 -18
 - data/test/geometry/point.rb +135 -46
 - data/test/geometry/point_zero.rb +151 -0
 - data/test/geometry/polygon.rb +10 -7
 - data/test/geometry/rectangle.rb +60 -55
 - data/test/geometry/rotation.rb +35 -0
 - data/test/geometry/size.rb +46 -46
 - data/test/geometry/size_zero.rb +151 -0
 - data/test/geometry/square.rb +56 -0
 - data/test/geometry/transformation.rb +90 -0
 - data/test/geometry/vector.rb +17 -0
 - metadata +28 -11
 - data/test/helper.rb +0 -2
 - data/test/test_unit_extensions.rb +0 -23
 
| 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Geometry
         
     | 
| 
      
 2 
     | 
    
         
            +
            =begin
         
     | 
| 
      
 3 
     | 
    
         
            +
            A generalized representation of a rotation transformation.
         
     | 
| 
      
 4 
     | 
    
         
            +
            =end
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Rotation
         
     | 
| 
      
 6 
     | 
    
         
            +
            	# !@attribute [r] dimensions
         
     | 
| 
      
 7 
     | 
    
         
            +
            	# @return [Integer]
         
     | 
| 
      
 8 
     | 
    
         
            +
            	attr_reader :dimensions
         
     | 
| 
      
 9 
     | 
    
         
            +
            	attr_reader :x, :y, :z
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            	# @overload initialize(options={})
         
     | 
| 
      
 12 
     | 
    
         
            +
            	# @option options [Integer]	:dimensions Dimensionality of the rotation
         
     | 
| 
      
 13 
     | 
    
         
            +
            	# @option options [Vector]	:x	    X-axis
         
     | 
| 
      
 14 
     | 
    
         
            +
            	# @option options [Vector]	:y	    Y-axis
         
     | 
| 
      
 15 
     | 
    
         
            +
            	# @option options [Vector]	:z	    Z-axis
         
     | 
| 
      
 16 
     | 
    
         
            +
            	def initialize(*args)
         
     | 
| 
      
 17 
     | 
    
         
            +
            	    options, args = args.partition {|a| a.is_a? Hash}
         
     | 
| 
      
 18 
     | 
    
         
            +
            	    options = options.reduce({}, :merge)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            	    @dimensions = options[:dimensions] || nil
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            	    axis_options = [options[:x], options[:y], options[:z]]
         
     | 
| 
      
 23 
     | 
    
         
            +
            	    all_axes_options = [options[:x], options[:y], options[:z]].select {|a| a}
         
     | 
| 
      
 24 
     | 
    
         
            +
            	    if all_axes_options.count != 0
         
     | 
| 
      
 25 
     | 
    
         
            +
            		@x = options[:x] || nil
         
     | 
| 
      
 26 
     | 
    
         
            +
            		@y = options[:y] || nil
         
     | 
| 
      
 27 
     | 
    
         
            +
            		@z = options[:z] || nil
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            		raise ArgumentError, "All axis options must be Vectors" unless all_axes_options.all? {|a| a.is_a?(Vector) or a.is_a?(Array) }
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            		raise ArgumentError, "All provided axes must be the same size" unless all_axes_options.all? {|a| a.size == all_axes_options.first.size}
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            		@dimensions ||= all_axes_options.first.size
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            		raise ArgumentError, "Dimensionality mismatch" unless all_axes_options.first.size <= @dimensions
         
     | 
| 
      
 36 
     | 
    
         
            +
            		if all_axes_options.first.size < @dimensions
         
     | 
| 
      
 37 
     | 
    
         
            +
            		    @x, @y, @z = [@x, @y, @z].map {|a| (a && (a.size < @dimensions)) ? Array.new(@dimensions) {|i| a[i] || 0 } : a }
         
     | 
| 
      
 38 
     | 
    
         
            +
            		end
         
     | 
| 
      
 39 
     | 
    
         
            +
            	    end
         
     | 
| 
      
 40 
     | 
    
         
            +
            	end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            	def identity?
         
     | 
| 
      
 43 
     | 
    
         
            +
            	    (!@x && !@y && !@z) || ([@x, @y, @z].select {|a| a}.all? {|a| a.respond_to?(:magnitude) ? (1 == a.magnitude) : (1 == a.size)})
         
     | 
| 
      
 44 
     | 
    
         
            +
            	end
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,69 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative 'point'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Geometry
         
     | 
| 
      
 4 
     | 
    
         
            +
            =begin rdoc
         
     | 
| 
      
 5 
     | 
    
         
            +
            An object repesenting a zero {Size} in N-dimensional space
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            A {SizeZero} object is a {Size} that will always compare equal to zero and unequal to
         
     | 
| 
      
 8 
     | 
    
         
            +
            everything else, regardless of dimensionality.
         
     | 
| 
      
 9 
     | 
    
         
            +
            =end
         
     | 
| 
      
 10 
     | 
    
         
            +
                class SizeZero
         
     | 
| 
      
 11 
     | 
    
         
            +
            	def eql?(other)
         
     | 
| 
      
 12 
     | 
    
         
            +
            	    if other.respond_to? :all?
         
     | 
| 
      
 13 
     | 
    
         
            +
            		other.all? {|e| e.eql? 0}
         
     | 
| 
      
 14 
     | 
    
         
            +
            	    else
         
     | 
| 
      
 15 
     | 
    
         
            +
            		other == 0
         
     | 
| 
      
 16 
     | 
    
         
            +
            	    end
         
     | 
| 
      
 17 
     | 
    
         
            +
            	end
         
     | 
| 
      
 18 
     | 
    
         
            +
            	alias == eql?
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            	def coerce(other)
         
     | 
| 
      
 21 
     | 
    
         
            +
            	    if other.is_a? Numeric
         
     | 
| 
      
 22 
     | 
    
         
            +
            		[other, 0]
         
     | 
| 
      
 23 
     | 
    
         
            +
            	    elsif other.is_a? Array
         
     | 
| 
      
 24 
     | 
    
         
            +
            		[other, Array.new(other.size,0)]
         
     | 
| 
      
 25 
     | 
    
         
            +
            	    elsif other.is_a? Vector
         
     | 
| 
      
 26 
     | 
    
         
            +
            		[other, Vector[*Array.new(other.size,0)]]
         
     | 
| 
      
 27 
     | 
    
         
            +
            	    else
         
     | 
| 
      
 28 
     | 
    
         
            +
            		[Size[other], Size[Array.new(other.size,0)]]
         
     | 
| 
      
 29 
     | 
    
         
            +
            	    end
         
     | 
| 
      
 30 
     | 
    
         
            +
            	end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            # !@group Arithmetic
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            # !@group Unary operators
         
     | 
| 
      
 35 
     | 
    
         
            +
            	def +@
         
     | 
| 
      
 36 
     | 
    
         
            +
            	    self
         
     | 
| 
      
 37 
     | 
    
         
            +
            	end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            	def -@
         
     | 
| 
      
 40 
     | 
    
         
            +
            	    self
         
     | 
| 
      
 41 
     | 
    
         
            +
            	end
         
     | 
| 
      
 42 
     | 
    
         
            +
            # !@endgroup
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            	def +(other)
         
     | 
| 
      
 45 
     | 
    
         
            +
            	    other
         
     | 
| 
      
 46 
     | 
    
         
            +
            	end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            	def -(other)
         
     | 
| 
      
 49 
     | 
    
         
            +
            	    if other.respond_to? :-@
         
     | 
| 
      
 50 
     | 
    
         
            +
            		-other
         
     | 
| 
      
 51 
     | 
    
         
            +
            	    elsif other.respond_to? :map
         
     | 
| 
      
 52 
     | 
    
         
            +
            		other.map {|a| -a }
         
     | 
| 
      
 53 
     | 
    
         
            +
            	    end
         
     | 
| 
      
 54 
     | 
    
         
            +
            	end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            	def *(other)
         
     | 
| 
      
 57 
     | 
    
         
            +
            	    self
         
     | 
| 
      
 58 
     | 
    
         
            +
            	end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
            	def /(other)
         
     | 
| 
      
 61 
     | 
    
         
            +
            	    raise OperationNotDefined unless other.is_a? Numeric
         
     | 
| 
      
 62 
     | 
    
         
            +
            	    raise ZeroDivisionError if 0 == other
         
     | 
| 
      
 63 
     | 
    
         
            +
            	    self
         
     | 
| 
      
 64 
     | 
    
         
            +
            	end
         
     | 
| 
      
 65 
     | 
    
         
            +
            # !@endgroup
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
            end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,103 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative 'point'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Geometry
         
     | 
| 
      
 4 
     | 
    
         
            +
                NotSquareError = Class.new(ArgumentError)
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            =begin
         
     | 
| 
      
 7 
     | 
    
         
            +
            The {Square} class cluster is like the {Rectangle} class cluster, but not longer in one direction.
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            == Constructors
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                square = Square.new [1,2], [2,3]	# Using two corners
         
     | 
| 
      
 12 
     | 
    
         
            +
                square = Square.new [3,4], 5	# Using an origin point and a size
         
     | 
| 
      
 13 
     | 
    
         
            +
                square = Square.new 6		# Using a size and an origin of [0,0]
         
     | 
| 
      
 14 
     | 
    
         
            +
            =end
         
     | 
| 
      
 15 
     | 
    
         
            +
                class Square
         
     | 
| 
      
 16 
     | 
    
         
            +
            	attr_reader :origin
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            	# Creates a {Square} given two {Point}s
         
     | 
| 
      
 19 
     | 
    
         
            +
            	# @param [Point] point0	A corner (ie. bottom-left)
         
     | 
| 
      
 20 
     | 
    
         
            +
            	# @param [Point] point1	The other corner (ie. top-right)
         
     | 
| 
      
 21 
     | 
    
         
            +
            	def initialize(point0, point1)
         
     | 
| 
      
 22 
     | 
    
         
            +
            	    point0, point1 = Point[point0], Point[point1]
         
     | 
| 
      
 23 
     | 
    
         
            +
            	    raise(ArgumentError, "Point sizes must match (#{point0.size} != #{point1.size}") unless point0.size == point1.size
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            	    # Reorder the points to get lower-left and upper-right
         
     | 
| 
      
 26 
     | 
    
         
            +
            	    minx, maxx = [point0.x, point1.x].minmax
         
     | 
| 
      
 27 
     | 
    
         
            +
            	    miny, maxy = [point0.y, point1.y].minmax
         
     | 
| 
      
 28 
     | 
    
         
            +
            	    @points = [Point[minx, miny], Point[maxx, maxy]]
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            	    raise(NotSquareError) if height != width
         
     | 
| 
      
 31 
     | 
    
         
            +
            	end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            # !@group Accessors
         
     | 
| 
      
 34 
     | 
    
         
            +
            	# !@attribute [r] origin
         
     | 
| 
      
 35 
     | 
    
         
            +
            	# @return [Point] The lower left corner
         
     | 
| 
      
 36 
     | 
    
         
            +
            	def origin
         
     | 
| 
      
 37 
     | 
    
         
            +
            	    @points.first
         
     | 
| 
      
 38 
     | 
    
         
            +
            	end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            	def height
         
     | 
| 
      
 41 
     | 
    
         
            +
            	    min, max = @points.minmax {|a,b| a.y <=> b.y}
         
     | 
| 
      
 42 
     | 
    
         
            +
            	    max.y - min.y
         
     | 
| 
      
 43 
     | 
    
         
            +
            	end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
            	def width
         
     | 
| 
      
 46 
     | 
    
         
            +
            	    min, max = @points.minmax {|a,b| a.x <=> b.x}
         
     | 
| 
      
 47 
     | 
    
         
            +
            	    max.x - min.x
         
     | 
| 
      
 48 
     | 
    
         
            +
            	end
         
     | 
| 
      
 49 
     | 
    
         
            +
            # !@endgroup
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                # A {Square} created with a center point and a size
         
     | 
| 
      
 53 
     | 
    
         
            +
                class CenteredSquare < Square
         
     | 
| 
      
 54 
     | 
    
         
            +
            	# !@attribute [r] center
         
     | 
| 
      
 55 
     | 
    
         
            +
            	# @return [Point]   The center of the {Square}
         
     | 
| 
      
 56 
     | 
    
         
            +
            	attr_reader :center
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
            	# @param [Point]	center	The center point
         
     | 
| 
      
 59 
     | 
    
         
            +
            	# @param [Numeric]	size	The length of each side
         
     | 
| 
      
 60 
     | 
    
         
            +
            	def initialize(center, size)
         
     | 
| 
      
 61 
     | 
    
         
            +
            	    @center = Point[center]
         
     | 
| 
      
 62 
     | 
    
         
            +
            	    @size = size
         
     | 
| 
      
 63 
     | 
    
         
            +
            	end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
            # !@group Accessors
         
     | 
| 
      
 66 
     | 
    
         
            +
            	# !@attribute [r] origin
         
     | 
| 
      
 67 
     | 
    
         
            +
            	# @return [Point] The lower left corner
         
     | 
| 
      
 68 
     | 
    
         
            +
            	def origin
         
     | 
| 
      
 69 
     | 
    
         
            +
            	    Point[@center.x - size/2, @center.y - size/2]
         
     | 
| 
      
 70 
     | 
    
         
            +
            	end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
            	# !@attribute [r] points
         
     | 
| 
      
 73 
     | 
    
         
            +
            	# @return [Array<Point>]    The {Square}'s four points (clockwise)
         
     | 
| 
      
 74 
     | 
    
         
            +
            	def points
         
     | 
| 
      
 75 
     | 
    
         
            +
            	    half_size = @size/2
         
     | 
| 
      
 76 
     | 
    
         
            +
            	    minx = @center.x - half_size
         
     | 
| 
      
 77 
     | 
    
         
            +
            	    maxx = @center.x + half_size
         
     | 
| 
      
 78 
     | 
    
         
            +
            	    miny = @center.y - half_size
         
     | 
| 
      
 79 
     | 
    
         
            +
            	    maxy = @center.y + half_size
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
            	    [Point[minx,miny], Point[minx,maxy], Point[maxx, maxy], Point[maxx, miny]]
         
     | 
| 
      
 82 
     | 
    
         
            +
            	end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
            	def height
         
     | 
| 
      
 85 
     | 
    
         
            +
            	    @size
         
     | 
| 
      
 86 
     | 
    
         
            +
            	end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
            	def width
         
     | 
| 
      
 89 
     | 
    
         
            +
            	    @size
         
     | 
| 
      
 90 
     | 
    
         
            +
            	end
         
     | 
| 
      
 91 
     | 
    
         
            +
            # !@endgroup
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                # A {Square} created with an origin point and a size
         
     | 
| 
      
 95 
     | 
    
         
            +
                class SizedSquare < Square
         
     | 
| 
      
 96 
     | 
    
         
            +
            	# @param [Point]    origin  The origin point (bottom-left corner)
         
     | 
| 
      
 97 
     | 
    
         
            +
            	# @param [Numeric]  size    The length of each side
         
     | 
| 
      
 98 
     | 
    
         
            +
            	def initialize(origin, size)
         
     | 
| 
      
 99 
     | 
    
         
            +
            	    @origin = Point[origin]
         
     | 
| 
      
 100 
     | 
    
         
            +
            	    @size = size
         
     | 
| 
      
 101 
     | 
    
         
            +
            	end
         
     | 
| 
      
 102 
     | 
    
         
            +
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,106 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'geometry/point'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'geometry/rotation'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Geometry
         
     | 
| 
      
 5 
     | 
    
         
            +
            =begin
         
     | 
| 
      
 6 
     | 
    
         
            +
            {Transformation} represents a relationship between two coordinate frames
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            To create a pure translation relationship:
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                translate = Geometry::Transformation.new(:translate => Point[4, 2])
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            To create a transformation with an origin and an X-axis aligned with the parent
         
     | 
| 
      
 13 
     | 
    
         
            +
            coordinate system's Y-axis (the Y and Z axes will be chosen arbitrarily):
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                translate = Geometry::Transformation.new(:origin => [4, 2], :x => [0,1,0])
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            To create a transformation with an origin, an X-axis aligned with the parent
         
     | 
| 
      
 18 
     | 
    
         
            +
            coordinate system's Y-axis, and a Y-axis aligned with the parent coordinate
         
     | 
| 
      
 19 
     | 
    
         
            +
            system's X-axis:
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                translate = Geometry::Transformation.new(:origin => [4, 2], :x => [0,1,0], :y => [1,0,0])
         
     | 
| 
      
 22 
     | 
    
         
            +
            =end
         
     | 
| 
      
 23 
     | 
    
         
            +
                class Transformation
         
     | 
| 
      
 24 
     | 
    
         
            +
            	attr_reader :dimensions
         
     | 
| 
      
 25 
     | 
    
         
            +
                	attr_reader :rotation
         
     | 
| 
      
 26 
     | 
    
         
            +
                	attr_reader :scale
         
     | 
| 
      
 27 
     | 
    
         
            +
                	attr_reader :translation
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            	attr_reader :x_axis, :y_axis, :z_axis
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                	# @overload new(translate, rotate, scale)
         
     | 
| 
      
 32 
     | 
    
         
            +
                	# @param [Point] translate	Linear displacement
         
     | 
| 
      
 33 
     | 
    
         
            +
                	# @param [Rotation] rotate	Rotation
         
     | 
| 
      
 34 
     | 
    
         
            +
                	# @param [Vector]   scale	Scaling
         
     | 
| 
      
 35 
     | 
    
         
            +
                	# @overload new(options)
         
     | 
| 
      
 36 
     | 
    
         
            +
                	# @param [Hash] options
         
     | 
| 
      
 37 
     | 
    
         
            +
            	# @option options [Integer]	:dimensions Dimensionality of the transformation
         
     | 
| 
      
 38 
     | 
    
         
            +
                	# @option options [Point]	:origin	    Same as :translate
         
     | 
| 
      
 39 
     | 
    
         
            +
                	# @option options [Point]	:move	    Same as :translate
         
     | 
| 
      
 40 
     | 
    
         
            +
                	# @option options [Point]	:translate  Linear displacement
         
     | 
| 
      
 41 
     | 
    
         
            +
                	# @option options [Rotation]	:rotate	    Rotation
         
     | 
| 
      
 42 
     | 
    
         
            +
                	# @option options [Vector]	:scale	    Scaling
         
     | 
| 
      
 43 
     | 
    
         
            +
            	# @option options [Vector]	:x	    X-axis
         
     | 
| 
      
 44 
     | 
    
         
            +
            	# @option options [Vector]	:y	    Y-axis
         
     | 
| 
      
 45 
     | 
    
         
            +
            	# @option options [Vector]	:z	    Z-axis
         
     | 
| 
      
 46 
     | 
    
         
            +
            	def initialize(*args)
         
     | 
| 
      
 47 
     | 
    
         
            +
            	    options, args = args.partition {|a| a.is_a? Hash}
         
     | 
| 
      
 48 
     | 
    
         
            +
            	    translate, rotate, scale = args
         
     | 
| 
      
 49 
     | 
    
         
            +
            	    options = options.reduce({}, :merge)
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
            	    @dimensions = options[:dimensions] || nil
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
            	    @rotation = options[:rotate] || rotate || Geometry::Rotation.new(options)
         
     | 
| 
      
 54 
     | 
    
         
            +
            	    @scale = options[:scale] || scale
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            	    case options.count {|k,v| [:move, :origin, :translate].include? k }
         
     | 
| 
      
 57 
     | 
    
         
            +
            		when 0
         
     | 
| 
      
 58 
     | 
    
         
            +
            		    @translation = translate
         
     | 
| 
      
 59 
     | 
    
         
            +
            		when 1
         
     | 
| 
      
 60 
     | 
    
         
            +
            		    @translation = (options[:translate] ||= options.delete(:move) || options.delete(:origin))
         
     | 
| 
      
 61 
     | 
    
         
            +
            		else
         
     | 
| 
      
 62 
     | 
    
         
            +
            		    raise ArgumentError, "Too many translation parameters in #{options}"
         
     | 
| 
      
 63 
     | 
    
         
            +
            	    end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
            	    @translation = Point[*@translation]
         
     | 
| 
      
 66 
     | 
    
         
            +
            	    if @translation
         
     | 
| 
      
 67 
     | 
    
         
            +
            		@translation = nil if @translation.all? {|v| v == 0}
         
     | 
| 
      
 68 
     | 
    
         
            +
            		raise ArgumentError, ":translate must be a Point or a Vector" if @translation and not @translation.is_a?(Vector)
         
     | 
| 
      
 69 
     | 
    
         
            +
            	    end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
            	    if @dimensions
         
     | 
| 
      
 72 
     | 
    
         
            +
            		biggest = [@translation, @scale].select {|a| a}.map {|a| a.size}.max
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
            		if biggest and (biggest != 0) and (((biggest != @dimensions)) or (@rotation and (@rotation.dimensions != biggest)))
         
     | 
| 
      
 75 
     | 
    
         
            +
            		    raise ArgumentError, "Dimensionality mismatch"
         
     | 
| 
      
 76 
     | 
    
         
            +
            		end
         
     | 
| 
      
 77 
     | 
    
         
            +
            	    end
         
     | 
| 
      
 78 
     | 
    
         
            +
            	end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
            	# Returns true if the {Transformation} is the identity transformation
         
     | 
| 
      
 81 
     | 
    
         
            +
            	def identity?
         
     | 
| 
      
 82 
     | 
    
         
            +
            	    @rotation.identity? && !(@scale || @translation)
         
     | 
| 
      
 83 
     | 
    
         
            +
            	end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
            	# Compose the current {Transformation} with another one
         
     | 
| 
      
 86 
     | 
    
         
            +
            	def +(other)
         
     | 
| 
      
 87 
     | 
    
         
            +
            	    if other.is_a?(Array) or other.is_a?(Vector)
         
     | 
| 
      
 88 
     | 
    
         
            +
            		if @translation
         
     | 
| 
      
 89 
     | 
    
         
            +
            		    Transformation.new(@translation+other, @rotation, @scale)
         
     | 
| 
      
 90 
     | 
    
         
            +
            		else
         
     | 
| 
      
 91 
     | 
    
         
            +
            		    Transformation.new(other, @rotation, @scale)
         
     | 
| 
      
 92 
     | 
    
         
            +
            		end
         
     | 
| 
      
 93 
     | 
    
         
            +
            	    end
         
     | 
| 
      
 94 
     | 
    
         
            +
            	end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
            	def -(other)
         
     | 
| 
      
 97 
     | 
    
         
            +
            	    if other.is_a?(Array) or other.is_a?(Vector)
         
     | 
| 
      
 98 
     | 
    
         
            +
            		if @translation
         
     | 
| 
      
 99 
     | 
    
         
            +
            		    Transformation.new(@translation-other, @rotation, @scale)
         
     | 
| 
      
 100 
     | 
    
         
            +
            		else
         
     | 
| 
      
 101 
     | 
    
         
            +
            		    Transformation.new(other.map {|e| -e}, @rotation, @scale)
         
     | 
| 
      
 102 
     | 
    
         
            +
            		end
         
     | 
| 
      
 103 
     | 
    
         
            +
            	    end
         
     | 
| 
      
 104 
     | 
    
         
            +
            	end
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,12 +1,13 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            require 'minitest/autorun'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'geometry'
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
                 
     | 
| 
      
 4 
     | 
    
         
            +
            describe Geometry do
         
     | 
| 
      
 5 
     | 
    
         
            +
                it "create a Point object" do
         
     | 
| 
       6 
6 
     | 
    
         
             
            	point = Geometry.point(2,1)
         
     | 
| 
       7 
7 
     | 
    
         
             
            	assert_kind_of(Geometry::Point, point)
         
     | 
| 
       8 
8 
     | 
    
         
             
                end
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                it "create a Line object" do
         
     | 
| 
       10 
11 
     | 
    
         
             
            	line = Geometry.line([0,0], [10,10])
         
     | 
| 
       11 
12 
     | 
    
         
             
            	assert_kind_of(Geometry::Line, line)
         
     | 
| 
       12 
13 
     | 
    
         
             
            	assert_kind_of(Geometry::TwoPointLine, line)
         
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'minitest/autorun'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'geometry/arc'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            describe Geometry::Arc do
         
     | 
| 
      
 5 
     | 
    
         
            +
                it "must create an Arc object from a Point and a radius" do
         
     | 
| 
      
 6 
     | 
    
         
            +
            	arc = Geometry::Arc.new [1,2], 3, 0, 90
         
     | 
| 
      
 7 
     | 
    
         
            +
            	arc.must_be_kind_of Geometry::Arc
         
     | 
| 
      
 8 
     | 
    
         
            +
            	arc.center.must_equal Point[1,2]
         
     | 
| 
      
 9 
     | 
    
         
            +
            	arc.radius.must_equal 3
         
     | 
| 
      
 10 
     | 
    
         
            +
            	arc.start_angle.must_equal 0
         
     | 
| 
      
 11 
     | 
    
         
            +
            	arc.end_angle.must_equal 90
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
    
        data/test/geometry/circle.rb
    CHANGED
    
    | 
         @@ -1,22 +1,22 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            require 'minitest/autorun'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'geometry/circle'
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            def Circle(*args)
         
     | 
| 
       5 
5 
     | 
    
         
             
                Geometry::Circle.new(*args)
         
     | 
| 
       6 
6 
     | 
    
         
             
            end
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
                must  
     | 
| 
      
 8 
     | 
    
         
            +
            describe Geometry::Circle do
         
     | 
| 
      
 9 
     | 
    
         
            +
                it "must create a Circle object from a Point and a radius" do
         
     | 
| 
       10 
10 
     | 
    
         
             
            	circle = Circle [1,2], 3
         
     | 
| 
       11 
11 
     | 
    
         
             
            	assert_kind_of(Geometry::Circle, circle)
         
     | 
| 
       12 
12 
     | 
    
         
             
                end
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
                must  
     | 
| 
      
 14 
     | 
    
         
            +
                it "must have a center point accessor" do
         
     | 
| 
       15 
15 
     | 
    
         
             
            	circle = Circle [1,2], 3
         
     | 
| 
       16 
16 
     | 
    
         
             
            	assert_equal(circle.center, [1,2])
         
     | 
| 
       17 
17 
     | 
    
         
             
                end
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
                must  
     | 
| 
      
 19 
     | 
    
         
            +
                it "must have a radius accessor" do
         
     | 
| 
       20 
20 
     | 
    
         
             
            	circle = Circle [1,2], 3
         
     | 
| 
       21 
21 
     | 
    
         
             
            	assert_equal(3, circle.radius)
         
     | 
| 
       22 
22 
     | 
    
         
             
                end
         
     | 
    
        data/test/geometry/edge.rb
    CHANGED
    
    | 
         @@ -1,40 +1,40 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            require 'minitest/autorun'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'geometry/edge'
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            def Edge(*args)
         
     | 
| 
       5 
5 
     | 
    
         
             
                Geometry::Edge.new(*args)
         
     | 
| 
       6 
6 
     | 
    
         
             
            end
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
      
 8 
     | 
    
         
            +
            describe Geometry::Edge do
         
     | 
| 
       9 
9 
     | 
    
         
             
                Edge = Geometry::Edge
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
                must  
     | 
| 
       12 
     | 
    
         
            -
            	edge =  
     | 
| 
      
 11 
     | 
    
         
            +
                it "must create an Edge object" do
         
     | 
| 
      
 12 
     | 
    
         
            +
            	edge = Edge.new([0,0], [1,0])
         
     | 
| 
       13 
13 
     | 
    
         
             
            	assert_kind_of(Geometry::Edge, edge)
         
     | 
| 
       14 
14 
     | 
    
         
             
            	assert_equal(Geometry::Point[0,0], edge.first)
         
     | 
| 
       15 
15 
     | 
    
         
             
            	assert_equal(Geometry::Point[1,0], edge.last)
         
     | 
| 
       16 
16 
     | 
    
         
             
                end
         
     | 
| 
       17 
     | 
    
         
            -
                must  
     | 
| 
       18 
     | 
    
         
            -
            	edge =  
     | 
| 
       19 
     | 
    
         
            -
            	assert_kind_of( 
     | 
| 
      
 17 
     | 
    
         
            +
                it "must create swap endpoints in place" do
         
     | 
| 
      
 18 
     | 
    
         
            +
            	edge = Edge.new([0,0], [1,0])
         
     | 
| 
      
 19 
     | 
    
         
            +
            	assert_kind_of(Edge, edge)
         
     | 
| 
       20 
20 
     | 
    
         
             
            	edge.reverse!
         
     | 
| 
       21 
21 
     | 
    
         
             
            	assert_equal(Geometry::Point[1,0], edge.first)
         
     | 
| 
       22 
22 
     | 
    
         
             
            	assert_equal(Geometry::Point[0,0], edge.last)
         
     | 
| 
       23 
23 
     | 
    
         
             
                end
         
     | 
| 
       24 
     | 
    
         
            -
                must  
     | 
| 
       25 
     | 
    
         
            -
            	edge1 =  
     | 
| 
       26 
     | 
    
         
            -
            	edge2 =  
     | 
| 
       27 
     | 
    
         
            -
            	edge3 =  
     | 
| 
      
 24 
     | 
    
         
            +
                it "must handle equality" do
         
     | 
| 
      
 25 
     | 
    
         
            +
            	edge1 = Edge.new([1,0], [0,1])
         
     | 
| 
      
 26 
     | 
    
         
            +
            	edge2 = Edge.new([1,0], [0,1])
         
     | 
| 
      
 27 
     | 
    
         
            +
            	edge3 = Edge.new([1,1], [5,5])
         
     | 
| 
       28 
28 
     | 
    
         
             
            	assert_equal(edge1, edge2)
         
     | 
| 
       29 
     | 
    
         
            -
            	 
     | 
| 
      
 29 
     | 
    
         
            +
            	edge1.wont_equal edge3
         
     | 
| 
       30 
30 
     | 
    
         
             
                end
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
                must  
     | 
| 
      
 32 
     | 
    
         
            +
                it "must return the height of the edge" do
         
     | 
| 
       33 
33 
     | 
    
         
             
            	edge = Edge([0,0], [1,1])
         
     | 
| 
       34 
34 
     | 
    
         
             
            	assert_equal(1, edge.height)
         
     | 
| 
       35 
35 
     | 
    
         
             
                end
         
     | 
| 
       36 
36 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
                must  
     | 
| 
      
 37 
     | 
    
         
            +
                it "must return the width of the edge" do
         
     | 
| 
       38 
38 
     | 
    
         
             
            	edge = Edge([0,0], [1,1])
         
     | 
| 
       39 
39 
     | 
    
         
             
            	assert_equal(1, edge.width)
         
     | 
| 
       40 
40 
     | 
    
         
             
                end
         
     |