geos-extensions 1.0.0 → 2.0.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +17 -0
  3. data/MIT-LICENSE +1 -1
  4. data/Rakefile +2 -0
  5. data/lib/geos-extensions.rb +6 -0
  6. data/lib/geos/coordinate_sequence.rb +290 -12
  7. data/lib/geos/extensions/exceptions.rb +2 -0
  8. data/lib/geos/extensions/version.rb +3 -1
  9. data/lib/geos/geometry.rb +3 -1
  10. data/lib/geos/geometry_collection.rb +52 -0
  11. data/lib/geos/geos_helper.rb +2 -0
  12. data/lib/geos/google_maps.rb +2 -0
  13. data/lib/geos/google_maps/api_2.rb +2 -0
  14. data/lib/geos/google_maps/api_3.rb +2 -0
  15. data/lib/geos/google_maps/api_common.rb +2 -0
  16. data/lib/geos/google_maps/polyline_encoder.rb +2 -0
  17. data/lib/geos/line_string.rb +123 -0
  18. data/lib/geos/multi_line_string.rb +2 -0
  19. data/lib/geos/multi_point.rb +2 -0
  20. data/lib/geos/multi_polygon.rb +2 -0
  21. data/lib/geos/point.rb +59 -0
  22. data/lib/geos/polygon.rb +97 -0
  23. data/lib/geos/yaml.rb +1 -0
  24. data/lib/geos/yaml/psych.rb +1 -0
  25. data/lib/geos/yaml/syck.rb +1 -0
  26. data/lib/geos_extensions.rb +2 -0
  27. data/test/coordinate_sequence_tests.rb +256 -0
  28. data/test/geometry_collection_tests.rb +429 -0
  29. data/test/geometry_tests.rb +57 -0
  30. data/test/google_maps_api_2_tests.rb +2 -0
  31. data/test/google_maps_api_3_tests.rb +2 -0
  32. data/test/google_maps_api_common_tests.rb +2 -0
  33. data/test/google_maps_polyline_encoder_tests.rb +2 -0
  34. data/test/helper_tests.rb +2 -0
  35. data/test/line_string_tests.rb +211 -0
  36. data/test/misc_tests.rb +2 -0
  37. data/test/point_tests.rb +173 -0
  38. data/test/polygon_tests.rb +329 -0
  39. data/test/reader_tests.rb +2 -0
  40. data/test/test_helper.rb +73 -3
  41. data/test/writer_tests.rb +2 -0
  42. data/test/yaml_tests.rb +1 -0
  43. metadata +16 -3
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos::Helper
3
5
  JS_ESCAPE_MAP = {
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  module GoogleMaps
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos::GoogleMaps::Api2
3
5
  module Geometry
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos::GoogleMaps
3
5
  module Api3
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  module GoogleMaps
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  module GoogleMaps
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  class LineString
@@ -10,6 +12,127 @@ module Geos
10
12
  self.coord_seq.to_geojsonable(options)
11
13
  end
12
14
  alias_method :to_geojsonable, :as_geojson
15
+
16
+ # Dumps points similarly to the PostGIS `ST_DumpPoints` function.
17
+ def dump_points(cur_path = [])
18
+ cur_path.concat(self.to_a)
19
+ end
20
+
21
+ %w{ max min }.each do |op|
22
+ %w{ x y }.each do |dimension|
23
+ self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
24
+ def #{dimension}_#{op}
25
+ unless self.empty?
26
+ self.coord_seq.#{dimension}_#{op}
27
+ end
28
+ end
29
+ EOF
30
+ end
31
+
32
+ self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
33
+ def z_#{op}
34
+ unless self.empty?
35
+ if self.has_z?
36
+ self.coord_seq.z_#{op}
37
+ else
38
+ 0
39
+ end
40
+ end
41
+ end
42
+ EOF
43
+ end
44
+
45
+ def snap_to_grid!(*args)
46
+ if !self.empty?
47
+ cs = self.coord_seq.snap_to_grid!(*args)
48
+
49
+ if cs.length == 0
50
+ @ptr = Geos.create_empty_line_string(:srid => self.srid).ptr
51
+ elsif cs.length <= 1
52
+ raise Geos::InvalidGeometryError.new("snap_to_grid! produced an invalid number of points in for a LineString - found #{cs.length} - must be 0 or > 1")
53
+ else
54
+ @ptr = Geos.create_line_string(cs).ptr
55
+ end
56
+ end
57
+
58
+ self
59
+ end
60
+
61
+ def snap_to_grid(*args)
62
+ ret = self.dup.snap_to_grid!(*args)
63
+ ret.srid = pick_srid_according_to_policy(self.srid)
64
+ ret
65
+ end
66
+
67
+ def line_interpolate_point(fraction)
68
+ if !fraction.between?(0, 1)
69
+ raise ArgumentError.new("fraction must be between 0 and 1")
70
+ end
71
+
72
+ case fraction
73
+ when 0
74
+ self.start_point
75
+ when 1
76
+ self.end_point
77
+ else
78
+ length = self.length
79
+ total_length = 0
80
+ segs = self.num_points - 1
81
+
82
+ segs.times do |i|
83
+ p1 = self[i]
84
+ p2 = self[i + 1]
85
+
86
+ seg_length = p1.distance(p2) / length
87
+
88
+ if fraction < total_length + seg_length
89
+ dseg = (fraction - total_length) / seg_length
90
+
91
+ args = []
92
+ args << p1.x + ((p2.x - p1.x) * dseg)
93
+ args << p1.y + ((p2.y - p1.y) * dseg)
94
+ args << p1.z + ((p2.z - p1.z) * dseg) if self.has_z?
95
+
96
+ args << { :srid => pick_srid_according_to_policy(self.srid) } unless self.srid == 0
97
+
98
+ return Geos.create_point(*args)
99
+ end
100
+
101
+ total_length += seg_length
102
+ end
103
+
104
+ # if all else fails...
105
+ self.end_point
106
+ end
107
+ end
108
+ alias_method :interpolate_point, :line_interpolate_point
109
+
110
+ %w{
111
+ affine
112
+ rotate
113
+ rotate_x
114
+ rotate_y
115
+ rotate_z
116
+ scale
117
+ trans_scale
118
+ translate
119
+ }.each do |m|
120
+ self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
121
+ def #{m}!(*args)
122
+ unless self.empty?
123
+ self.coord_seq.#{m}!(*args)
124
+ end
125
+
126
+ self
127
+ end
128
+
129
+ def #{m}(*args)
130
+ ret = self.dup.#{m}!(*args)
131
+ ret.srid = pick_srid_according_to_policy(self.srid)
132
+ ret
133
+ end
134
+ EOF
135
+ end
13
136
  end
14
137
  end
15
138
 
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  class MultiLineString < GeometryCollection
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  class MultiPoint < GeometryCollection
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  class MultiPolygon < GeometryCollection
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  class Point
@@ -115,6 +117,63 @@ module Geos
115
117
  }
