geometry 1 → 2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  *.gem
2
2
  .bundle
3
+ .yardoc
3
4
  Gemfile.lock
4
5
  pkg/*
5
6
  doc
@@ -20,6 +20,7 @@ Primitives
20
20
 
21
21
  - Point
22
22
  - Line
23
+ - Circle
23
24
 
24
25
  Examples
25
26
  --------
@@ -27,7 +28,7 @@ Examples
27
28
  ### Point
28
29
  ```ruby
29
30
  point = Geometry::Point[3,4] # 2D Point at coordinate 3, 4
30
- point = Geometry.Point(1,2) # Functional constructor
31
+ point = Geometry.point(1,2) # Functional constructor
31
32
 
32
33
  # Copy constructors
33
34
  point2 = Geometry::Point[point]
@@ -57,4 +58,10 @@ Examples
57
58
  # Special constructors (2D only)
58
59
  Geometry::Line.horizontal(y=0)
59
60
  Geometry::Line.vertical(x=0)
60
- ```
61
+ ```
62
+
63
+ ### Circle
64
+ ```ruby
65
+ # A circle at Point[1,2] with a radius of 3
66
+ circle = Geometry::Circle [1,2], 3
67
+ ```
data/Rakefile CHANGED
@@ -1 +1,19 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ task :fixdates do
4
+ branch = `git branch --no-color -r --merged`.strip
5
+ `git fix-dates #{branch}..HEAD`
6
+ end
7
+
8
+ task :fixdates_f do
9
+ branch = `git branch --no-color -r --merged`.strip
10
+ `git fix-dates -f #{branch}..HEAD`
11
+ end
12
+
13
+ task :trim_whitespace do
14
+ system(%Q[git status --short | awk '{if ($1 != "D" && $1 != "R") print $2}' | grep -e '.*\.rb$' | xargs sed -i '' -e 's/[ \t]*$//g;'])
15
+ end
16
+
17
+ task :uninstall do
18
+ `gem uninstall geometry`
19
+ end
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "geometry"
6
- s.version = '1'
6
+ s.version = '2'
7
7
  s.authors = ["Brandon Fosdick"]
8
8
  s.email = ["bfoz@bfoz.net"]
9
9
  s.homepage = "http://github.com/bfoz/geometry"
@@ -1,22 +1,40 @@
1
+ require_relative 'geometry/circle'
1
2
  require_relative 'geometry/point'
2
3
  require_relative 'geometry/line'
4
+ require_relative 'geometry/polygon'
3
5
 
4
6
  module Geometry
5
- # :call-seq:
6
- # Line[Array, Array] -> TwoPointLine
7
- # Line[Point, Point] -> TwoPointLine
8
- # Line[Vector, Vector] -> TwoPointLine
9
- # Line[y-intercept, slope] -> SlopeInterceptLine
10
- # Line[point, slope] -> PointSlopeLine
11
- def self.Line(*args)
12
- Geometry::Line[*args]
7
+ # @overload Line(array0, array1)
8
+ # @param [Array] array0 First endpoint
9
+ # @param [Array] array1 Second endpoint
10
+ # @return [TwoPointLine]
11
+ # @overload Line(point0, point1)
12
+ # @param [Point] point0 First endpoint
13
+ # @param [Point] point1 Second endpoint
14
+ # @return [TwoPointLine]
15
+ # @overload Line(vector0, vector1)
16
+ # @param [Vector] vector0 First endpoint
17
+ # @param [Vector] vector1 Second endpoint
18
+ # @return [TwoPointLine]
19
+ # @overload Line(y_intercept, slope)
20
+ # @param [Numeric] y_intercept Y-intercept
21
+ # @param [Numeric] slope Slope
22
+ # @return [SlopeInterceptLine]
23
+ # @overload Line(point, slope)
24
+ # @param [Point] point Starting point
25
+ # @param [Numeric] slope Slope
26
+ # @return [PointSlopeLine]
27
+ def self.line(*args)
28
+ Line[*args]
13
29
  end
14
30
 
15
- # :call-seq:
16
- # Point[x,y,z,...]
17
- # Point[Point]
18
- # Point[Vector]
19
- def self.Point(*args)
20
- Geometry::Point[*args]
31
+ # @overload Point(x,y,z,...)
32
+ # @return [Point]
33
+ # @overload Point(Point)
34
+ # @return [Point]
35
+ # @overload Point(Vector)
36
+ # @return [Point]
37
+ def self.point(*args)
38
+ Point[*args]
21
39
  end
22
40
  end
@@ -0,0 +1,24 @@
1
+ require_relative 'point'
2
+
3
+ module Geometry
4
+
5
+ =begin rdoc
6
+ Circles come in all shapes and sizes, but they're usually round.
7
+
8
+ == Usage
9
+ circle = Geometry::Circle [1,1], 2
10
+ =end
11
+
12
+ class Circle
13
+ attr_reader :center, :radius
14
+
15
+ # Construct a new {Circle} from a centerpoint and radius
16
+ # @param [Point] center The center point of the Circle
17
+ # @param [Number] radius The radius of the Circle
18
+ # @return [Circle] A new Circle object
19
+ def initialize(center, radius)
20
+ @center = center.is_a?(Point) ? center : Point[center]
21
+ @radius = radius
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,57 @@
1
+ require_relative 'point'
2
+
3
+ module Geometry
4
+
5
+ =begin rdoc
6
+ An edge. It's a line segment between 2 points. Generally part of a {Polygon}.
7
+
8
+ == Usage
9
+ edge = Geometry::Edge([1,1], [2,2])
10
+
11
+ =end
12
+
13
+ class Edge
14
+ attr_reader :first, :last
15
+
16
+ # Construct a new {Edge} object from any two things that can be converted
17
+ # to a {Point}.
18
+ def initialize(point0, point1)
19
+ @first, @last = [point0, point1].map {|p| p.is_a?(Point) ? p : Point[p] }
20
+ end
21
+
22
+ # Two Edges are equal if both have equal {Point}s in the same order
23
+ def ==(other)
24
+ (@first == other.first) && (@last == other.last)
25
+ end
26
+
27
+ # Return a new {Edge} with swapped endpoints
28
+ def reverse
29
+ Edge.new(@last, @first)
30
+ end
31
+
32
+ # In-place swap the endpoints
33
+ def reverse!
34
+ @first, @last = @last, @first
35
+ self
36
+ end
37
+
38
+ # Return the {Edge}'s length along the Y axis
39
+ def height
40
+ (@first.y - @last.y).abs
41
+ end
42
+
43
+ # Return the {Edge}'s length along the X axis
44
+ def width
45
+ (@first.x - @last.x).abs
46
+ end
47
+
48
+ def inspect
49
+ 'Edge(' + @first.inspect + ', ' + @last.inspect + ')'
50
+ end
51
+ alias :to_s :inspect
52
+
53
+ def to_a
54
+ [@first, @last]
55
+ end
56
+ end
57
+ end
@@ -26,14 +26,19 @@ Supports two-point, slope-intercept, and point-slope initializer forms
26
26
  Geometry::Line.horizontal(y=0)
27
27
  Geometry::Line.vertical(x=0)
28
28
  =end
29
+
29
30
  class Line
30
31
 
31
- # :call-seq:
32
- # Line[Array, Array] -> TwoPointLine
33
- # Line[Point, Point] -> TwoPointLine
34
- # Line[Vector, Vector] -> TwoPointLine
35
- # Line[y-intercept, slope] -> SlopeInterceptLine
36
- # Line[point, slope] -> PointSlopeLine
32
+ # @overload [](Array, Array)
33
+ # @return [TwoPointLine]
34
+ # @overload [](Point, Point)
35
+ # @return [TwoPointLine]
36
+ # @overload [](Vector, Vector)
37
+ # @return [TwoPointLine]
38
+ # @overload [](y-intercept, slope)
39
+ # @return [SlopeInterceptLine]
40
+ # @overload [](point, slope)
41
+ # @return [PointSlopeLine]
37
42
  def self.[](*args)
38
43
  if( 2 == args.size )
39
44
  args.map! {|x| x.is_a?(Array) ? Point[*x] : x}
@@ -59,7 +64,8 @@ Supports two-point, slope-intercept, and point-slope initializer forms
59
64
  end
60
65
  end
61
66
 
62
- class PointSlopeLine < Line # :nodoc:
67
+ # @private
68
+ class PointSlopeLine < Line
63
69
  def initialize(point, slope)
64
70
  @point = point.is_a?(Geometry::Point) ? point : Geometry.Point(point)
65
71
  @slope = slope
@@ -69,7 +75,8 @@ Supports two-point, slope-intercept, and point-slope initializer forms
69
75
  end
70
76
  end
71
77
 
72
- class SlopeInterceptLine < Line # :nodoc:
78
+ # @private
79
+ class SlopeInterceptLine < Line
73
80
  def initialize(slope, intercept)
74
81
  @slope = slope
75
82
  @intercept = intercept
@@ -99,14 +106,15 @@ Supports two-point, slope-intercept, and point-slope initializer forms
99
106
  end
100
107
  end
101
108
 
102
- class TwoPointLine < Line # :nodoc:
109
+ # @private
110
+ class TwoPointLine < Line
103
111
  attr_reader :first, :last
104
112
 
105
113
  def initialize(point0, point1)
106
114
  @first, @last = [point0, point1].map {|p| p.is_a?(Point) ? p : Point[p] }
107
115
  end
108
116
  def inspect
109
- 'Line(' + @first.to_s + ', ' + @last.to_s + ')'
117
+ 'Line(' + @first.inspect + ', ' + @last.inspect + ')'
110
118
  end
111
119
  alias :to_s :inspect
112
120
  end
@@ -1,43 +1,57 @@
1
1
  require 'matrix'
2
2
 
3
3
  module Geometry
4
+ =begin rdoc
5
+ An object repesenting a Point in N-dimensional space
6
+
7
+ Supports all of the familiar Vector methods and adds convenience
8
+ accessors for those variables you learned to hate in your high school
9
+ geometry class (x, y, z).
10
+
11
+ == Usage
12
+
13
+ === Constructor
14
+ point = Geometry::Point[x,y]
15
+ =end
4
16
  class Point < Vector
5
- # An object repesenting a Point in N-dimensional space
6
- #
7
- # Supports all of the familiar Vector methods and adds convenience
8
- # accessors for those variables you learned to hate in your high school
9
- # geometry class (x, y, z).
10
- #
11
- # *Usage*
12
- #
13
- # point = Geometry::Point[x,y]
17
+ attr_reader :x, :y, :z
14
18
 
15
- # :call-seq:
16
- # Point[x,y,z,...]
17
- # Point[Point]
18
- # Point[Vector]
19
- #
20
19
  # Allow vector-style initialization, but override to support copy-init
21
20
  # from Vector or another Point
21
+ #
22
+ # @overload [](x,y,z,...)
23
+ # @overload [](Point)
24
+ # @overload [](Vector)
22
25
  def self.[](*array)
23
26
  array = array[0] if array[0].is_a?(Array)
24
27
  array = array[0].to_a if array[0].is_a?(Vector)
25
28
  super *array
26
29
  end
27
30
 
28
- def inspect # :nodoc:
31
+ # Allow comparison with an Array, otherwise do the normal thing
32
+ def ==(other)
33
+ return @elements == other if other.is_a?(Array)
34
+ super other
35
+ end
36
+
37
+ def inspect
29
38
  'Point' + @elements.inspect
30
39
  end
31
- def to_s # :nodoc:
40
+ def to_s
32
41
  'Point' + @elements.to_s
33
42
  end
34
43
 
44
+ # @return [Numeric] X-component
35
45
  def x
36
46
  @elements[0]
37
47
  end
48
+
49
+ # @return [Numeric] Y-component
38
50
  def y
39
51
  @elements[1]
40
52
  end
53
+
54
+ # @return [Numeric] Z-component
41
55
  def z
42
56
  @elements[2]
43
57
  end
@@ -0,0 +1,82 @@
1
+ require_relative 'edge'
2
+
3
+ module Geometry
4
+
5
+ =begin rdoc
6
+ An object representing a closed set of vertices and edges.
7
+
8
+ {http://en.wikipedia.org/wiki/Polygon}
9
+
10
+ == Usage
11
+
12
+ =end
13
+
14
+ class Polygon
15
+ attr_reader :edges, :vertices
16
+
17
+ # Construct a new Polygon from Points and/or Edges
18
+ # The constructor will try to convert all of its arguments into Points and
19
+ # Edges. Then successive Points will be collpased into Edges. Successive
20
+ # Edges that share a common vertex will be added to the new Polygon. If
21
+ # there's a gap between Edges it will be automatically filled with a new
22
+ # Edge. The resulting Polygon will then be closed if it isn't already.
23
+ # @overload new(Array, Array, ...)
24
+ # @return [Polygon]
25
+ # @overload new(Edge, Edge, ...)
26
+ # @return [Polygon]
27
+ # @overload new(Point, Point, ...)
28
+ # @return [Polygon]
29
+ # @overload new(Vector, Vector, ...)
30
+ # @return [Polygon]
31
+ def initialize(*args)
32
+ args.map! {|a| (a.is_a?(Array) || a.is_a?(Vector)) ? Point[a] : a}
33
+ raise(ArgumentError,'Unknown argument type') unless args.all? {|a| a.is_a?(Point) || a.is_a?(Edge) }
34
+
35
+ @edges = [];
36
+ @vertices = [];
37
+
38
+ first = args.shift
39
+ if first.is_a?(Point)
40
+ @vertices.push first
41
+ elsif first.is_a?(Edge)
42
+ @edges.push first
43
+ @vertices.push *(first.to_a)
44
+ end
45
+
46
+ args.reduce(@vertices.last) do |previous,n|
47
+ if n.is_a?(Point)
48
+ push_edge Edge.new(previous, n)
49
+ push_vertex n
50
+ n
51
+ elsif n.is_a?(Edge)
52
+ if previous == n.first
53
+ push_edge n
54
+ push_vertex n.last
55
+ elsif previous == n.last
56
+ push_edge n.reverse!
57
+ push_vertex n.last
58
+ else
59
+ e = Edge.new(previous, n.first)
60
+ push_edge e, n
61
+ push_vertex *(e.to_a), *(n.to_a)
62
+ end
63
+ n.last
64
+ end
65
+ end
66
+
67
+ # Close the polygon if needed
68
+ @edges.push Edge.new(@edges.last.last, @edges.first.first) unless @edges.empty? || (@edges.last.last == @edges.first.first)
69
+ end
70
+
71
+ private
72
+
73
+ def push_edge(*e)
74
+ @edges.push *e
75
+ @edges.uniq!
76
+ end
77
+ def push_vertex(*v)
78
+ @vertices.push *v
79
+ @vertices.uniq!
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,23 @@
1
+ require_relative '../helper'
2
+ require_relative '../../lib/geometry/circle'
3
+
4
+ def Circle(*args)
5
+ Geometry::Circle.new(*args)
6
+ end
7
+
8
+ class CircleTest < Test::Unit::TestCase
9
+ must "create a Circle object from a Point and a radius" do
10
+ circle = Circle [1,2], 3
11
+ assert_kind_of(Geometry::Circle, circle)
12
+ end
13
+
14
+ must "have a center point accessor" do
15
+ circle = Circle [1,2], 3
16
+ assert_equal(circle.center, [1,2])
17
+ end
18
+
19
+ must "have a radius accessor" do
20
+ circle = Circle [1,2], 3
21
+ assert_equal(3, circle.radius)
22
+ end
23
+ end
@@ -0,0 +1,41 @@
1
+ require_relative '../helper'
2
+ require_relative '../../lib/geometry/edge'
3
+
4
+ def Edge(*args)
5
+ Geometry::Edge.new(*args)
6
+ end
7
+
8
+ class EdgeTest < Test::Unit::TestCase
9
+ Edge = Geometry::Edge
10
+
11
+ must "create an Edge object" do
12
+ edge = Geometry::Edge.new([0,0], [1,0])
13
+ assert_kind_of(Geometry::Edge, edge)
14
+ assert_equal(Geometry::Point[0,0], edge.first)
15
+ assert_equal(Geometry::Point[1,0], edge.last)
16
+ end
17
+ must "create swap endpoints in place" do
18
+ edge = Geometry::Edge.new([0,0], [1,0])
19
+ assert_kind_of(Geometry::Edge, edge)
20
+ edge.reverse!
21
+ assert_equal(Geometry::Point[1,0], edge.first)
22
+ assert_equal(Geometry::Point[0,0], edge.last)
23
+ end
24
+ must "handle equality" do
25
+ edge1 = Geometry::Edge.new([1,0], [0,1])
26
+ edge2 = Geometry::Edge.new([1,0], [0,1])
27
+ edge3 = Geometry::Edge.new([1,1], [5,5])
28
+ assert_equal(edge1, edge2)
29
+ assert_not_equal(edge1, edge3)
30
+ end
31
+
32
+ must "return the height of the edge" do
33
+ edge = Edge([0,0], [1,1])
34
+ assert_equal(1, edge.height)
35
+ end
36
+
37
+ must "return the width of the edge" do
38
+ edge = Edge([0,0], [1,1])
39
+ assert_equal(1, edge.width)
40
+ end
41
+ end
@@ -1,8 +1,9 @@
1
- require 'test/unit'
2
- require_relative 'test_unit_extensions'
1
+ require_relative 'helper'
3
2
  require_relative '../lib/geometry/point'
4
3
 
5
4
  class PointTest < Test::Unit::TestCase
5
+ Point = Geometry::Point
6
+
6
7
  must "create a Point object using list syntax" do
7
8
  point = Geometry::Point[2,1]
8
9
  assert_equal(2, point.size)
@@ -58,6 +59,24 @@ class PointTest < Test::Unit::TestCase
58
59
  assert_equal(6, point.y)
59
60
  assert_equal(7, point.z)
60
61
  end
62
+ must "compare equal" do
63
+ point1 = Geometry::Point[1,2]
64
+ point2 = Geometry::Point[1,2]
65
+ point3 = Geometry::Point[3,4]
66
+ assert_equal(point1, point2)
67
+ assert_not_equal(point2, point3)
68
+ end
69
+
70
+ must "compare equal to an array with equal elements" do
71
+ point1 = Point[1,2]
72
+ assert_equal(point1, [1,2])
73
+ end
74
+
75
+ must "not compare equal to an array with unequal elements" do
76
+ point1 = Point[1,2]
77
+ assert_not_equal(point1, [3,2])
78
+ end
79
+
61
80
  must "implement inspect" do
62
81
  point = Geometry::Point[8,9]
63
82
  assert_equal('Point[8, 9]', point.inspect)
@@ -0,0 +1,28 @@
1
+ require_relative '../helper'
2
+ require_relative '../../lib/geometry/polygon'
3
+
4
+ class PolygonTest < Test::Unit::TestCase
5
+ must "create a Polygon object with no arguments" do
6
+ polygon = Geometry::Polygon.new
7
+ assert_kind_of(Geometry::Polygon, polygon)
8
+ assert_equal(0, polygon.edges.size)
9
+ assert_equal(0, polygon.vertices.size)
10
+ end
11
+ must "create a Polygon object from array arguments" do
12
+ polygon = Geometry::Polygon.new([0,0], [1,0], [1,1], [0,1])
13
+ assert_kind_of(Geometry::Polygon, polygon)
14
+ assert_equal(4, polygon.edges.size)
15
+ assert_equal(4, polygon.vertices.size)
16
+ end
17
+ must "create closed polygons" do
18
+ polygon = Geometry::Polygon.new([0,0], [1,0], [1,1], [0,1])
19
+ assert_equal(polygon.edges.first.first, polygon.edges.last.last)
20
+ end
21
+ must "handle already closed polygons" do
22
+ polygon = Geometry::Polygon.new([0,0], [1,0], [1,1], [0,1], [0,0])
23
+ assert_kind_of(Geometry::Polygon, polygon)
24
+ assert_equal(4, polygon.edges.size)
25
+ assert_equal(4, polygon.vertices.size)
26
+ assert_equal(polygon.edges.first.first, polygon.edges.last.last)
27
+ end
28
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require_relative 'test_unit_extensions'
@@ -1,14 +1,13 @@
1
- require 'test/unit'
2
- require_relative 'test_unit_extensions'
1
+ require_relative 'helper'
3
2
  require_relative '../lib/geometry'
4
3
 
5
4
  class GeometryTest < Test::Unit::TestCase
6
5
  must "create a Point object" do
7
- point = Geometry.Point(2,1)
6
+ point = Geometry.point(2,1)
8
7
  assert_kind_of(Geometry::Point, point)
9
8
  end
10
9
  must "create a Line object" do
11
- line = Geometry.Line([0,0], [10,10])
10
+ line = Geometry.line([0,0], [10,10])
12
11
  assert_kind_of(Geometry::Line, line)
13
12
  assert_kind_of(Geometry::TwoPointLine, line)
14
13
  end
@@ -1,5 +1,4 @@
1
- require 'test/unit'
2
- require_relative 'test_unit_extensions'
1
+ require_relative 'helper'
3
2
  require_relative '../lib/geometry/line'
4
3
 
5
4
  class PointTest < Test::Unit::TestCase
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geometry
3
3
  version: !ruby/object:Gem::Version
4
- version: '1'
4
+ version: '2'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-09 00:00:00.000000000 Z
12
+ date: 2012-04-22 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Geometric primitives and algorithms for Ruby
15
15
  email:
@@ -25,11 +25,18 @@ files:
25
25
  - Rakefile
26
26
  - geometry.gemspec
27
27
  - lib/geometry.rb
28
+ - lib/geometry/circle.rb
29
+ - lib/geometry/edge.rb
28
30
  - lib/geometry/line.rb
29
31
  - lib/geometry/point.rb
32
+ - lib/geometry/polygon.rb
33
+ - test/geometry/circle.rb
34
+ - test/geometry/edge.rb
35
+ - test/geometry/point.rb
36
+ - test/geometry/polygon.rb
37
+ - test/helper.rb
30
38
  - test/test_geometry.rb
31
39
  - test/test_line.rb
32
- - test/test_point.rb
33
40
  - test/test_unit_extensions.rb
34
41
  homepage: http://github.com/bfoz/geometry
35
42
  licenses: []
@@ -56,7 +63,12 @@ signing_key:
56
63
  specification_version: 3
57
64
  summary: Geometric primitives and algoritms
58
65
  test_files:
66
+ - test/geometry/circle.rb
67
+ - test/geometry/edge.rb
68
+ - test/geometry/point.rb
69
+ - test/geometry/polygon.rb
70
+ - test/helper.rb
59
71
  - test/test_geometry.rb
60
72
  - test/test_line.rb
61
- - test/test_point.rb
62
73
  - test/test_unit_extensions.rb
74
+ has_rdoc: