symmetry_axis 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6c13dde182042eff38651139bade52ae10525937
4
+ data.tar.gz: 7de66b90f6e33fdb5647cfd2f7442c507e7a6f5a
5
+ SHA512:
6
+ metadata.gz: 251442207e32769923a89800e3b2d0a8c71518b53a3750146d771c10a537987d25e34209300ae6a30fc67d2aefe684764d564cad7ffd3136381b7d96d2cc7592
7
+ data.tar.gz: 5897c6c376f89a0c5bb97b66aa52a0285031b4c5c21b0d041b98abcf6b2eb3abb89470603c516a30d5685b3cba8327bc7613ae2ce54205810ca393030c1f6e38
@@ -0,0 +1,101 @@
1
+ class Line
2
+ attr_reader :a, :b, :x
3
+ EPS, TOO_BIG_B = 0.000001, 100_000_000
4
+
5
+ def initialize(x1, y1, x2, y2)
6
+ @vertical = nil
7
+ if x1 < x2
8
+ resolve_coefs(x1, y1, x2, y2)
9
+ else
10
+ resolve_coefs(x2, y2, x1, y1)
11
+ end
12
+ end
13
+
14
+ def self.for_points(p1, p2)
15
+ Line.new(p1[0], p1[1], p2[0], p2[1])
16
+ end
17
+
18
+ def ==(other)
19
+ self.class.aeq(self, other) && self.class.beq(self, other)
20
+ end
21
+
22
+ def to_s
23
+ if !vertical
24
+ "(a=#{@a}, b=#{@b})"
25
+ else
26
+ "(x=#{@x})"
27
+ end
28
+ end
29
+
30
+ def vertical
31
+ @vertical = (nil == @b) || (TOO_BIG_B <= @b.abs) if nil == @vertical
32
+ @vertical
33
+ end
34
+
35
+ def angle_rad
36
+ return (Math::PI / 2) if vertical
37
+ Math.atan(@b)
38
+ end
39
+
40
+ def angle_degrees
41
+ angle_rad / Math::PI * 180
42
+ end
43
+
44
+ def on_me(x, y)
45
+ if vertical
46
+ (@x - x).abs < EPS
47
+ else
48
+ f = @a + @b * x
49
+ (f - y).abs < on_me_eps(x)
50
+ end
51
+ end
52
+
53
+ def on_me_eps(x)
54
+ e = EPS
55
+ e += (EPS / @b).abs if @b != 0
56
+ e += (EPS / x).abs if x != 0
57
+ e
58
+ end
59
+
60
+ def self.symmetryof(x1, y1, x2, y2)
61
+ line = Line.new(x1, y1, x2, y2)
62
+ x, y = (x1 + x2) / 2, (y1 + y2) / 2
63
+ if line.vertical
64
+ Line.new(0, y, 1, y)
65
+ else
66
+ r = line.angle_rad + (Math::PI / 2)
67
+ return Line.new(x, 0, x, 1) if Math::PI / 2 == r.abs
68
+ return Line.new(x, y, x + 1, y + Math.tan(r))
69
+ end
70
+ end
71
+
72
+ def parallel(x, y)
73
+ return Line.new(x, y, x, y + 1) if vertical
74
+ Line.new(x, y, x + 1, y + @b)
75
+ end
76
+
77
+ private
78
+
79
+ def resolve_coefs(xmin, yxmin, xmax, yxmax)
80
+ if 0 == (xmax - xmin).abs
81
+ @b, @a = nil, yxmin
82
+ else
83
+ @b = (yxmax - yxmin) / Float(xmax - xmin)
84
+ @a = yxmin - @b * xmin
85
+ end
86
+ @x, @a = xmin, 0 if vertical
87
+ end
88
+
89
+ def self.aeq(o1, o2)
90
+ (o1.a - o2.a).abs < (2 * EPS)
91
+ end
92
+
93
+ def self.beq(o1, o2)
94
+ if o1.vertical || o2.vertical
95
+ return false unless o1.vertical && o2.vertical
96
+ (o1.x - o2.x).abs < (2 * EPS)
97
+ else
98
+ (o1.b - o2.b).abs < (2 * EPS)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,81 @@
1
+ class SymmetryAxis
2
+ attr_reader :line
3
+
4
+ def initialize(point_array)
5
+ @input = point_array
6
+ @impossible_angles = []
7
+ @two_points_cursor = 1
8
+ @s1counter, @s2counter, @s2loopcounter = 0, 0, 0
9
+ loop do
10
+ take_two_points # step 1
11
+ break if points_are_on_parallel_lines # step 2
12
+ end
13
+ find_axis # step 3
14
+ end
15
+
16
+ private
17
+
18
+ def take_two_points
19
+ @s1counter += 1
20
+ @two_points = [@input.first]
21
+ (@two_points_cursor...(@input.size)).each do |i|
22
+ rad = Line.for_points(@two_points[0], @input[i]).angle_rad
23
+ next if impossible?(rad)
24
+ choose_point i
25
+ return
26
+ end
27
+ fail 'There is no suitable pair of points.'
28
+ end
29
+
30
+ def choose_point(i)
31
+ @two_points_cursor = i + 1
32
+ @two_points[1] = @input[i]
33
+ @two_points_line = Line.for_points(@two_points[0], @input[i])
34
+ end
35
+
36
+ def impossible?(rad)
37
+ return false if @impossible_angles.empty?
38
+ impossible_rad = @impossible_angles.min_by { |e| (e - rad).abs }
39
+ (rad - impossible_rad).abs < (2 * Line.EPS)
40
+ end
41
+
42
+ def points_are_on_parallel_lines
43
+ @s2counter += 1
44
+ points = @input.select { |p| !@two_points.include?(p) }
45
+ @perpendicular_points_pairs_list = [@two_points.dup]
46
+ loop do
47
+ break if points.empty?
48
+ @s2loopcounter += 1
49
+ line = @two_points_line.parallel(*points.first)
50
+ size0 = points.size
51
+ on_line = points.select { |p2| line.on_me(*p2) }
52
+ points -= on_line
53
+ return false if size0 - points.size < 2
54
+ save_perpendicular_points(on_line, line)
55
+ end
56
+ true
57
+ end
58
+
59
+ def save_perpendicular_points(points, line)
60
+ return if line != @two_points_line
61
+ points += @two_points
62
+ @perpendicular_points_pairs_list[0] = pppair(points, line)
63
+ end
64
+
65
+ def pppair(points, line)
66
+ if line.vertical
67
+ [points.min_by { |p| p[1] }, points.max_by { |p| p[1] }]
68
+ else
69
+ [points.min_by { |p| p[0] }, points.max_by { |p| p[0] }]
70
+ end
71
+ end
72
+
73
+ def find_axis
74
+ # puts "\n Iterations:\n Step1 = #{@s1counter}\n Step2 = #{@s2counter}\n Step2Loop = #{@s2loopcounter}"
75
+ points = @perpendicular_points_pairs_list[0]
76
+ p1, p2 = points[0], points[1]
77
+ @line = Line.symmetryof(p1[0], p1[1], p2[0], p2[1])
78
+ end
79
+ end
80
+
81
+ require 'symmetry_axis/line'
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: symmetry_axis
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - "Георгий Устинов"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3'
41
+ description: 'It has the restriction: no points must be on axis. Thus every point
42
+ must have a pair. Also you can separately use the Line class from the gem. It allows
43
+ you to compare for equality the lines, defined by different points on itself.'
44
+ email: httpcatharsis@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - lib/symmetry_axis.rb
50
+ - lib/symmetry_axis/line.rb
51
+ homepage: https://github.com/georgy7/symmetry_axis
52
+ licenses:
53
+ - MIT
54
+ metadata: {}
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 2.4.5
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: Get axis from a point list.
75
+ test_files: []