euclidean 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +8 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +90 -0
- data/Rakefile +1 -0
- data/euclidean.gemspec +24 -0
- data/lib/euclidean.rb +12 -0
- data/lib/euclidean/circle.rb +118 -0
- data/lib/euclidean/cluster_factory.rb +15 -0
- data/lib/euclidean/edge.rb +143 -0
- data/lib/euclidean/point.rb +160 -0
- data/lib/euclidean/point_zero.rb +104 -0
- data/lib/euclidean/rectangle.rb +378 -0
- data/lib/euclidean/size.rb +78 -0
- data/lib/euclidean/vector.rb +34 -0
- data/lib/euclidean/version.rb +3 -0
- data/spec/euclidean/circle_spec.rb +115 -0
- data/spec/euclidean/edge_spec.rb +129 -0
- data/spec/euclidean/point_spec.rb +264 -0
- data/spec/euclidean/rectangle_spec.rb +155 -0
- data/spec/euclidean/size_spec.rb +98 -0
- data/spec/euclidean/vector_spec.rb +41 -0
- data/spec/spec_helper.rb +15 -0
- metadata +118 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'matrix'
|
2
|
+
|
3
|
+
module Euclidean
|
4
|
+
=begin
|
5
|
+
An object representing the size of something.
|
6
|
+
|
7
|
+
Supports all of the familiar {Vector} methods as well as a few convenience
|
8
|
+
methods (width, height and depth).
|
9
|
+
|
10
|
+
== Usage
|
11
|
+
|
12
|
+
=== Constructor
|
13
|
+
size = Geometry::Size[x,y,z]
|
14
|
+
=end
|
15
|
+
|
16
|
+
class Size < Vector
|
17
|
+
attr_reader :x, :y, :z
|
18
|
+
|
19
|
+
# Allow vector-style initialization, but override to support copy-init
|
20
|
+
# from Vector, Point or another Size
|
21
|
+
#
|
22
|
+
# @overload [](x,y,z,...)
|
23
|
+
# @overload [](Point)
|
24
|
+
# @overload [](Size)
|
25
|
+
# @overload [](Vector)
|
26
|
+
# @return [Size] A new {Size} object
|
27
|
+
def self.[](*array)
|
28
|
+
array = array[0].to_a unless array[0].is_a?(Numeric)
|
29
|
+
super *array
|
30
|
+
end
|
31
|
+
|
32
|
+
# Allow comparison with an Array, otherwise do the normal thing
|
33
|
+
def ==(other)
|
34
|
+
return @elements == other if other.is_a?(Array)
|
35
|
+
super other
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Number] The size along the Z axis
|
39
|
+
def depth
|
40
|
+
z
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Number] The size along the Y axis
|
44
|
+
def height
|
45
|
+
y
|
46
|
+
end
|
47
|
+
|
48
|
+
def inspect
|
49
|
+
'Size' + @elements.inspect
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
'Size' + @elements.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [Number] The size along the X axis
|
57
|
+
def width
|
58
|
+
x
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [Number] X-component (width)
|
62
|
+
def x
|
63
|
+
@elements[0]
|
64
|
+
end
|
65
|
+
|
66
|
+
# @return [Number] Y-component (height)
|
67
|
+
def y
|
68
|
+
@elements[1]
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [Number] Z-component (depth)
|
72
|
+
def z
|
73
|
+
@elements[2]
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'matrix'
|
2
|
+
|
3
|
+
# Monkeypatch Vector to overcome some deficiencies
|
4
|
+
class Vector
|
5
|
+
X = Vector[1,0,0]
|
6
|
+
Y = Vector[0,1,0]
|
7
|
+
Z = Vector[0,0,1]
|
8
|
+
|
9
|
+
# @group Unary operators
|
10
|
+
def +@
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def -@
|
15
|
+
Vector[*(@elements.map {|e| -e })]
|
16
|
+
end
|
17
|
+
# @endgroup
|
18
|
+
|
19
|
+
# Cross-product of two {Vector}s
|
20
|
+
# @return [Vector]
|
21
|
+
def cross(other)
|
22
|
+
Vector.Raise ErrDimensionMismatch unless @elements.size == other.size
|
23
|
+
|
24
|
+
case @elements.size
|
25
|
+
when 0 then raise ArgumentError, "Can't multply zero-length Vectors"
|
26
|
+
when 1 then @elements.first * other.first
|
27
|
+
when 2 then @elements.first * other[1] - @elements.last * other.first
|
28
|
+
when 3 then Vector[ @elements[1]*other[2] - @elements[2]*other[1],
|
29
|
+
@elements[2]*other[0] - @elements[0]*other[2],
|
30
|
+
@elements[0]*other[1] - @elements[1]*other[0]]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias ** cross
|
34
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'euclidean/circle'
|
3
|
+
|
4
|
+
describe Euclidean::Circle do
|
5
|
+
Circle = Euclidean::Circle
|
6
|
+
Point = Euclidean::Point
|
7
|
+
Rectangle = Euclidean::Rectangle
|
8
|
+
|
9
|
+
context "When contstructed with center and radius arguments" do
|
10
|
+
let(:circle) { Circle.new [1,2], 3 }
|
11
|
+
|
12
|
+
it "must create a Circle" do
|
13
|
+
expect(circle).to be_an_instance_of Circle
|
14
|
+
end
|
15
|
+
|
16
|
+
it "must have a center point accessor" do
|
17
|
+
expect(circle.center).to eq Point[1,2]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "must have a radius accessor" do
|
21
|
+
expect(circle.radius).to eq 3
|
22
|
+
end
|
23
|
+
|
24
|
+
it "must compare equal" do
|
25
|
+
expect(circle).to eq Circle.new([1,2], 3)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "When constructed with named center and radius arguments" do
|
30
|
+
let(:circle) { Circle.new :center => [1,2], :radius => 3 }
|
31
|
+
|
32
|
+
it "must create a Circle" do
|
33
|
+
expect(circle).to be_an_instance_of Euclidean::Circle
|
34
|
+
end
|
35
|
+
|
36
|
+
it "must have a center point accessor" do
|
37
|
+
expect(circle.center).to eq Point[1,2]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "must have a radius accessor" do
|
41
|
+
expect(circle.radius).to eq 3
|
42
|
+
end
|
43
|
+
|
44
|
+
it "must compare equal" do
|
45
|
+
expect((circle == Circle.new(:center => [1,2], :radius => 3))).to eq true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "When constructed with named center and diameter arguments" do
|
50
|
+
let(:circle) { Circle.new center:[1,2], diameter:4 }
|
51
|
+
|
52
|
+
it "must be a CenterDiameterCircle" do
|
53
|
+
expect(circle).to be_an_instance_of Euclidean::CenterDiameterCircle
|
54
|
+
expect(circle).to be_a_kind_of Euclidean::Circle
|
55
|
+
end
|
56
|
+
|
57
|
+
it "must have a center" do
|
58
|
+
expect(circle.center).to eq Point[1,2]
|
59
|
+
end
|
60
|
+
|
61
|
+
it "must have a diameter" do
|
62
|
+
expect(circle.diameter).to eq 4
|
63
|
+
end
|
64
|
+
|
65
|
+
it "must calculate the correct radius" do
|
66
|
+
expect(circle.radius).to eq 2
|
67
|
+
end
|
68
|
+
|
69
|
+
it "must compare equal" do
|
70
|
+
expect(circle).to eq Circle.new([1,2], :diameter => 4)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "When constructed with a diameter and no center" do
|
75
|
+
let(:circle) { Circle.new :diameter => 4 }
|
76
|
+
|
77
|
+
it "must be a CenterDiameterCircle" do
|
78
|
+
expect(circle).to be_an_instance_of Euclidean::CenterDiameterCircle
|
79
|
+
expect(circle).to be_a_kind_of Euclidean::Circle
|
80
|
+
end
|
81
|
+
|
82
|
+
it "must have a nil center" do
|
83
|
+
expect(circle.center).to be_a_kind_of Euclidean::PointZero
|
84
|
+
end
|
85
|
+
|
86
|
+
it "must have a diameter" do
|
87
|
+
expect(circle.diameter).to eq 4
|
88
|
+
end
|
89
|
+
|
90
|
+
it "must calculate the correct radius" do
|
91
|
+
expect(circle.radius).to eq 2
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "Properties" do
|
96
|
+
subject { Circle.new center:[1,2], :diameter => 4 }
|
97
|
+
|
98
|
+
it "must have a bounds property that returns a Rectangle" do
|
99
|
+
expect(subject.bounds).to eq Rectangle.new([-1,0], [3,4])
|
100
|
+
end
|
101
|
+
|
102
|
+
it "must have a minmax property that returns the corners of the bounding rectangle" do
|
103
|
+
expect(subject.minmax).to eq [Point[-1,0], Point[3,4]]
|
104
|
+
end
|
105
|
+
|
106
|
+
it "must have a max property that returns the upper right corner of the bounding rectangle" do
|
107
|
+
expect(subject.max).to eq Point[3,4]
|
108
|
+
end
|
109
|
+
|
110
|
+
it "must have a min property that returns the lower left corner of the bounding rectangle" do
|
111
|
+
expect(subject.min).to eq Point[-1,0]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'euclidean/edge'
|
3
|
+
|
4
|
+
describe Euclidean::Edge do
|
5
|
+
Edge = Euclidean::Edge
|
6
|
+
subject { Edge.new [0,0], [1,1] }
|
7
|
+
|
8
|
+
it "must create an Edge object" do
|
9
|
+
edge = Edge.new([0,0], [1,0])
|
10
|
+
expect(edge).to be_a_kind_of Euclidean::Edge
|
11
|
+
expect(edge.first).to eq Euclidean::Point[0,0]
|
12
|
+
expect(edge.last).to eq Euclidean::Point[1,0]
|
13
|
+
end
|
14
|
+
|
15
|
+
it "must handle equality" do
|
16
|
+
edge1 = Edge.new([1,0], [0,1])
|
17
|
+
edge2 = Edge.new([1,0], [0,1])
|
18
|
+
edge3 = Edge.new([1,1], [5,5])
|
19
|
+
expect(edge1==edge2).to be true
|
20
|
+
expect(edge1==edge3).to be false
|
21
|
+
end
|
22
|
+
|
23
|
+
it "must return the height of the edge" do
|
24
|
+
edge = Edge([0,0], [1,1])
|
25
|
+
expect(edge.height).to eq 1
|
26
|
+
end
|
27
|
+
|
28
|
+
it "must return the width of the edge" do
|
29
|
+
edge = Edge([0,0], [1,1])
|
30
|
+
expect(edge.width).to eq 1
|
31
|
+
end
|
32
|
+
|
33
|
+
it "must convert an Edge to a Vector" do
|
34
|
+
expect(Edge.new([0,0], [1,0]).vector).to eq Vector[1,0]
|
35
|
+
end
|
36
|
+
|
37
|
+
it "must return the normalized direction of a vector" do
|
38
|
+
expect(Edge.new([0,0], [1,0]).direction).to eq Vector[1,0]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "must return true for parallel edges" do
|
42
|
+
expect( Edge.new([0,0], [1,0]).parallel?(Edge.new([0,0], [1,0])) ).to be true
|
43
|
+
expect( Edge.new([0,0], [1,0]).parallel?(Edge.new([1,0], [2,0])) ).to be true
|
44
|
+
expect( Edge.new([0,0], [1,0]).parallel?(Edge.new([3,0], [4,0])) ).to be true
|
45
|
+
expect( Edge.new([0,0], [1,0]).parallel?(Edge.new([3,1], [4,1])) ).to be true
|
46
|
+
end
|
47
|
+
|
48
|
+
it "must return false for non-parallel edges" do
|
49
|
+
expect( Edge.new([0,0], [2,0]).parallel?(Edge.new([1,-1], [1,1])) ).to be false
|
50
|
+
end
|
51
|
+
|
52
|
+
it "must clone and reverse" do
|
53
|
+
reversed = subject.reverse
|
54
|
+
expect(reversed.to_a).to eq subject.to_a.reverse
|
55
|
+
expect(reversed==subject).to be false
|
56
|
+
end
|
57
|
+
|
58
|
+
it "must reverse itself" do
|
59
|
+
original = subject.to_a
|
60
|
+
subject.reverse!
|
61
|
+
expect(subject.to_a).to eq original.reverse
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "Spaceship" do
|
65
|
+
it "ascending with a Point" do
|
66
|
+
edge = Edge.new [0,0], [1,1]
|
67
|
+
expect(edge <=> Point[0,0]).to eq 0
|
68
|
+
expect(edge <=> Point[1,0]).to eq -1
|
69
|
+
expect(edge <=> Point[0,1]).to eq 1
|
70
|
+
expect(edge <=> Point[2,2]).to be_nil
|
71
|
+
end
|
72
|
+
|
73
|
+
it "descending with a Point" do
|
74
|
+
edge = Edge.new [1,1], [0,0]
|
75
|
+
expect(edge <=> Point[0,0]).to eq 0
|
76
|
+
expect(edge <=> Point[1,0]).to eq 1
|
77
|
+
expect(edge <=> Point[0,1]).to eq -1
|
78
|
+
expect(edge <=> Point[2,2]).to be_nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "Intersection" do
|
83
|
+
it "must find the intersection of two end-intersecting Edges" do
|
84
|
+
intersection = Edge.new([0,0],[1,1]).intersection(Edge.new([0,1],[1,1]))
|
85
|
+
expect(intersection).to be_a_kind_of Euclidean::Point
|
86
|
+
expect(intersection).to eq Euclidean::Point[1,1]
|
87
|
+
end
|
88
|
+
|
89
|
+
it "must find the intersection of two collinear end-intersecting Edges" do
|
90
|
+
intersection = Edge.new([2,2], [0,2]).intersection(Edge.new([3,2], [2,2]))
|
91
|
+
expect(intersection).to be_a_kind_of Euclidean::Point
|
92
|
+
expect(intersection).to eq Euclidean::Point[2,2]
|
93
|
+
|
94
|
+
intersection = Edge.new([0,2], [2,2]).intersection(Edge.new([2,2], [3,2]))
|
95
|
+
expect(intersection).to be_a_kind_of Euclidean::Point
|
96
|
+
expect(intersection).to eq Euclidean::Point[2,2]
|
97
|
+
end
|
98
|
+
|
99
|
+
it "must find the itersection of two crossed Edges" do
|
100
|
+
edge1 = Edge.new [0.0, 0], [2.0, 2.0]
|
101
|
+
edge2 = Edge.new [2.0, 0], [0.0, 2.0]
|
102
|
+
intersection = edge1.intersection edge2
|
103
|
+
expect(intersection).to be_a_kind_of Euclidean::Point
|
104
|
+
expect(intersection).to eq Euclidean::Point[1,1]
|
105
|
+
end
|
106
|
+
|
107
|
+
it "must return nil for two edges that do not intersect" do
|
108
|
+
expect( Edge.new([0,0],[1,0]).intersection(Edge.new([0,1],[1,1])) ).to be_nil
|
109
|
+
end
|
110
|
+
|
111
|
+
it "must return true for two collinear and overlapping edges" do
|
112
|
+
expect( Edge.new([0,0],[2,0]).intersection(Edge.new([1,0],[3,0])) ).to be true
|
113
|
+
end
|
114
|
+
|
115
|
+
it "must return false for collinear but non-overlapping edges" do
|
116
|
+
expect( Edge.new([0,0],[2,0]).intersection(Edge.new([3,0],[4,0])) ).to be false
|
117
|
+
expect( Edge.new([0,0],[0,2]).intersection(Edge.new([0,3],[0,4])) ).to be false
|
118
|
+
end
|
119
|
+
|
120
|
+
it "must return nil for two parallel but not collinear edges" do
|
121
|
+
expect( Edge.new([0,0],[2,0]).intersection(Edge.new([1,1],[3,1])) ).to be_nil
|
122
|
+
end
|
123
|
+
|
124
|
+
it "must return nil for two perpendicular but not interseting edges" do
|
125
|
+
expect( Edge.new([0, 0], [2, 0]).intersection(Edge.new([3, 3], [3, 1])) ).to be_nil
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
@@ -0,0 +1,264 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'euclidean/point'
|
3
|
+
|
4
|
+
describe Euclidean::Point do
|
5
|
+
PointZero = Euclidean::PointZero
|
6
|
+
Point = Euclidean::Point
|
7
|
+
|
8
|
+
it "must generate a PointZero" do
|
9
|
+
expect(Point.zero).to be_an_instance_of(PointZero)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "must generate a Point full of zeros" do
|
13
|
+
expect( Point.zero(3) ).to eq Point[0,0,0]
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "constructor" do
|
17
|
+
it "must return the Point when constructed from a Point" do
|
18
|
+
original_point = Point[3,4]
|
19
|
+
point = Point[original_point]
|
20
|
+
expect( point ).to eq original_point
|
21
|
+
expect( point.size ).to eq 2
|
22
|
+
expect( point.x ).to eq 3
|
23
|
+
expect( point.y ).to eq 4
|
24
|
+
end
|
25
|
+
|
26
|
+
it "must return the PointZero when constructed from a PointZero" do
|
27
|
+
original_point = PointZero.new
|
28
|
+
point = Point[original_point]
|
29
|
+
expect( point ).to eq original_point
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "create a Point object from an array" do
|
34
|
+
point = Point[[3,4]]
|
35
|
+
expect(point.size).to eq 2
|
36
|
+
expect(point.x).to eq 3
|
37
|
+
expect(point.y).to eq 4
|
38
|
+
end
|
39
|
+
|
40
|
+
it "create a Point object from individual parameters" do
|
41
|
+
point = Point[3,4]
|
42
|
+
expect(point.size).to eq 2
|
43
|
+
expect(point.x).to eq 3
|
44
|
+
expect(point.y).to eq 4
|
45
|
+
end
|
46
|
+
|
47
|
+
it "create a Point object from a Vector" do
|
48
|
+
point = Point[Vector[3,4]]
|
49
|
+
expect(point.size).to eq 2
|
50
|
+
expect(point.x).to eq 3
|
51
|
+
expect(point.y).to eq 4
|
52
|
+
end
|
53
|
+
|
54
|
+
it "create a Point object from a Point using list syntax" do
|
55
|
+
point = Point[Point[13,14]]
|
56
|
+
expect(point.size).to eq 2
|
57
|
+
expect(point.x).to eq 13
|
58
|
+
expect(point.y).to eq 14
|
59
|
+
end
|
60
|
+
|
61
|
+
it "allow indexed element access" do
|
62
|
+
point = Point[5,6]
|
63
|
+
expect(point.size).to eq 2
|
64
|
+
expect(point[0]).to eq 5
|
65
|
+
expect(point[1]).to eq 6
|
66
|
+
end
|
67
|
+
|
68
|
+
it "allow named element access" do
|
69
|
+
point = Point[5,6,7]
|
70
|
+
expect(point.size).to eq 3
|
71
|
+
expect(point.x).to eq 5
|
72
|
+
expect(point.y).to eq 6
|
73
|
+
expect(point.z).to eq 7
|
74
|
+
end
|
75
|
+
|
76
|
+
it "implement inspect" do
|
77
|
+
point = Point[8,9]
|
78
|
+
expect(point.inspect).to eq 'Point[8, 9]'
|
79
|
+
end
|
80
|
+
|
81
|
+
it "implement to_s" do
|
82
|
+
point = Point[10,11]
|
83
|
+
expect(point.to_s).to eq 'Point[10, 11]'
|
84
|
+
end
|
85
|
+
|
86
|
+
it "must support array access" do
|
87
|
+
expect( Point[1,2][0] ).to eq 1
|
88
|
+
expect( Point[1,2][1] ).to eq 2
|
89
|
+
expect( Point[1,2][2] ).to eq nil
|
90
|
+
end
|
91
|
+
|
92
|
+
it "must clone" do
|
93
|
+
expect(Point[1,2].clone).to be_an_instance_of(Point)
|
94
|
+
expect( Point[1,2].clone ).to eq Point[1,2]
|
95
|
+
end
|
96
|
+
|
97
|
+
it "must duplicate" do
|
98
|
+
expect(Point[1,2].dup).to be_an_instance_of(Point)
|
99
|
+
expect( Point[1,2].dup ).to eq Point[1,2]
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "Artrithmetic" do
|
103
|
+
let(:left) { Point[1,2] }
|
104
|
+
let(:right) { Point[3,4] }
|
105
|
+
|
106
|
+
it "must have +@" do
|
107
|
+
expect(+left).to eq Point[1,2]
|
108
|
+
expect(+left).to be_an_instance_of(Point)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "must have unary negation" do
|
112
|
+
expect(-left).to eq Point[-1,-2]
|
113
|
+
expect(-left).to be_an_instance_of(Point)
|
114
|
+
end
|
115
|
+
|
116
|
+
context "When adding" do
|
117
|
+
it "return a Point when adding two Points" do
|
118
|
+
expect(left+right).to be_a_kind_of Point
|
119
|
+
end
|
120
|
+
|
121
|
+
it "must return a Point when adding an array to a Point" do
|
122
|
+
expect(left + [5,6]).to eq Point[6,8]
|
123
|
+
end
|
124
|
+
|
125
|
+
it "must add a Numeric to all elements" do
|
126
|
+
expect(left + 2).to eq Point[3,4]
|
127
|
+
expect(2 + left).to eq Point[3,4]
|
128
|
+
end
|
129
|
+
|
130
|
+
it "must raise an exception when adding mismatched sizes" do
|
131
|
+
expect { left + [1,2,3,4] }.to raise_error Euclidean::DimensionMismatch
|
132
|
+
end
|
133
|
+
|
134
|
+
it "must return a Point when adding a Vector" do
|
135
|
+
expect(left + Vector[5,6]).to eq Point[6,8]
|
136
|
+
expect(Vector[5,6] + right).to eq Vector[8,10]
|
137
|
+
end
|
138
|
+
|
139
|
+
it "must return self when adding a PointZero" do
|
140
|
+
expect(left + Point.zero).to eq left
|
141
|
+
end
|
142
|
+
|
143
|
+
it "must return self when adding a NilClass" do
|
144
|
+
expect(left + nil).to eq left
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "When subtracting" do
|
149
|
+
it "return a Point when subtracting two Points" do
|
150
|
+
expect(left-right).to be_a_kind_of Point
|
151
|
+
end
|
152
|
+
|
153
|
+
it "must return a Point when subtracting an array from a Point" do
|
154
|
+
expect(left - [5,6]).to eq Point[-4, -4]
|
155
|
+
end
|
156
|
+
|
157
|
+
it "must subtract a Numeric from all elements" do
|
158
|
+
expect(left - 2).to eq Point[-1, 0]
|
159
|
+
expect(2 - left).to eq Point[1,0]
|
160
|
+
end
|
161
|
+
|
162
|
+
it "must raise an exception when subtracting mismatched sizes" do
|
163
|
+
expect{ left - [1,2,3,4] }.to raise_error Euclidean::DimensionMismatch
|
164
|
+
end
|
165
|
+
|
166
|
+
it "must return self when subtracting a PointZero" do
|
167
|
+
expect(left - Point.zero).to eq left
|
168
|
+
end
|
169
|
+
|
170
|
+
it "must return self when subtracting a NilClass" do
|
171
|
+
expect(left - nil).to eq left
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "When multiplying" do
|
176
|
+
it "must return a Point when multiplied by a Matrix" do
|
177
|
+
expect(Matrix[[1,2],[3,4]]*Point[5,6]).to eq Point[17, 39]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
describe "Coercion" do
|
184
|
+
subject { Point[1,2] }
|
185
|
+
|
186
|
+
it "must coerce Arrays into Points" do
|
187
|
+
expect( subject.coerce([3,4]) ).to eq [Point[3,4], subject]
|
188
|
+
end
|
189
|
+
|
190
|
+
it "must coerce Vectors into Points" do
|
191
|
+
expect( subject.coerce(Vector[3,4]) ).to eq [Point[3,4], subject]
|
192
|
+
end
|
193
|
+
|
194
|
+
it "must coerce a Numeric into a Point" do
|
195
|
+
expect( subject.coerce(42) ).to eq [Point[42,42], subject]
|
196
|
+
end
|
197
|
+
|
198
|
+
it "must reject anything that can't be coerced" do
|
199
|
+
expect{ subject.coerce(NilClass) }.to raise_error TypeError
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "Comparison" do
|
204
|
+
let(:point) { Point[1,2] }
|
205
|
+
|
206
|
+
it "must compare equal to an equal Array" do
|
207
|
+
expect(point).to eq [1,2]
|
208
|
+
expect(point).to eql [1,2]
|
209
|
+
expect([1,2]).to eq point.to_a
|
210
|
+
end
|
211
|
+
|
212
|
+
it "must not compare equal to an unequal Array" do
|
213
|
+
expect(point==[3,2]).to be false
|
214
|
+
expect([3,2]==point).to be false
|
215
|
+
end
|
216
|
+
|
217
|
+
it "must compare equal to an equal Point" do
|
218
|
+
expect(point).to eq Point[1,2]
|
219
|
+
expect(point).to eql Point[1,2]
|
220
|
+
expect(Point[1,2]).to eq point
|
221
|
+
end
|
222
|
+
|
223
|
+
it "must not compare equal to an unequal Point" do
|
224
|
+
expect(point==Point[3,2]).to be false
|
225
|
+
expect(Point[3,2]==point).to be false
|
226
|
+
end
|
227
|
+
|
228
|
+
it "must compare equal to an equal Vector" do
|
229
|
+
expect(point).to eq Vector[1,2]
|
230
|
+
expect(Vector[1,2]).to eq point
|
231
|
+
end
|
232
|
+
|
233
|
+
it "must not compare equal to an unequal Vector" do
|
234
|
+
expect(point == Vector[3,2]).to be false
|
235
|
+
expect(Vector[3,2] == point).to be false
|
236
|
+
end
|
237
|
+
|
238
|
+
it "must think that floats == ints" do
|
239
|
+
expect(Point[1,2]).to eq Point[1.0,2.0]
|
240
|
+
expect(Point[1.0,2.0]).to eq Point[1,2]
|
241
|
+
end
|
242
|
+
|
243
|
+
it "must not think that floats eql? ints" do
|
244
|
+
expect(Point[1,2].eql? Point[1.0,2.0]).to be false
|
245
|
+
expect(Point[1.0,2.0].eql? Point[1,2]).to be false
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe "spaceship" do
|
250
|
+
it "must spaceship with another Point of the same length" do
|
251
|
+
expect(Point[1,2] <=> Point[0,3]).to eq Point[1,-1]
|
252
|
+
end
|
253
|
+
|
254
|
+
it "must spaceship with another Point of different length" do
|
255
|
+
expect(Point[1,2] <=> Point[0,3,4]).to eq Point[1,-1]
|
256
|
+
expect(Point[1,2,4] <=> Point[0,3]).to eq Point[1,-1]
|
257
|
+
end
|
258
|
+
|
259
|
+
it "must spaceship with an Array" do
|
260
|
+
expect(Point[1,2] <=> [0,3]).to eq Point[1,-1]
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|