116
118
  end
117
119
  alias_method :to_geojsonable, :as_geojson
120
+
121
+ # Dumps points similarly to the PostGIS `ST_DumpPoints` function.
122
+ def dump_points(cur_path = [])
123
+ cur_path.push(self.dup)
124
+ end
125
+
126
+ %w{ max min }.each do |op|
127
+ %w{ x y }.each do |dimension|
128
+ self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
129
+ def #{dimension}_#{op}
130
+ unless self.empty?
131
+ self.#{dimension}
132
+ end
133
+ end
134
+ EOF
135
+ end
136
+
137
+ self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
138
+ def z_#{op}
139
+ unless self.empty?
140
+ if self.has_z?
141
+ self.z
142
+ else
143
+ 0
144
+ end
145
+ end
146
+ end
147
+ EOF
148
+ end
149
+
150
+ %w{
151
+ affine
152
+ rotate
153
+ rotate_x
154
+ rotate_y
155
+ rotate_z
156
+ scale
157
+ snap_to_grid
158
+ trans_scale
159
+ translate
160
+ }.each do |m|
161
+ self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
162
+ def #{m}!(*args)
163
+ unless self.empty?
164
+ self.coord_seq.#{m}!(*args)
165
+ end
166
+
167
+ self
168
+ end
169
+
170
+ def #{m}(*args)
171
+ ret = self.dup.#{m}!(*args)
172
+ ret.srid = pick_srid_according_to_policy(self.srid)
173
+ ret
174
+ end
175
+ EOF
176
+ end
118
177
  end
