winding-polygon 0.0.7 → 0.0.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f8c0df6f0765c7a2a1a8a060d1bb6c78510ecef9
4
- data.tar.gz: 6a0d538f3f4b56cf79c1445953da99f363130b27
3
+ metadata.gz: 7d8c4d4bd5532c0f315bfd93c56b2abeb83d71ba
4
+ data.tar.gz: 29690ff49b94f6005f0b3b4ab5d43f1467ee7f8a
5
5
  SHA512:
6
- metadata.gz: 77e7991c609942476f78bf67c2944ccdde34d88f99e17cbc51823f98634ce6000bae5aa47635dc811b24cc125da188b011fc2d2bef2b2f1bd1fd1aed8b9a656e
7
- data.tar.gz: 42958476e6918e4794eaa881c2c54624baf0aa41521865af4887341a36bdcb7d960c0abeaf326a0d54ba23614e87ebf4e2fc63c9d630b1ff09abc8de1d4410e1
6
+ metadata.gz: 3270fcf7ab126fb0aee8044905b9595fad6f17d54f47f4216941ccd365adc65bf838b43a3f2524fece01c3789655511271059628ab08bf4d0d0faddee437f30f
7
+ data.tar.gz: a6e3ea0045b182ae9b18d9f799b94a31ead92806b9d86b20e0a33430ab5005a8634795d345a288f5021bf5449638c2dd260acea6ed0ed4fe6bdbe8353ba08dc6
@@ -1,15 +1,79 @@
1
1
  require "winding-polygon/version"
2
+ require "winding-polygon/vector"
2
3
 
3
4
  module WindingPolygon
4
5
 
5
- def self.decompose(input_polygon, output_polygons)
6
- output_polygons = Array.new if output_polygons.nil?
7
- result = input_polygon.decompose
8
- if result.instance_of?(Array) && result.size==1 && !output_polygons.include?(input_polygon)
9
- output_polygons.concat(result)
10
- else
11
- decompose(result[0],output_polygons )
12
- decompose(result[1],output_polygons )
6
+ def self.decompose(input_polygon)
7
+
8
+ intersection_points = input_polygon.get_intersection_points
9
+ return input_polygon if intersection_points.nil? || intersection_points.size==0
10
+
11
+ input_polygon.simple_segments.sort_by!{|seg| [seg.left_point] }
12
+ simple_polygons = Array.new
13
+ while !input_polygon.simple_segments.nil? && input_polygon.simple_segments.size>=3
14
+ simple_polygons << get_one_simple_polygon(get_first_segment(input_polygon), input_polygon)
15
+ end
16
+ simple_polygons
17
+
18
+ end
19
+
20
+ def self.get_one_simple_polygon(first_segment, input_polygon)
21
+ current_simple_polygon = Array.new
22
+ current_simple_polygon_vertices = Array.new
23
+ current_simple_polygon << first_segment
24
+ current_simple_polygon_vertices << first_segment.left_point << first_segment.right_point
25
+ current_point = first_segment.right_point
26
+
27
+ while !input_polygon.simple_segments.nil? && input_polygon.simple_segments.size>=1
28
+ previous_edge = current_simple_polygon.last.edge
29
+ next_segment_candidates = input_polygon.simple_segments.select { |seg| seg.edge!=previous_edge && (seg.left_point == current_point ||seg.right_point == current_point) }.dup
30
+
31
+ if !next_segment_candidates.nil? && next_segment_candidates.size>=2
32
+ #determine previous segment vector
33
+ if current_point == current_simple_polygon.last.left_point
34
+ v0 = Vector.new(current_simple_polygon.last.right_point.x-current_point.x, current_simple_polygon.last.right_point.y-current_point.y)
35
+ else
36
+ v0 = Vector.new(current_simple_polygon.last.left_point.x-current_point.x, current_simple_polygon.last.left_point.y-current_point.y)
37
+ end
38
+
39
+ #determine next segment vector
40
+ if current_point == next_segment_candidates[0].left_point
41
+ v1 = Vector.new(next_segment_candidates[0].right_point.x-current_point.x, next_segment_candidates[0].right_point.y-current_point.y)
42
+ else
43
+ v1 = Vector.new(next_segment_candidates[0].left_point.x-current_point.x, next_segment_candidates[0].left_point.y-current_point.y)
44
+ end
45
+
46
+ if v1.cross_product(v0) > 0
47
+ current_simple_polygon << next_segment_candidates[0]
48
+ else
49
+ current_simple_polygon << next_segment_candidates[1]
50
+ end
51
+ else
52
+ current_simple_polygon << next_segment_candidates[0]
53
+ end
54
+
55
+ if current_simple_polygon.last.left_point == current_point
56
+ current_point = current_simple_polygon.last.right_point
57
+ else
58
+ current_point = current_simple_polygon.last.left_point
59
+ end
60
+
61
+ current_simple_polygon_vertices << current_point
62
+
63
+ input_polygon.simple_segments.delete_if { |seg| seg.left_point==current_simple_polygon.last.left_point && seg.right_point==current_simple_polygon.last.right_point }
64
+
65
+ return current_simple_polygon_vertices if current_simple_polygon.first.left_point == current_simple_polygon.last.left_point
66
+
13
67
  end
68
+ current_simple_polygon_vertices
69
+ end
70
+
71
+ def self.get_first_segment(input_polygon)
72
+ start_point = input_polygon.simple_segments[0].left_point
73
+ first_segment_candidates = input_polygon.simple_segments.select { |seg| seg.left_point == start_point }.dup
74
+ first_segment_candidates.sort!
75
+ input_polygon.simple_segments.delete_if { |seg| seg.left_point==first_segment_candidates[0].left_point && seg.right_point==first_segment_candidates[0].right_point }
76
+ first_segment_candidates[0]
14
77
  end
78
+
15
79
  end
@@ -46,6 +46,11 @@ module WindingPolygon
46
46
  d = search(v)
47
47
  return nil if d.nil?
48
48
 
49
+ if @root.height==1 && @root.value==v
50
+ @root = nil
51
+ return nil
52
+ end
53
+
49
54
  if d.left == nil or d.right == nil
50
55
  n = d
51
56
  else #both children exist
@@ -26,14 +26,29 @@ module WindingPolygon
26
26
  @events[a][:type] = 'right'
27
27
  @events[b][:type] = 'left'
28
28
  end
29
-
30
- @events[a][:other_y]=@events[b][:vertex].y
31
- @events[b][:other_y]=@events[a][:vertex].y
32
29
  end
33
30
 
34
31
  # sort events lexicographically
