laptimer-geometry 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+ require File.expand_path('../../src/lap_counter', __FILE__)
3
+
4
+ describe LapCounter do
5
+ it "should find a straight segment witch is valid" do
6
+ true.should be_true
7
+ end
8
+ end
data/spec/line_spec.rb ADDED
@@ -0,0 +1,137 @@
1
+ require 'spec_helper'
2
+ require File.expand_path('../../src/geometry', __FILE__)
3
+
4
+ describe Line do
5
+ before do
6
+ # first semi-line
7
+ @pointA = {:x => 2, :y => 1}
8
+ @pointB = {:x => 4, :y => 3}
9
+ #second semi-line
10
+ @pointC = {:x => 3, :y => 1}
11
+ @pointD = {:x => 2, :y => 4}
12
+ #third semi-line
13
+ @pointE = {:x => 3, :y => 0}
14
+ @pointF = {:x => 1, :y => 2}
15
+ end
16
+
17
+ it "should find the gradient of the straight line" do
18
+ sl = Line.new(@pointA, @pointB)
19
+ sl.gradient.should be_eql(1.to_f)
20
+ end
21
+
22
+ it "should find the linear touch of the line" do
23
+ sl = Line.new(@pointA, @pointB)
24
+ sl.linear_touch.should be_eql(-1.to_f)
25
+
26
+ # Double Test
27
+ sl2 = Line.new(@pointE, @pointF)
28
+ sl2.linear_touch.should be_eql(3.to_f)
29
+ end
30
+
31
+ it "should include anotther point from the line" do
32
+ pointC = {:x => 3, :y => 2}
33
+ sl = Line.new(@pointA, @pointB)
34
+ sl.include?(pointC).should be_true
35
+ end
36
+
37
+ it "should intercept with the other line (3,0) and (1,2)" do
38
+ sl = Line.new(@pointA, @pointB)
39
+ sl2 = Line.new(@pointC, @pointD)
40
+ sl.intercept?(sl2).should be_true
41
+ sl3 = Line.new(@pointE, @pointF)
42
+ sl.intercept?(sl3).should be_true
43
+ end
44
+
45
+ it "two concurrent lines should intercept at (2,2)" do
46
+ pA = {:x => 1, :y => 1}
47
+ pB = {:x => 3, :y => 3}
48
+ pC = {:x => 1, :y => 3}
49
+ pD = {:x => 3, :y => 1}
50
+ sl = Line.new(pA, pB)
51
+ sl2 = Line.new(pC, pD)
52
+ sl.intercept?(sl2).should be_true
53
+ sl.intercept_at(sl2).should be_eql({:x => 2.0, :y => 2.0})
54
+ end
55
+
56
+ it "should not intercept with a parallel line" do
57
+ pA = {:x => 1, :y => 1}
58
+ pB = {:x => 3, :y => 3}
59
+ pC = {:x => 2, :y => 1}
60
+ pD = {:x => 3, :y => 2}
61
+ sl = Line.new(pA, pB)
62
+ sl2 = Line.new(pC, pD)
63
+ sl.intercept?(sl2).should be_false
64
+ end
65
+
66
+ it "should intercept between the 2 original points of the first line" do
67
+ pA = {:x => 1, :y => 1}
68
+ pB = {:x => 3, :y => 3}
69
+ pC = {:x => 1, :y => 3}
70
+ pD = {:x => 3, :y => 1}
71
+ sl = Line.new(pA, pB)
72
+ sl2 = Line.new(pC, pD)
73
+ sl.intercept_line_segment?(sl2).should be_true
74
+ end
75
+
76
+ it "should not find an interception between the two segments" do
77
+ pA = {:x => 1, :y => 1}
78
+ pB = {:x => 3, :y => 3}
79
+ pC = {:x => 4, :y => 3}
80
+ pD = {:x => 6, :y => 1}
81
+ sl = Line.new(pA, pB)
82
+ sl2 = Line.new(pC, pD)
83
+ sl.intercept_line_segment?(sl2).should be_false
84
+ end
85
+
86
+ it "should intercept event in the negative Y quadrant" do
87
+ flag_line = Line.new({:x => 1, :y => -1, }, {:x => 3, :y => -4})
88
+ segment1 = Line.new({:x => 1, :y => -3}, {:x => 3, :y => -1})
89
+
90
+ flag_line.intercept_line_segment?(segment1).should be_true
91
+ end
92
+
93
+
94
+ it "should intercept event in the negative X quadrant" do
95
+ flag_line = Line.new({:x => -1, :y => 1, }, {:x => -3, :y => 4})
96
+ segment1 = Line.new({:x => -3, :y => 1}, {:x => -1, :y => 3})
97
+
98
+ flag_line.intercept_line_segment?(segment1).should be_true
99
+ end
100
+
101
+ it "should intercept event in the all negative quadrant" do
102
+ flag_line = Line.new({:x => -1, :y => -1, }, {:x => -3, :y => -3})
103
+ segment1 = Line.new({:x => -1, :y => -3}, {:x => -2, :y => -2})
104
+
105
+ flag_line.intercept_line_segment?(segment1).should be_true
106
+ end
107
+
108
+ it "should intercept when segments are in different quadrants" do
109
+ flag_line = Line.new({:x => -1, :y => 1, }, {:x => -3, :y => 4})
110
+ segment1 = Line.new({:x => 1, :y => 3}, {:x => 3, :y => 1})
111
+
112
+ flag_line.intercept_line_segment?(segment1).should be_false
113
+ end
114
+
115
+ it "should calculate the distance using the Earth radius Nelson Piquet" do
116
+ point1 = {:x => -47.900648, :y => -15.772954}
117
+ point2 = {:x => -47.900365, :y => -15.772869}
118
+ point3 = {:x => -47.90025, :y => -15.772835}
119
+ Geometry.calculate_distance(point1, point2).should be_true
120
+ Geometry.calculate_distance(point1, point3).should be_true
121
+ end
122
+
123
+ it "should find the crossing segment with some real coordinates - Aut. Nelson Piquet" do
124
+ flag_line = Line.new({:x => -47.900333, :y => -15.772656, }, {:x => -47.900148, :y => -15.773056})
125
+ segment1 = Line.new({:x => -47.900648, :y => -15.772954}, {:x =>-47.899923, :y => -15.772683})
126
+
127
+ flag_line.intercept_line_segment?(segment1).should be_true
128
+ end
129
+
130
+ it "should not accept the crossing form real segments with they do not touch - Aut. Nelson Piquet" do
131
+ flag_line = Line.new({:x => -47.900333, :y => -15.772656, }, {:x => -47.900148, :y => -15.773056})
132
+ segment1 = Line.new({:x => -47.900648, :y => -15.772954}, {:x =>-47.900365, :y => -15.772869})
133
+
134
+ flag_line.intercept_line_segment?(segment1).should be_false
135
+ end
136
+
137
+ end
data/src/geometry.rb ADDED
@@ -0,0 +1,129 @@
1
+ module Geometry
2
+ include Math
3
+ extend Math
4
+
5
+ def distance(point1, point2)
6
+ x1 = (point1[:x] > 0) ? point1[:x] : (point1[:x]*-1 )
7
+ x2 = (point2[:x] > 0) ? point2[:x] : (point2[:x]*-1 )
8
+ y1 = (point1[:y] > 0) ? point1[:y] : (point1[:y]*-1 )
9
+ y2 = (point2[:y] > 0) ? point2[:y] : (point2[:y]*-1 )
10
+ hypot x1 - x2, y1 - y2
11
+ end
12
+
13
+ def calculate_distance(point1, point2)
14
+ x1 = point1[:x] * Math::PI / 180
15
+ x2 = point2[:x] * Math::PI / 180
16
+ y1 = point1[:y] * Math::PI / 180
17
+ y2 = point2[:y] * Math::PI / 180
18
+
19
+ s = sin(y2)*sin(y1)+(cos(y2)*cos(y1)*cos(x2 - x1))
20
+ rad_distance = acos(s)
21
+ degree_distance = rad_distance/(Math::PI/180)
22
+ earth_radius = 6378
23
+ distance_km = earth_radius*degree_distance
24
+ distance_km
25
+ end
26
+
27
+ module_function :distance
28
+ module_function :calculate_distance
29
+ end
30
+
31
+ class Line
32
+ include Math
33
+ def initialize(pointA, pointB)
34
+ @a, @b, @c = nil, nil, nil
35
+ @gradient = nil
36
+ @linear = nil
37
+ @pointA = pointA
38
+ @pointB = pointB
39
+ calculate_gradient
40
+ calculate_linear
41
+ end
42
+
43
+ def pointA
44
+ @pointA
45
+ end
46
+
47
+ def pointB
48
+ @pointB
49
+ end
50
+
51
+ def gradient(ac = nil)
52
+ if ac.nil?
53
+ @gradient
54
+ else
55
+ @gradient = ac
56
+ end
57
+
58
+ @gradient
59
+ end
60
+
61
+ # m(x2-x1) - y2 + y1 = 0
62
+ def include?(point)
63
+ result = @gradient * (point[:x] - @pointA[:x]) - point[:y] + @pointA[:y]
64
+ if(result == 0)
65
+ true
66
+ else
67
+ false
68
+ end
69
+ end
70
+
71
+ def intercept?(line)
72
+ (intercept_at(line).nil? == true) ? false : true
73
+ end
74
+
75
+ def intercept_at(line)
76
+ if @gradient != line.gradient
77
+ x = calculate_x_from_two_lines(self, line)
78
+ y = (@gradient*x.to_f) + @linear
79
+
80
+ new_point = { :x => sprintf('%.6f',x).to_f, :y => sprintf('%.6f',y).to_f}
81
+ return new_point
82
+ end
83
+
84
+ return nil
85
+ end
86
+
87
+ def linear_touch
88
+ @linear
89
+ end
90
+
91
+ def intercept_line_segment?(line)
92
+
93
+ # verify the distance
94
+ if intercept?(line)
95
+ interception_point = intercept_at(line)
96
+
97
+ distance_segment = Geometry.calculate_distance(line.pointA, line.pointB)
98
+ distance_A_line_segment = Geometry.calculate_distance(line.pointA, interception_point)
99
+ distance_B_line_segment = Geometry.calculate_distance(line.pointB, interception_point)
100
+
101
+ if distance_A_line_segment > distance_segment
102
+ return false
103
+ elsif distance_B_line_segment > distance_segment
104
+ return false
105
+ else
106
+ return true
107
+ end
108
+ end
109
+
110
+ return false
111
+ end
112
+
113
+ private
114
+ def calculate_gradient
115
+ @gradient = (@pointB[:y] - @pointA[:y]) / (@pointB[:x] - @pointA[:x]).to_f
116
+ end
117
+
118
+ def calculate_linear
119
+ @linear = nil
120
+ if not @gradient.nil?
121
+ @linear = @pointA[:y].to_f - (@gradient * @pointA[:x].to_f )
122
+ end
123
+ @linear
124
+ end
125
+
126
+ def calculate_x_from_two_lines(line1, line2)
127
+ (line2.linear_touch - line1.linear_touch) / (line1.gradient - line2.gradient)
128
+ end
129
+ end
@@ -0,0 +1,2 @@
1
+ class LapCounter
2
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: laptimer-geometry
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Eduardo Marques
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-10-03 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: " laptimer-geometry is a gem created to help the coordinates calculations used in a laptimer software.\n"
22
+ email: edhana@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - src/geometry.rb
31
+ - src/lap_counter.rb
32
+ - spec/lap_counter_spec.rb
33
+ - spec/line_spec.rb
34
+ homepage: http://github.com/edhana/laptimer-geometry
35
+ licenses: []
36
+
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - src
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ hash: 3
48
+ segments:
49
+ - 0
50
+ version: "0"
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.8.5
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: Implementation of basic 2D geometry algorithms, to help with a lap timer system written in Ruby
67
+ test_files:
68
+ - spec/lap_counter_spec.rb
69
+ - spec/line_spec.rb