119
178
  end
120
179
 
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  class Polygon
@@ -153,6 +155,101 @@ module Geos
153
155
  ret
154
156
  end
155
157
  alias_method :to_geojsonable, :as_geojson
158
+
159
+ # Dumps points similarly to the PostGIS `ST_DumpPoints` function.
160
+ def dump_points(cur_path = [])
161
+ points = [ self.exterior_ring.dump_points ]
162
+
163
+ self.interior_rings.each do |ring|
164
+ points.push(ring.dump_points)
165
+ end
166
+
167
+ cur_path.concat(points)
168
+ end
169
+
170
+ %w{ max min }.each do |op|
171
+ %w{ x y }.each do |dimension|
172
+ self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
173
+ def #{dimension}_#{op}
174
+ unless self.empty?
175
+ self.envelope.exterior_ring.#{dimension}_#{op}
176
+ end
177
+ end
178
+ EOF
179
+ end
180
+
181
+ self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
182
+ def z_#{op}
183
+ unless self.empty?
184
+ if self.has_z?
185
+ self.exterior_ring.z_#{op}
186
+ else
187
+ 0
188
+ end
189
+ end
190
+ end
191
+ EOF
192
+ end
193
+
194
+ def snap_to_grid!(*args)
195
+ if !self.empty?
196
+ exterior_ring = self.exterior_ring.coord_seq.snap_to_grid!(*args)
197
+
198
+ if exterior_ring.length == 0
199
+ @ptr = Geos.create_empty_polygon(:srid => self.srid).ptr
200
+ elsif exterior_ring.length < 4
201
+ raise Geos::InvalidGeometryError.new("snap_to_grid! produced an invalid number of points in exterior ring - found #{exterior_ring.length} - must be 0 or >= 4")
202
+ else
203
+ interior_rings = []
204
+
205
+ self.num_interior_rings.times { |i|
206
+ interior_ring = self.interior_ring_n(i).coord_seq.snap_to_grid!(*args)
207
+
208
+ interior_rings << interior_ring unless interior_ring.length < 4
209
+ }
210
+
211
+ interior_rings.compact!
212
+
213
+ polygon = Geos.create_polygon(exterior_ring, interior_rings, :srid => self.srid)
214
+ @ptr = polygon.ptr
215
+ end
216
+ end
217
+
218
+ self
219
+ end
220
+
221
+ def snap_to_grid(*args)
222
+ ret = self.dup.snap_to_grid!(*args)
223
+ ret.srid = pick_srid_according_to_policy(self.srid)
224
+ ret
225
+ end
226
+
227
+ %w{
228
+ affine
229
+ rotate
230
+ rotate_x
231
+ rotate_y
232
+ rotate_z
233
+ scale
234
+ trans_scale
235
+ translate
236
+ }.each do |m|
237
+ self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
238
+ def #{m}!(*args)
239
+ self.exterior_ring.coord_seq.#{m}!(*args)
240
+ self.interior_rings.each do |ring|
241
+ ring.coord_seq.#{m}!(*args)
242
+ end
243
+ self
244
+ end
245
+
246
+ def #{m}(*args)
247
+ ret = self.dup.#{m}!(*args)
248
+ ret.srid = pick_srid_according_to_policy(self.srid)
249
+ ret
250
+ end
251
+ EOF
252
+ end
156
253
  end