35
- @events.sort!{|a,b| a[:vertex].compare(b[:vertex]).nonzero? || a[:type]<=>b[:type] || a[:other_y]<=>b[:other_y] }
32
+ #@events.sort!{|a,b| [a[:vertex], b[:type], a[:other_endpoint]] <=> [b[:vertex], a[:type], b[:other_endpoint]] }
33
+ @events.sort!{|a,b| [a[:vertex], b[:type]] <=> [b[:vertex], a[:type]] }
34
+
35
+ end
36
+
37
+ def insert(point_hash)
38
+ for i in 0..@events.size-1
39
+ next if @events[i][:vertex].x < point_hash[:point].x
40
+ next if @events[i][:vertex].x == point_hash[:point].x && @events[i][:vertex].y<point_hash[:point].y
41
+ @events.insert(i,{:type=>'intersection_point',:vertex=>point_hash[:point],:edge1=>point_hash[:edge1],:edge2=>point_hash[:edge2]})
42
+ break
43
+ end
44
+ end
45
+
46
+ def exist(point)
47
+ for i in 0..@events.size-1
48
+ return true if @events[i][:vertex] ==point
49
+ end
36
50
 
51
+ return false
37
52
  end
38
53
 
39
54
  end
@@ -42,6 +42,18 @@ module WindingPolygon
42
42
  @x==other_point.x && @y==other_point.y
43
43
  end
44
44
 
45
+ def < (other_point)
46
+ return true if @x < other_point.x
47
+ return true if @x == other_point.x && @y < other_point.y
48
+ return false
49
+ end
50
+
51
+ def > (other_point)
52
+ return true if @x > other_point.x
53
+ return true if @x == other_point.x && @y > other_point.y
54
+ return false
55
+ end
56
+
45
57
  # tests if point is Left|On|Right of the line P0 to P1.
46
58
  #
47
59
  # returns:
@@ -2,10 +2,32 @@ module WindingPolygon
2
2
  class Polygon
3
3
  attr_reader :vertices
4
4
  attr_reader :intersection_points
5
+ attr_accessor :segments
6
+ attr_accessor :simple_segments
5
7
 
6
8
  def initialize(points)
7
9
  @vertices = points
8
10
  @intersection_points = Array.new
11
+
12
+ @simple_segments = Array.new
13
+
14
+ @segments = Array.new
15
+ for i in 0..@vertices.length-2
16
+
17
+ seg = Segment.new({:edge=>i})
18
+
19
+ if @vertices[i]<@vertices[i+1]
20
+ seg.left_point = @vertices[i]
21
+ seg.right_point = @vertices[i+1]
22
+ else
23
+ seg.left_point = @vertices[i+1]
24
+ seg.right_point = @vertices[i]
25
+ end
26
+
27
+ @segments << seg
28
+
29
+ end
30
+
9
31
  end
10
32
 
11
33
  def is_simple
@@ -45,14 +67,41 @@ module WindingPolygon
45
67
 
46
68
  if e[:type] == 'left'
47
69
  s = sweep_line.add(e)
70
+ find_intersection_point_between_segments(s,s.above, event_queue, sweep_line)
71
+ find_intersection_point_between_segments(s,s.below, event_queue, sweep_line)
72
+ end
48
73
 
49
- add_to_intersection_point_collection(sweep_line.intersect(s, s.above))
50
- add_to_intersection_point_collection(sweep_line.intersect(s, s.below))
51
-
52
- else
53
- s = sweep_line.find(e)
54
- add_to_intersection_point_collection(sweep_line.intersect(s.above, s.below))
74
+ if e[:type] == 'right'
75
+ s = sweep_line.find_segment(@segments[e[:edge]])
76
+ point = sweep_line.intersect(s.above, s.below)
77
+ event_queue.insert(point_hash_with_edge_info(point, s.above, s.below)) if !point.nil? && !event_queue.exist(point)
55
78
  sweep_line.remove(s)
79
+ @simple_segments << s
80
+ end
81
+
82
+ if e[:type] == 'intersection_point'
83
+ add_to_intersection_point_collection(e[:vertex])
84
+
85
+ s1 = sweep_line.find_segment(@segments[e[:edge1]])
86
+ sweep_line.remove(s1)
87
+ s11 = s1.dup
88
+ s11.right_point = e[:vertex]
89
+ @simple_segments << s11
90
+
91
+
92
+ s2 = sweep_line.find_segment(@segments[e[:edge2]])
93
+ sweep_line.remove(s2)
94
+ s22 = s2.dup
95
+ s22.right_point = e[:vertex]
96
+ @simple_segments << s22
97
+
98
+ @segments[e[:edge1]].left_point=e[:vertex]
99
+ s1 = sweep_line.add_segment(@segments[e[:edge1]])
100
+ find_intersection_point_between_segments(s1,s1.below, event_queue, sweep_line)
101
+
102
+ @segments[e[:edge2]].left_point=e[:vertex]
103
+ s2 = sweep_line.add_segment(@segments[e[:edge2]])
104
+ find_intersection_point_between_segments(s2,s2.above, event_queue, sweep_line)
56
105
  end
57
106
 
58
107
  e = event_queue.events.shift
@@ -62,6 +111,11 @@ module WindingPolygon
62
111
  @intersection_points
63
112
  end
64
113
 
114
+ def find_intersection_point_between_segments(s1,s2, event_queue, sweep_line)
115
+ point =sweep_line.intersect(s1, s2)
116
+ event_queue.insert(point_hash_with_edge_info(point, s1, s2)) if !point.nil? && !event_queue.exist(point)
117
+ end
118
+
65
119
  def add_to_intersection_point_collection(point)
66
120
  @intersection_points << point if !point.nil? && !@intersection_points.any?{|p| p.compare(point)==0}
67
121
  end
@@ -93,16 +147,12 @@ module WindingPolygon
93
147
  return nil
94
148
  end
95
149
 
96
- def decompose
97
- intersection_point = get_first_intersection_point_hash
98
- return [self] if intersection_point.nil?
99
- first_polygon = Polygon.new((@vertices[0..intersection_point[:edge1]]<<intersection_point[:point]).concat( @vertices[intersection_point[:edge2]+1,@vertices.length-intersection_point[:edge2]]))
100
- second_polygon = Polygon.new(@vertices[intersection_point[:edge1]+1..intersection_point[:edge2]].insert(0,intersection_point[:point])<<intersection_point[:point])
101
- return [first_polygon,second_polygon]
102
- end
103
-
104
150
  def point_hash_with_edge_info(point, s1, s2)
105
- edges=[s1.edge, s2.edge].sort!
151
+ if s1.right_point.y<s2.right_point.y
152
+ edges=[s1.edge, s2.edge]
153
+ else
154
+ edges=[s2.edge, s1.edge]
155
+ end
106
156
  {:point => point, :edge1 => edges[0], :edge2 => edges[1]}
107
157
  end
108
158
 
@@ -14,20 +14,38 @@ module WindingPolygon
14
14
  end
15
15
 
16
16
  def <(other_segment)
17
- return true if @left_point.y < other_segment.left_point.y
17
+ if @left_point == other_segment.left_point
18
+ return true if @right_point.is_left(other_segment.left_point,other_segment.right_point)<0
19
+ end
20
+
21
+ return true if @left_point.is_left(other_segment.left_point,other_segment.right_point)<0
22
+
18
23
  return false
19
24
  end
20
25
 
21
26
  def >(other_segment)
22
- return true if @left_point.y > other_segment.left_point.y
27
+ if @left_point == other_segment.left_point
28
+ return true if @right_point.is_left(other_segment.left_point,other_segment.right_point)>0
29
+ end
30
+
31
+ return true if @left_point.is_left(other_segment.left_point,other_segment.right_point)>0
32
+
23
33
  return false
24
34
  end
25
35
 
26
36
  def == (other_segment)
27
- return true if @left_point.y == other_segment.left_point.y
37
+ return true if @left_point == other_segment.left_point && @right_point == other_segment.right_point && @edge==other_segment.edge
28
38
  return false
29
39
  end
30
40
 
41
+ def <=> other_segment
42
+ raise Exception.new("Self is edge=#{@edge}, the other_segment is nil") if other_segment.nil?
43
+
44
+ return 1 if self > other_segment
45
+ return -1 if self < other_segment
46
+ return 0 if self == other_segment
47
+ end
48
+
31
49
  def to_s
32
50
  return "edge:#{@edge.to_s}"
33
51
  end
@@ -48,5 +66,35 @@ module WindingPolygon
48
66
 
49
67
  Point.new(x, y)
50
68
  end
69
+
70
+ def is_intersected_with(other_segment)
71
+ # no intersect if either segment doesn't existend
72
+ return false if other_segment.nil?
73
+
74
+ #test for existence of an intersect point
75
+ #other_segment left point sign
76
+ lsign = other_segment.left_point.is_left(@left_point, @right_point)
77
+ #other_segment right point sign
78
+ rsign = other_segment.right_point.is_left(@left_point, @right_point)
79
+
80
+ # other_segment endpoints have same sign relative to it => on same side => no intersect is possible
81
+ return false if (lsign * rsign > 0)
82
+
83
+ # its left point sign
84
+ lsign = @left_point.is_left(other_segment.left_point, other_segment.right_point)
85
+ #its right point sign
86
+ rsign = @right_point.is_left(other_segment.left_point, other_segment.right_point)
87
+
88
+ # its endpoints have same sign relative to other_segment => on same side => no intersect is possible
89
+ return false if (lsign * rsign > 0)
90
+
91
+ return true
92
+
93
+ end
94
+
95
+ def is_on_the_line(point)
96
+ return true if @left_point.x<point.x && point.x < @right_point.x && (@left_point.y<point.y && point.y < @right_point.y || @right_point.y<point.y && point.y < @left_point.y) && ((point.x - @left_point.x) / (@right_point.x - @left_point.x) - (point.y - @left_point.y) / (@right_point.y - @left_point.y)).abs<0.00000000001
97
+ false
98
+ end
51
99
  end
52
100
  end
@@ -45,6 +45,29 @@ module WindingPolygon
45
45
 
46
46
  end
47
47
 
48
+ def add_segment(seg)
49
+ seg.below=seg.above=nil
50
+
51
+ # Add node to tree and setup linkages to "above" and "below"
52
+ # edges as per algorithm
53
+ nd = @tree.insert(seg)
54
+
55
+ nx = nd.next
56
+ np = nd.prev
57
+
58
+ if !nx.nil?
59
+ seg.above = nx.value
60
+ seg.above.below = seg
61
+ end
62
+
63
+ if !np.nil?
64
+ seg.below = np.value
65
+ seg.below.above = seg
66
+ end
67
+ return seg
68
+
69
+ end
70
+
48
71
  def find(event)
49
72
  # need a segment to find it in the tree
50
73
  seg = Segment.new(event)
@@ -68,6 +91,12 @@ module WindingPolygon
68
91
  node.value
69
92
  end
70
93
 
94
+ def find_segment(seg)
95
+ node = @tree.search(seg)
96
+ return nil if node.nil?
97
+ node.value
98
+ end
99
+
71
100
  def remove(seg)
72
101
  nd = @tree.search(seg)
73
102
  return if nd.nil?
@@ -81,13 +110,14 @@ module WindingPolygon
81
110
  @tree.delete(seg)
82
111
  end
83
112
 
84
- def switch(s1,s2)
113
+ def swap(s1,s2)
85
114
  nd1 = @tree.search(s1)
86
115
  return if nd1.nil?
87
116
 
88
117
  nd2 = @tree.search(s2)
89
118
  return if nd2.nil?
90
119
 
120
+
91
121
  nx1 = nd1.next
92
122
  nx1.value.below = nd2.value if !nx1.nil?
93
123
 
@@ -100,6 +130,28 @@ module WindingPolygon
100
130
  np2 = nd2.prev
101
131
  np2.value.above = nd1.value if !np2.nil?
102
132
 
133
+
134
+ nd1.value = s2
135
+ nd2.value = s1
136
+
137
+ temp = s1
138
+ s1=s2
139
+ s2=temp
140
+
141
+
142
+
143
+ temp_above = s1.above
144
+ temp_below = s1.below
145
+
146
+ s1.above = s2.above
147
+ s1.below = s2.below
148
+
149
+ s2.above = temp_above
150
+ s2.below = temp_below
151
+
152
+
153
+ [s1,s2]
154
+
103
155
  end
104
156
 
105
157
  #test intersect of 2 segments and return: nil when none, point when intersecting