157
254
  end
158
255
 
@@ -1,4 +1,5 @@
1
1
  # encoding: UTF-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  # This file adds yaml serialization support to geometries. The generated yaml
4
5
  # has this format:
@@ -1,4 +1,5 @@
1
1
  # encoding: UTF-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Geos
4
5
  class Geometry
@@ -1,4 +1,5 @@
1
1
  # encoding: UTF-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Geos
4
5
  class Geometry
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  begin
3
5
  if !ENV['USE_BINARY_GEOS']
@@ -0,0 +1,256 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ $: << File.dirname(__FILE__)
5
+ require 'test_helper'
6
+
7
+ class CoordinateSequenceTests < Minitest::Test
8
+ include TestHelper
9
+
10
+ def test_x_max
11
+ cs = Geos::CoordinateSequence.new([ -10, -15 ], [ 0, 5 ], [ 10, 20 ])
12
+ assert_equal(10, cs.x_max)
13
+ end
14
+
15
+ def test_x_min
16
+ cs = Geos::CoordinateSequence.new([ -10, -15 ], [ 0, 5 ], [ 10, 20 ])
17
+ assert_equal(-10, cs.x_min)
18
+ end
19
+
20
+ def test_y_max
21
+ cs = Geos::CoordinateSequence.new([ -10, -15 ], [ 0, 5 ], [ 10, 20 ])
22
+ assert_equal(20, cs.y_max)
23
+ end
24
+
25
+ def test_y_min
26
+ cs = Geos::CoordinateSequence.new([ -10, -15 ], [ 0, 5 ], [ 10, 20 ])
27
+ assert_equal(-15, cs.y_min)
28
+ end
29
+
30
+ def test_z_max
31
+ cs = Geos::CoordinateSequence.new([ -10, -15 ], [ 0, 5 ], [ 10, 20 ])
32
+ assert(cs.z_max.nan?, " Expected NaN")
33
+
34
+ cs = Geos::CoordinateSequence.new([ -10, -15, -20 ], [ 0, 5, 10 ], [ 10, 20, 30 ])
35
+ assert_equal(30, cs.z_max)
36
+ end
37
+
38
+ def test_z_min
39
+ cs = Geos::CoordinateSequence.new([ -10, -15 ], [ 0, 5 ], [ 10, 20 ])
40
+ assert(cs.z_min.nan?, " Expected NaN")
41
+
42
+ cs = Geos::CoordinateSequence.new([ -10, -15, -20 ], [ 0, 5, 10 ], [ 10, 20, 30 ])
43
+ assert_equal(-20, cs.z_min)
44
+ end
45
+
46
+ def test_snap_to_grid_with_size
47
+ expected = [
48
+ [[-10.0, -15.0], [0.0, 5.0], [10.0, 20.0]],
49
+ [[-10.1, -15.1], [0.1, 5.1], [10.1, 20.1]],
50
+ [[-10.12, -15.12], [0.12, 5.12], [10.12, 20.12]],
51
+ [[-10.123, -15.123], [0.123, 5.123], [10.123, 20.123]],
52
+ [[-10.1235, -15.1235], [0.1235, 5.1235], [10.1235, 20.1235]],
53
+ [[-10.12346, -15.12346], [0.12346, 5.12346], [10.12346, 20.12346]],
54
+ [[-10.123457, -15.123457], [0.123457, 5.123457], [10.123457, 20.123457]],
55
+ [[-10.1234568, -15.1234568], [0.1234568, 5.1234568], [10.1234568, 20.1234568]],
56
+ [[-10.12345679, -15.12345679], [0.12345679, 5.12345679], [10.12345679, 20.12345679]]
57
+ ]
58
+
59
+ coordinates = [
60
+ [ -10.123456789, -15.123456789 ],
61
+ [ 0.123456789, 5.123456789 ],
62
+ [ 10.123456789, 20.123456789 ]
63
+ ]
64
+
65
+ 9.times do |i|
66
+ cs = Geos::CoordinateSequence.new(*coordinates)
67
+ cs.snap_to_grid!(10 ** -i)
68
+
69
+ # XXX - Ruby 1.8.7 sometimes sees the the float values as differing
70
+ # slightly, but not enough that it would matter for these tests.
71
+ # Test equality on the inspect Strings instead of the float values.
72
+ assert_equal(expected[i].inspect, cs.to_a.inspect)
73
+
74
+ cs = Geos::CoordinateSequence.new(*coordinates)
75
+ snapped = cs.snap_to_grid(10 ** -i)
76
+ assert_equal(coordinates, cs.to_a)
77
+ assert_equal(expected[i].inspect, snapped.to_a.inspect)
78
+ end
79
+ end
80
+
81
+ def test_snap_to_grid_with_hash
82
+ cs = Geos::CoordinateSequence.new(
83
+ [ 10, 10 ],
84
+ [ 20, 20 ],
85
+ [ 30, 30 ]
86
+ )
87
+ cs.snap_to_grid!(:size_x => 1, :size_y => 1, :offset_x => 12.5, :offset_y => 12.5)
88
+
89
+ assert_equal([
90
+ [ 9.5, 9.5 ],
91
+ [ 20.5, 20.5 ],
92
+ [ 30.5, 30.5 ]
93
+ ], cs.to_a)
94
+ end
95
+
96
+ def test_snap_to_grid_with_geometry_origin
97
+ cs = Geos::CoordinateSequence.new(
98
+ [ 10, 10 ],
99
+ [ 20, 20 ],
100
+ [ 30, 30 ]
101
+ )
102
+ cs.snap_to_grid!(:size => 1, :offset => read('LINESTRING (0 0, 25 25)'))
103
+
104
+ assert_equal([
105
+ [ 9.5, 9.5 ],
106
+ [ 20.5, 20.5 ],
107
+ [ 30.5, 30.5 ]
108
+ ], cs.to_a)
109
+ end
110
+
111
+ def test_snap_to_grid_with_z
112
+ cs = Geos::CoordinateSequence.new(
113
+ [ 10, 10, 10 ],
114
+ [ 20, 20, 20 ],
115
+ [ 30, 30, 30 ]
116
+ )
117
+ cs.snap_to_grid!(
118
+ :size_x => 1,
119
+ :size_y => 1,
120
+ :size_z => 1,
121
+
122
+ :offset_x => 12.5,
123
+ :offset_y => 12.5,
124
+ :offset_z => 12.5
125
+ )
126
+
127
+ assert_equal([
128
+ [ 9.5, 9.5, 9.5 ],
129
+ [ 20.5, 20.5, 20.5 ],
130
+ [ 30.5, 30.5, 30.5 ]
131
+ ], cs.to_a)
132
+ end
133
+
134
+ def test_snap_to_grid_remove_duplicate_points
135
+ coords = [
136
+ [-10.0, 0.0],
137
+ [-10.0, 5.0], [-10.0, 5.0],
138
+ [-10.0, 6.0], [-10.0, 6.0], [-10.0, 6.0],
139
+ [-10.0, 7.0], [-10.0, 7.0], [-10.0, 7.0],
140
+ [-10.0, 8.0], [-10.0, 8.0],
141
+ [-9.0, 8.0], [-9.0, 9.0],
142
+ [-10.0, 0.0]
143
+ ]
144
+
145
+ expected = [
146
+ [-10.0, 0.0],
147
+ [-10.0, 5.0],
148
+ [-10.0, 6.0],
149
+ [-10.0, 7.0],
150
+ [-10.0, 8.0],
151
+ [-9.0, 8.0],
152
+ [-9.0, 9.0],
153
+ [-10.0, 0.0]
154
+ ]
155
+
156
+ cs = Geos::CoordinateSequence.new(coords)
157
+ cs.snap_to_grid!
158
+
159
+ assert_equal(expected, cs.to_a)
160
+
161
+ cs = Geos::CoordinateSequence.new(coords)
162
+ cs2 = cs.snap_to_grid
163
+
164
+ assert_equal(coords, cs.to_a)
165
+ assert_equal(expected, cs2.to_a)
166
+ end
167
+
168
+ def cs_affine_tester(method, expected, coords, *args)
169
+ cs = Geos::CoordinateSequence.new(coords)
170
+ cs.send("#{method}!", *args)
171
+
172
+ expected.length.times do |i|
173
+ assert_in_delta(expected[i], cs.get_ordinate(0, i), DELTA_TOLERANCE)
174
+ end
175
+
176
+ cs = Geos::CoordinateSequence.new(coords)
177
+ cs2 = cs.send(method, *args)
178
+
179
+ expected.length.times do |i|
180
+ assert_in_delta(coords[i], cs.get_ordinate(0, i), DELTA_TOLERANCE)
181
+ assert_in_delta(expected[i], cs2.get_ordinate(0, i), DELTA_TOLERANCE)
182
+ end
183
+ end
184
+
185
+ def test_rotate
186
+ cs_affine_tester(:rotate, [ 29.0, 11.0 ], [ 1, 1 ], Math::PI / 2, [ 10.0, 20.0 ])
187
+ cs_affine_tester(:rotate, [ -2.0, 0.0 ], [ 1, 1 ], -Math::PI / 2, [ -1.0, 2.0 ])
188
+ cs_affine_tester(:rotate, [ 19.0, 1.0 ], [ 1, 1 ], Math::PI / 2, read('POINT(10 10)'))
189
+ cs_affine_tester(:rotate, [ -0.5, 0.5 ], [ 1, 1 ], Math::PI / 2, read('LINESTRING(0 0, 1 0)'))
190
+ end
191
+
192
+ def test_rotate_x
193
+ cs_affine_tester(:rotate_x, [ 1, -1, -1 ], [ 1, 1, 1 ], Math::PI)
194
+ cs_affine_tester(:rotate_x, [ 1, -1, 1 ], [ 1, 1, 1 ], Math::PI / 2)
195
+ cs_affine_tester(:rotate_x, [ 1, 1, -1 ], [ 1, 1, 1 ], Math::PI + Math::PI / 2)
196
+ cs_affine_tester(:rotate_x, [ 1, 1, 1 ], [ 1, 1, 1 ], Math::PI * 2)
197
+ end
198
+
199
+ def test_rotate_y
200
+ cs_affine_tester(:rotate_y, [ -1, 1, -1 ], [ 1, 1, 1 ], Math::PI)
201
+ cs_affine_tester(:rotate_y, [ 1, 1, -1 ], [ 1, 1, 1 ], Math::PI / 2)
202
+ cs_affine_tester(:rotate_y, [ -1, 1, 1 ], [ 1, 1, 1 ], Math::PI + Math::PI / 2)
203
+ cs_affine_tester(:rotate_y, [ 1, 1, 1 ], [ 1, 1, 1 ], Math::PI * 2)
204
+ end
205
+
206
+ def test_rotate_z
207
+ cs_affine_tester(:rotate_z, [ -1, -1 ], [ 1, 1 ], Math::PI)
208
+ cs_affine_tester(:rotate_z, [ -1, 1 ], [ 1, 1 ], Math::PI / 2)
209
+ cs_affine_tester(:rotate_z, [ 1, -1 ], [ 1, 1 ], Math::PI + Math::PI / 2)
210
+ cs_affine_tester(:rotate_z, [ 1, 1 ], [ 1, 1 ], Math::PI * 2)
211
+ end
212
+
213
+ def test_scale
214
+ cs_affine_tester(:scale, [ 5, 5 ], [ 1, 1 ], 5, 5)
215
+ cs_affine_tester(:scale, [ 3, 2 ], [ 1, 1 ], 3, 2)
216
+ cs_affine_tester(:scale, [ 40, 40, 40 ], [ 10, 20, -5 ], 4, 2, -8)
217
+ end
218
+
219
+ def test_scale_hash
220
+ cs_affine_tester(:scale, [ 5, 5 ], [ 1, 1 ], :x => 5, :y => 5)
221
+ cs_affine_tester(:scale, [ 3, 2 ], [ 1, 1 ], :x => 3, :y => 2)
222
+ cs_affine_tester(:scale, [ 40, 40, 40 ], [ 10, 20, -5 ], :x => 4, :y => 2, :z => -8)
223
+ end
224
+
225
+ def test_trans_scale
226
+ cs_affine_tester(:trans_scale, [ 2, 2 ], [ 1, 1 ], 1, 1, 1, 1)
227
+ cs_affine_tester(:trans_scale, [ 3, 3 ], [ 2, 2 ], 1, 1, 1, 1)
228
+ cs_affine_tester(:trans_scale, [ 0, 0 ], [ 1, 1 ], -1, -1, -1, -1)
229
+ cs_affine_tester(:trans_scale, [ 1, 2 ], [ 1, 1 ], 0, 1, 1, 1)
230
+ cs_affine_tester(:trans_scale, [ 2, 1 ], [ 1, 1 ], 1, 0, 1, 1)
231
+ cs_affine_tester(:trans_scale, [ 0, 2 ], [ 1, 1 ], 1, 1, 0, 1)
232
+ cs_affine_tester(:trans_scale, [ 2, 0 ], [ 1, 1 ], 1, 1, 1, 0)
233
+ cs_affine_tester(:trans_scale, [ 3, 2 ], [ 1, 1 ], 2, 1, 1, 1)
234
+ cs_affine_tester(:trans_scale, [ 2, 3 ], [ 1, 1 ], 1, 2, 1, 1)
235
+ cs_affine_tester(:trans_scale, [ 4, 2 ], [ 1, 1 ], 1, 1, 2, 1)
236
+ cs_affine_tester(:trans_scale, [ 2, 4 ], [ 1, 1 ], 1, 1, 1, 2)
237
+ cs_affine_tester(:trans_scale, [ 15, 28 ], [ 1, 1 ], 2, 3, 5, 7)
238
+ cs_affine_tester(:trans_scale, [ 15, 28, 1 ], [ 1, 1, 1 ], 2, 3, 5, 7)
239
+ end
240
+
241
+ def test_trans_scale_hash
242
+ cs_affine_tester(:trans_scale, [ 2, 2 ], [ 1, 1 ], :delta_x => 1, :delta_y => 1, :x_factor => 1, :y_factor => 1)
243
+ cs_affine_tester(:trans_scale, [ 15, 28, 1 ], [ 1, 1, 1 ], :delta_x => 2, :delta_y => 3, :x_factor => 5, :y_factor => 7)
244
+ cs_affine_tester(:trans_scale, [ 3, 1, 1 ], [ 1, 1, 1 ], :delta_x => 2, :z_factor => 2)
245
+ end
246
+
247
+ def test_translate
248
+ cs_affine_tester(:translate, [ 5, 12 ], [ 0, 0 ], 5, 12)
249
+ cs_affine_tester(:translate, [ -3, -7, 3 ], [ 0, 0, 0 ], -3, -7, 3)
250
+ end
251
+
252
+ def test_translate_hash
253
+ cs_affine_tester(:translate, [ 5, 12 ], [ 0, 0 ], :x => 5, :y => 12)
254
+ cs_affine_tester(:translate, [ -3, -7, 3 ], [ 0, 0, 0 ], :x => -3, :y => -7, :z => 3)
255
+ end
256
+ end