@@ -0,0 +1,50 @@
1
+ module WindingPolygon
2
+ class Vector < Struct.new(:x, :y)
3
+ def ==(vector)
4
+ x === vector.x && y === vector.y
5
+ end
6
+
7
+ # Modulus of vector. Also known as length, size or norm
8
+ def modulus
9
+ Math.hypot(x ,y)
10
+ end
11
+
12
+ # z-coordinate of cross product (also known as vector product or outer product)
13
+ # It is positive if other vector should be turned counter-clockwise in order to superpose them.
14
+ # It is negetive if other vector should be turned clockwise in order to superpose them.
15
+ # It is zero when vectors are collinear.
16
+ # Remark: x- and y- coordinates of plane vectors cross product are always zero
17
+ def cross_product(vector)
18
+ x * vector.y - y * vector.x
19
+ end
20
+
21
+ # Scalar product, also known as inner product or dot product
22
+ def scalar_product(vector)
23
+ x * vector.x + y * vector.y
24
+ end
25
+
26
+ def collinear_with?(vector)
27
+ cross_product(vector) === 0
28
+ end
29
+
30
+ def +(vector)
31
+ Vector.new(x + vector.x, y + vector.y)
32
+ end
33
+
34
+ def -(vector)
35
+ self + (-1) * vector
36
+ end
37
+
38
+ def *(scalar)
39
+ Vector.new(x * scalar, y * scalar)
40
+ end
41
+
42
+ def coerce(scalar)
43
+ if scalar.is_a?(Numeric)
44
+ [self, scalar]
45
+ else
46
+ raise ArgumentError, "Vector: cannot coerce #{scalar.inspect}"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,3 +1,3 @@
1
1
  module WindingPolygon
2
- VERSION = "0.0.7"
2
+ VERSION = "0.0.8"
3
3
  end
data/spec/polygon_spec.rb CHANGED
@@ -174,25 +174,28 @@ describe "Polygon" do
174
174
  points = JSON.parse("[[-98.4609375000036,40.3051841980949],[-98.4609375000036,38.057277897745],[-96.0878906250017,40.1038062719331],[-96.6152343750031,37.9880405545487],[-98.4609375000036,40.3051841980949]]")
175
175
  polygon = WindingPolygon::Polygon.new(points.map{|item| WindingPolygon::Point.new(item[0],item[1])})
176
176
 
177
- polygon.get_first_intersection_point_hash.should == {:point=>WindingPolygon::Point.new(-97.39951856124108,38.97265129300353),:edge1=>1,:edge2=>3 }
177
+ polygon.get_first_intersection_point_hash.should == {:point=>WindingPolygon::Point.new(-97.39951856124108,38.97265129300353),:edge1=>3,:edge2=>1 }
178
178
 
179
179
  end
180
180
 
181
- it 'should decompose a practical complex polygon into two simple polygons' do
181
+ it 'get practical complex polygon 5 intersection points' do
182
182
 
183
- points = JSON.parse("[[-98.4609375000036,40.3051841980949],[-98.4609375000036,38.057277897745],[-96.0878906250017,40.1038062719331],[-96.6152343750031,37.9880405545487],[-98.4609375000036,40.3051841980949]]")
183
+ points = JSON.parse("[[-98.4609375000036,40.3051841980949],[-98.4609375000036,38.057277897745],[-96.0878906250017,40.1038062719331],[-96.6152343750031,37.9880405545487],[-97.6152343750031,40.9880405545487],[-98.0152343750031,37.9880405545487],[-98.4609375000036,40.3051841980949]]")
184
184
  polygon = WindingPolygon::Polygon.new(points.map{|item| WindingPolygon::Point.new(item[0],item[1])})
185
185
 
186
- multi_polygons = polygon.decompose
187
- multi_polygons.should_not be_nil
186
+ polygon.get_intersection_points.size.should == 3
187
+
188
+ end
189
+
190
+ it 'get practical complex polygon 6 intersection points' do
191
+
192
+ points = JSON.parse("[["+"-115.86328125000495 34.15328996737699,-119.818359375002 45.45290147989422,-81.4101562500034 35.021556836103684,-110.50195312500117 46.97322061018078,-92.66015625000608 43.38159203778072,-94.68164062500398 46.18790776409503,-115.07226562500733 41.63237589430056,-104.17382812500229 40.17099871188528,-100.3945312500027 32.46399995947513,-95.82421875000551 37.08201838643584,-88.52929687500257 35.95188058401679,-84.31054687500303 33.64262887366194,-83.16796875000327 34.51617009551454,-82.37695312500296 32.538125275571225,-84.31054687500303 39.36031127050692,-72.79687500000323 42.739443407624286,-115.86328125000495 34.15328996737699".gsub(',','],[').gsub(' ',',')+"]]")
193
+ polygon = WindingPolygon::Polygon.new(points.map{|item| WindingPolygon::Point.new(item[0],item[1])})
188
194
 
189
- points1 = JSON.parse("[[-98.4609375000036,40.3051841980949],[-98.4609375000036,38.057277897745],[-97.39951856124108,38.97265129300353],[-98.4609375000036,40.3051841980949]]")
190
- polygon1 = WindingPolygon::Polygon.new(points1.map{|item| WindingPolygon::Point.new(item[0],item[1])})
191
- multi_polygons[0].should ==polygon1
195
+ intersection_points = polygon.get_intersection_points
196
+ intersection_points.size.should == 8
197
+ polygon.simple_segments.size == 32
192
198
 
193
- points2 = JSON.parse("[[-97.39951856124108,38.97265129300353],[-96.0878906250017,40.1038062719331],[-96.6152343750031,37.9880405545487],[-97.39951856124108,38.97265129300353]]")
194
- polygon2 = WindingPolygon::Polygon.new(points2.map{|item| WindingPolygon::Point.new(item[0],item[1])})
195
- multi_polygons[1].should ==polygon2
196
199
 
197
200
  end
198
201
 
data/spec/segment_spec.rb CHANGED
@@ -17,4 +17,16 @@ describe "Segment" do
17
17
  intersection_point.y.should ==2
18
18
 
19
19
  end
20
+
21
+ it "point is on the line segment" do
22
+
23
+ segment1 = WindingPolygon::Segment.new({:edge=>0})
24
+ segment1.left_point = WindingPolygon::Point.new(0,0)
25
+ segment1.right_point = WindingPolygon::Point.new(3,3)
26
+
27
+ segment1.is_on_the_line(WindingPolygon::Point.new(1.5,1.5)).should == true
28
+ segment1.is_on_the_line(WindingPolygon::Point.new(1.5,2.5)).should == false
29
+ segment1.is_on_the_line(WindingPolygon::Point.new(3.5,3.5)).should == false
30
+
31
+ end
20
32
  end
@@ -8,14 +8,17 @@ describe "SweepLine" do
8
8
  sweep_line = WindingPolygon::SweepLine.new(polygon)
9
9
  event_queue = WindingPolygon::EventQueue.new(polygon)
10
10
 
11
- event = event_queue.events.pop
11
+ event = event_queue.events.shift
12
12
  while !event.nil?
13
- sweep_line.add(event) if event[:type]=='left'
14
- event = event_queue.events.pop
15
- end
16
-
17
- for i in 0..3 do
18
- sweep_line.find({:edge=>i}).should_not be_nil
13
+ if event[:type]=='left'
14
+ sweep_line.add(event)
15
+ sweep_line.find(event).should_not be_nil
16
+ else
17
+ seg = sweep_line.find(event)
18
+ seg.should_not be_nil
19
+ sweep_line.remove(seg)
20
+ end
21
+ event = event_queue.events.shift
19
22
  end
20
23
 
21
24
  end
@@ -5,13 +5,18 @@ describe WindingPolygon do
5
5
  WindingPolygon::VERSION.should_not be_nil
6
6
  end
7
7
 
8
- it 'should decompose a practical complex polygon into two simple polygons' do
9
8
 
9
+ =begin
10
+ it 'should decompose a practical complex polygon into two simple polygons' do
11
+ puts 'should decompose a practical complex polygon into two simple polygons'
10
12
  points = JSON.parse("[[-98.4609375000036,40.3051841980949],[-98.4609375000036,38.057277897745],[-96.0878906250017,40.1038062719331],[-96.6152343750031,37.9880405545487],[-98.4609375000036,40.3051841980949]]")
11
13
  polygon = WindingPolygon::Polygon.new(points.map{|item| WindingPolygon::Point.new(item[0],item[1])})
12
14
 
13
15
  multi_polygons = []
16
+ t1 = Time.now
14
17
  WindingPolygon.decompose(polygon,multi_polygons)
18
+ t2 = Time.now
19
+ puts "elapsed time: #{(t2-t1)*1000.to_i} ms"
15
20
  multi_polygons.should_not be_nil
16
21
 
17
22
  points1 = JSON.parse("[[-98.4609375000036,40.3051841980949],[-98.4609375000036,38.057277897745],[-97.39951856124108,38.97265129300353],[-98.4609375000036,40.3051841980949]]")
@@ -26,12 +31,15 @@ describe WindingPolygon do
26
31
 
27
32
 
28
33
  it 'should decompose a practical complex polygon into four simple polygons' do
29
-
34
+ puts 'should decompose a practical complex polygon into four simple polygons'
30
35
  points = JSON.parse("[[-98.4609375000036,40.3051841980949],[-98.4609375000036,38.057277897745],[-96.0878906250017,40.1038062719331],[-96.6152343750031,37.9880405545487],[-97.6152343750031,40.9880405545487],[-98.0152343750031,37.9880405545487],[-98.4609375000036,40.3051841980949]]")
31
36
  polygon = WindingPolygon::Polygon.new(points.map{|item| WindingPolygon::Point.new(item[0],item[1])})
32
37
 
33
38
  multi_polygons = []
39
+ t1 = Time.now
34
40
  WindingPolygon.decompose(polygon,multi_polygons)
41
+ t2 = Time.now
42
+ puts "elapsed time: #{(t2-t1)*1000.to_i} ms"
35
43
  multi_polygons.should_not be_nil
36
44
 
37
45
  multi_polygons.size.should == 4
@@ -39,12 +47,15 @@ describe WindingPolygon do
39
47
  end
40
48
 
41
49
  it 'should decompose a production complex polygon 2 into two simple polygons' do
42
-
50
+ puts 'should decompose a production complex polygon 2 into two simple polygons'
43
51
  points = JSON.parse("[[-95.6968,29.93101],[-95.6992,29.93101],[-95.7016,29.9319],[-95.70401,29.93309],[-95.7071,29.93428],[-95.7095,29.93488],[-95.7119,29.93547],[-95.71431,29.93607],[-95.7174,29.93666],[-95.72014,29.93726],[-95.72255,29.93785],[-95.72564,29.93875],[-95.72804,29.93994],[-95.72598,29.95094],[-95.72358,29.95303],[-95.72152,29.95481],[-95.71877,29.95659],[-95.71637,29.95778],[-95.71362,29.95838],[-95.71122,29.95838],[-95.70847,29.95838],[-95.70538,29.95808],[-95.70195,29.95778],[-95.69851,29.95719],[-95.69405,29.956],[-95.69165,29.9554],[-95.6889,29.95451],[-95.68615,29.95332],[-95.68307,29.95184],[-95.68066,29.95124],[-95.67723,29.94856],[-95.67483,29.94648],[-95.67242,29.9438],[-95.67448,29.93607],[-95.67689,29.93339],[-95.67895,29.93161],[-95.68101,29.93131],[-95.68341,29.93131],[-95.68615,29.93131],[-95.6889,29.93101],[-95.69165,29.93101],[-95.69439,29.93101],[-95.69748,29.93161],[-95.70023,29.9322],[-95.70435,29.93339],[-95.70641,29.93428],[-95.70984,29.93458],[-95.6968,29.93101]]")
44
52
  polygon = WindingPolygon::Polygon.new(points.map{|item| WindingPolygon::Point.new(item[0].to_f,item[1].to_f)})
45
53
 
46
54
  multi_polygons = []
55
+ t1 = Time.now
47
56
  WindingPolygon.decompose(polygon,multi_polygons)
57
+ t2 = Time.now
58
+ puts "elapsed time: #{(t2-t1)*1000.to_i} ms"
48
59
  multi_polygons.should_not be_nil
49
60
 
50
61
  multi_polygons.size.should == 3
@@ -53,11 +64,15 @@ describe WindingPolygon do
53
64
 
54
65
  it 'should decompose a production complex polygon 3 into two simple polygons' do
55
66
 
67
+ puts 'should decompose a production complex polygon 3 into two simple polygons'
56
68
  points = JSON.parse("[[-95.6968,29.93101],[-95.6992,29.93101],[-95.7016,29.9319],[-95.70401,29.93309],[-95.7071,29.93428],[-95.70747130044843,29.934372825112106],[-95.70984,29.93458],[-95.6968,29.93101]]")
57
69
  polygon = WindingPolygon::Polygon.new(points.map{|item| WindingPolygon::Point.new(item[0].to_f,item[1].to_f)})
58
70
 
59
71
  multi_polygons = []
72
+ t1 = Time.now
60
73
  WindingPolygon.decompose(polygon,multi_polygons)
74
+ t2 = Time.now
75
+ puts "elapsed time: #{(t2-t1)*1000.to_i} ms"
61
76
  multi_polygons.should_not be_nil
62
77
 
63
78
  multi_polygons.size.should == 2
@@ -65,18 +80,102 @@ describe WindingPolygon do
65
80
  end
66
81
 
67
82
  it 'should decompose a production complex polygon 4 into two simple polygons' do
68
-
83
+ puts 'should decompose a production complex polygon 4 into two simple polygons'
69
84
  points = JSON.parse("[[-87.80356,41.96514],[-87.80115,41.96182],[-87.79909,41.96156],[-87.79635,41.96131],[-87.79394,41.96105],[-87.79188,41.9608],[-87.78914,41.95978],[-87.78708,41.95876],[-87.78467,41.95722],[-87.78193,41.95544],[-87.77987,41.95339],[-87.77781,41.9511],[-87.77541,41.94803],[-87.773,41.94446],[-87.7706,41.94063],[-87.76785,41.9368],[-87.76545,41.93271],[-87.76339,41.92658],[-87.76133,41.91687],[-87.76373,41.87522],[-87.76579,41.87445],[-87.76854,41.87394],[-87.7706,41.87368],[-87.77335,41.87343],[-87.77575,41.87266],[-87.77815,41.8724],[-87.78021,41.87215],[-87.78296,41.87164],[-87.78536,41.87113],[-87.78776,41.87113],[-87.78982,41.87113],[-87.79223,41.87113],[-87.79463,41.87113],[-87.79703,41.87113],[-87.79978,41.87138],[-87.80184,41.87189],[-87.8039,41.8724],[-87.8063,41.87317],[-87.80871,41.87419],[-87.81077,41.87471],[-87.81283,41.87547],[-87.81523,41.87649],[-87.81729,41.87726],[-87.81935,41.87752],[-87.82141,41.87777],[-87.82347,41.87803],[-87.82622,41.87828],[-87.82862,41.87854],[-87.83171,41.87854],[-87.83377,41.8788],[-87.83652,41.87931],[-87.83926,41.88007],[-87.84132,41.88058],[-87.84373,41.88135],[-87.84613,41.88237],[-87.84888,41.88365],[-87.85094,41.88493],[-87.85334,41.88697],[-87.8554,41.89055],[-87.85746,41.89515],[-87.85952,41.90026],[-87.86158,41.90435],[-87.85952,41.92913],[-87.85746,41.93373],[-87.8554,41.94139],[-87.85334,41.94573],[-87.85094,41.94778],[-87.84888,41.94905],[-87.84647,41.95059],[-87.84304,41.95263],[-87.84098,41.95365],[-87.83892,41.95442],[-87.83514,41.95671],[-87.83308,41.95773],[-87.83102,41.95901],[-87.82862,41.96003],[-87.82484,41.96207],[-87.82107,41.9631],[-87.81901,41.9631],[-87.81695,41.9631],[-87.81351,41.9631],[-87.81042,41.96284],[-87.80699,41.96233],[-87.80424,41.96207],[-87.80184,41.96156],[-87.79978,41.96105],[-87.79703,41.95901],[-87.80356,41.96514]]")
70
85
  polygon = WindingPolygon::Polygon.new(points.map{|item| WindingPolygon::Point.new(item[0].to_f,item[1].to_f)})
71
86
 
72
87
  multi_polygons = []
88
+ t1 = Time.now
73
89
  WindingPolygon.decompose(polygon,multi_polygons)
90
+ t2 = Time.now
91
+ puts "elapsed time: #{(t2-t1)*1000.to_i} ms"
74
92
  multi_polygons.should_not be_nil
75
93
 
76
94
  multi_polygons.size.should == 2
77
95
 
78
96
  end
79
97
 
98
+ it 'should decompose a production complex polygon 5 into two simple polygons' do
99
+ puts 'should decompose a production complex polygon 5 into two simple polygons'
100
+ points = JSON.parse("[[153.4229,-28.0902],[153.42346,-28.09104],[153.42395,-28.09178],[153.42435,-28.09254],[153.42487,-28.09328],[153.4229,-28.0902]]")
101
+ polygon = WindingPolygon::Polygon.new(points.map{|item| WindingPolygon::Point.new(item[0].to_f,item[1].to_f)})
102
+
103
+ multi_polygons = []
104
+ t1 = Time.now
105
+ WindingPolygon.decompose(polygon,multi_polygons)
106
+ t2 = Time.now
107
+ puts "elapsed time: #{(t2-t1)*1000.to_i} ms"
108
+ multi_polygons.should_not be_nil
109
+
110
+ multi_polygons.size.should == 2
111
+
112
+ end
113
+
114
+ it 'should decompose a production complex polygon 6 into three simple polygons' do
115
+ puts 'should decompose a production complex polygon 6 into three simple polygons'
116
+ points = JSON.parse("[["+"-78.17273 38.94346,-78.17685 38.93652,-78.18097 38.93171,-78.18578 38.92637,-78.1899 38.91836,-78.19196 38.91622,-78.17273 38.94346".gsub(',','],[').gsub(' ',',')+"]]")
117
+ polygon = WindingPolygon::Polygon.new(points.map{|item| WindingPolygon::Point.new(item[0].to_f,item[1].to_f)})
118
+
119
+ multi_polygons = []
120
+ t1 = Time.now
121
+ WindingPolygon.decompose(polygon,multi_polygons)
122
+ t2 = Time.now
123
+ puts "elapsed time: #{(t2-t1)*1000.to_i} ms"
124
+ multi_polygons.should_not be_nil
125
+
126
+ multi_polygons.size.should == 3
127
+
128
+ end
129
+
130
+ it 'should decompose a production complex polygon 7 into two simple polygons' do
131
+ puts 'should decompose a production complex polygon 7 into two simple polygons'
132
+ points = JSON.parse("[["+"-71.77812 41.42488,-71.774 41.42024,-71.76782 41.41818,-71.76301 41.41818,-71.75546 41.41818,-71.74859 41.41818,-71.74172 41.41818,-71.73486 41.41818,-71.7273 41.41818,-71.71906 41.41818,-71.71288 41.41818,-71.70739 41.41818,-71.70327 41.41818,-71.69778 41.41818,-71.69366 41.41612,-71.77812 41.42488".gsub(',','],[').gsub(' ',',')+"]]")
133
+ polygon = WindingPolygon::Polygon.new(points.map{|item| WindingPolygon::Point.new(item[0].to_f,item[1].to_f)})
134
+
135
+ multi_polygons = []
136
+ t1 = Time.now
137
+ WindingPolygon.decompose(polygon,multi_polygons)
138
+ t2 = Time.now
139
+ puts "elapsed time: #{(t2-t1)*1000.to_i} ms"
140
+ multi_polygons.should_not be_nil
141
+
142
+ multi_polygons.size.should == 2
143
+
144
+ end
145
+
146
+ it 'should decompose a production complex polygon 8 into four simple polygons' do
147
+ puts 'should decompose a production complex polygon 8 into four simple polygons'
148
+ points = JSON.parse("[["+"-88.69626 32.36626,-88.69697 32.36713,-88.69706 32.36722,-88.69713 32.3673,-88.69719 32.36738,-88.69729 32.36747,-88.69743 32.36758,-88.69749 32.36765,-88.69759 32.36777,-88.69767 32.36791,-88.69769 32.36798,-88.69626 32.36626".gsub(',','],[').gsub(' ',',')+"]]")
149
+ polygon = WindingPolygon::Polygon.new(points.map{|item| WindingPolygon::Point.new(item[0].to_f,item[1].to_f)})
150
+
151
+ multi_polygons = []
152
+ t1 = Time.now
153
+ WindingPolygon.decompose(polygon,multi_polygons)
154
+ t2 = Time.now
155
+ puts "elapsed time: #{(t2-t1)*1000.to_i} ms"
156
+ multi_polygons.should_not be_nil
157
+
158
+ multi_polygons.size.should == 4
159
+
160
+ end
161
+ =end
162
+
163
+
164
+ it 'should decompose Andy complex polygon into 7 simple polygons' do
165
+ puts 'should decompose Andy complex polygon into four simple polygons'
166
+ points = JSON.parse("[["+"-115.86328125000495 34.15328996737699,-119.818359375002 45.45290147989422,-81.4101562500034 35.021556836103684,-110.50195312500117 46.97322061018078,-92.66015625000608 43.38159203778072,-94.68164062500398 46.18790776409503,-115.07226562500733 41.63237589430056,-104.17382812500229 40.17099871188528,-100.3945312500027 32.46399995947513,-95.82421875000551 37.08201838643584,-88.52929687500257 35.95188058401679,-84.31054687500303 33.64262887366194,-83.16796875000327 34.51617009551454,-82.37695312500296 32.538125275571225,-84.31054687500303 39.36031127050692,-72.79687500000323 42.739443407624286,-115.86328125000495 34.15328996737699".gsub(',','],[').gsub(' ',',')+"]]")
167
+ polygon = WindingPolygon::Polygon.new(points.map{|item| WindingPolygon::Point.new(item[0].to_f,item[1].to_f)})
168
+
169
+ t1 = Time.now
170
+ multi_polygons = WindingPolygon.decompose(polygon)
171
+ t2 = Time.now
172
+ puts "elapsed time: #{(t2-t1)*1000.to_i} ms"
173
+
174
+
175
+ multi_polygons.size.should == 7
176
+
177
+ end
178
+
80
179
 
81
180
 
82
181
  end
data/test/test_avltree.rb CHANGED
@@ -65,6 +65,8 @@ class TestAVLTree < Test::Unit::TestCase
65
65
  assert_equal "[1, 5]", @tree.sort.inspect
66
66
  @tree.delete 1
67
67
  assert_equal "[5]", @tree.sort.inspect
68
+ @tree.delete 5
69
+ assert_equal nil, @tree.root
68
70
 
69
71
  @rot_tree.delete 1
70
72
  assert_equal 4.to_s, @rot_tree.root.to_s
@@ -0,0 +1,22 @@
1
+ require 'test/unit'
2
+ require 'winding-polygon/vector'
3
+
4
+ class ArithmeticsTest < Test::Unit::TestCase
5
+ include WindingPolygon
6
+
7
+ def test_summation
8
+ assert_equal Vector.new(4, 6), Vector.new(1, 2) + Vector.new(3, 4)
9
+ end
10
+
11
+ def test_subtraction
12
+ assert_equal Vector.new(-2, -4), Vector.new(1, 0) - Vector.new(3, 4)
13
+ end
14
+
15
+ def test_vector_multiplied_by_scalar
16
+ assert_equal Vector.new(-2, -4), Vector.new(1, 2) * -2
17
+ end
18
+
19
+ def test_scalar_multiplied_by_vector
20
+ assert_equal Vector.new(-2, -4), -2 * Vector.new(1, 2)
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ require 'test/unit'
2
+ require 'winding-polygon/vector'
3
+
4
+ class CollinearWithTest < Test::Unit::TestCase
5
+ include WindingPolygon
6
+
7
+ def test_vectors_are_collinear
8
+ vector1 = Vector.new(1, 2)
9
+ vector2 = Vector.new(2, 4)
10
+
11
+ assert vector1.collinear_with?(vector2)
12
+ end
13
+
14
+ def test_vectors_are_not_collinear
15
+ vector1 = Vector.new(1, 2)
16
+ vector2 = Vector.new(1, 1)
17
+
18
+ assert ! vector1.collinear_with?(vector2)
19
+ end
20
+
21
+ def test_vectors_are_oppositely_directed
22
+ vector1 = Vector.new(2, 2)
23
+ vector2 = Vector.new(-2, -2)
24
+
25
+ assert vector1.collinear_with?(vector2)
26
+ end
27
+ end
@@ -0,0 +1,86 @@
1
+ require 'test/unit'
2
+ require 'winding-polygon/vector'
3
+ require 'winding-polygon/point'
4
+
5
+ class CrossProductTest < Test::Unit::TestCase
6
+ include WindingPolygon
7
+
8
+ def test_positive
9
+ assert 1 === Vector.new(1, 0).cross_product(Vector.new(0, 1))
10
+ assert 1 === Vector.new(-1, 0).cross_product(Vector.new(0, -1))
11
+ assert 0 === Vector.new(-1, 0).cross_product(Vector.new(1, 0))
12
+ assert 0 === Vector.new(-1, 0).cross_product(Vector.new(2, 0))
13
+ end
14
+
15
+ def test_positive_for_latlon
16
+ #current vertex
17
+ p0= Point.new(-102.52676351947564,36.81219275411459)
18
+ #previous vertex
19
+ p1=Point.new(-115.86328125000495,34.15328996737699)
20
+ #current edge
21
+ v0 = Vector.new(p1.x-p0.x, p1.y-p0.y)
22
+
23
+ #next vertex
24
+ p2= Point.new(-104.17382812500229,40.17099871188528)
25
+ #next edge
26
+ v1 = Vector.new(p2.x-p0.x, p2.y-p0.y)
27
+
28
+ assert_true v1.cross_product(v0)>0
29
+
30
+ end
31
+
32
+ def test_positive_for_latlon2
33
+ #current vertex
34
+ p0=Point.new(-103.69099150583577,44.1751011402773)
35
+ #previous vertex
36
+ p1= Point.new(-110.50195312500117,46.97322061018078)
37
+ #current edge
38
+ v0 = Vector.new(p1.x-p0.x, p1.y-p0.y)
39
+
40
+ #next vertex
41
+ p2= Point.new(-100.33100481371673,44.925766039368476)
42
+ #next edge
43
+ v1 = Vector.new(p2.x-p0.x, p2.y-p0.y)
44
+
45
+ assert_true v1.cross_product(v0)>0
46
+ end
47
+
48
+ def test_negative
49
+ assert(-1 === Vector.new(0, 1).cross_product(Vector.new(1, 0)))
50
+ end
51
+
52
+ def test_negative_for_latlon
53
+ p0= Point.new(-81.4101562500034,35.021556836103684)
54
+ p1=Point.new(-115.86328125000495,34.15328996737699)
55
+ v0 = Vector.new(p1.x-p0.x, p1.y-p0.y)
56
+
57
+ p2= Point.new(-100.3945312500027,32.46399995947513)
58
+ v1 = Vector.new(p2.x-p0.x, p2.y-p0.y)
59
+
60
+ assert_true v1.cross_product(v0)<0
61
+ end
62
+
63
+ def test_negative_for_latlon_2
64
+ #current vertex
65
+ p0=Point.new(-103.69099150583577,44.1751011402773)
66
+ #previous vertex
67
+ p1= Point.new(-110.50195312500117,46.97322061018078)
68
+ #current edge
69
+ v0 = Vector.new(p1.x-p0.x, p1.y-p0.y)
70
+
71
+ #next vertex
72
+ p2= Point.new(-109.95811865270818,42.77494310443354)
73
+ #next edge
74
+ v1 = Vector.new(p2.x-p0.x, p2.y-p0.y)
75
+
76
+ assert_true v1.cross_product(v0)<0
77
+ end
78
+
79
+ def test_zero
80
+ assert 0 === Vector.new(1, 1).cross_product(Vector.new(-2, -2))
81
+ end
82
+
83
+ def test_unnormalized
84
+ assert 4 === Vector.new(1, 1).cross_product(Vector.new(-2, 2))
85
+ end
86
+ end
@@ -0,0 +1,15 @@
1
+ require 'test/unit'
2
+ require 'winding-polygon/vector'
3
+
4
+ class EqualsTest < Test::Unit::TestCase
5
+ include WindingPolygon
6
+
7
+ def test_equal
8
+ assert_equal Vector.new(1, 3), Vector.new(1, 3)
9
+ end
10
+
11
+ def test_not_equal
12
+ assert_not_equal Vector.new(1, 3), Vector.new(1, 2)
13
+ assert_not_equal Vector.new(1, 2), Vector.new(0, 2)
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'test/unit'
2
+ require 'winding-polygon/vector'
3
+
4
+ class ModulusTest < Test::Unit::TestCase
5
+ include WindingPolygon
6
+
7
+ def test_parallel_to_axis
8
+ assert 1 === Vector.new(1, 0).modulus
9
+ assert 1 === Vector.new(0, 1).modulus
10
+ end
11
+
12
+ def test_inclined
13
+ assert Math.sqrt(2) === Vector.new(1, 1).modulus
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ require 'test/unit'
2
+ require 'winding-polygon/vector'
3
+
4
+ class ScalarProductTest < Test::Unit::TestCase
5
+ include WindingPolygon
6
+
7
+ def test_vectors_are_perpendicular
8
+ assert 0 === Vector.new(1, 1).scalar_product(Vector.new(-1, 1))
9
+ end
10
+
11
+ def test_vectors_are_collinear
12
+ assert(-4 === Vector.new(1, 1).scalar_product(Vector.new(-2, -2)))
13
+ end
14
+
15
+ def test_vectors_are_inclined
16
+ assert 1 === Vector.new(1, 1).scalar_product(Vector.new(0, 1))
17
+ end
18
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: winding-polygon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Xu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-05-16 00:00:00.000000000 Z
11
+ date: 2013-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -28,31 +28,31 @@ dependencies:
28
28
  name: test-unit
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ! '>='
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ! '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ! '>='
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ! '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- description: 'Use Bentley-Ottmann algorithm to solve self-intersecting polygon issue '
55
+ description: ! 'Use Bentley-Ottmann algorithm to solve self-intersecting polygon issue '
56
56
  email:
57
57
  - mxu2008@gmail.com
58
58
  executables: []
@@ -72,6 +72,7 @@ files:
72
72
  - lib/winding-polygon/polygon.rb
73
73
  - lib/winding-polygon/segment.rb
74
74
  - lib/winding-polygon/sweep_line.rb
75
+ - lib/winding-polygon/vector.rb
75
76
  - lib/winding-polygon/version.rb
76
77
  - spec/event_queue_spec.rb
77
78
  - spec/point_spec.rb
@@ -81,6 +82,12 @@ files:
81
82
  - spec/sweep_line_spec.rb
82
83
  - spec/winding-polygon_spec.rb
83
84
  - test/test_avltree.rb
85
+ - test/vector/arithmetics_test.rb
86
+ - test/vector/collinear_with_test.rb
87
+ - test/vector/cross_product_test.rb
88
+ - test/vector/equals_test.rb
89
+ - test/vector/modulus_test.rb
90
+ - test/vector/scalar_product_test.rb
84
91
  - winding-polygon.gemspec
85
92
  homepage: ''
86
93
  licenses:
@@ -92,17 +99,17 @@ require_paths:
92
99
  - lib
93
100
  required_ruby_version: !ruby/object:Gem::Requirement
94
101
  requirements:
95
- - - '>='
102
+ - - ! '>='
96
103
  - !ruby/object:Gem::Version
97
104
  version: '0'
98
105
  required_rubygems_version: !ruby/object:Gem::Requirement
99
106
  requirements:
100
- - - '>='
107
+ - - ! '>='
101
108
  - !ruby/object:Gem::Version
102
109
  version: '0'
103
110
  requirements: []
104
111
  rubyforge_project:
105
- rubygems_version: 2.0.0
112
+ rubygems_version: 2.0.3
106
113
  signing_key:
107
114
  specification_version: 4
108
115
  summary: Detect intersecting points and decompose it into multi-polygons
@@ -115,3 +122,9 @@ test_files:
115
122
  - spec/sweep_line_spec.rb
116
123
  - spec/winding-polygon_spec.rb
117
124
  - test/test_avltree.rb
125
+ - test/vector/arithmetics_test.rb
126
+ - test/vector/collinear_with_test.rb
127
+ - test/vector/cross_product_test.rb
128
+ - test/vector/equals_test.rb
129
+ - test/vector/modulus_test.rb
130
+ - test/vector/scalar_product_test.rb