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 +7 -0
- data/lib/symmetry_axis/line.rb +101 -0
- data/lib/symmetry_axis.rb +81 -0
- metadata +75 -0
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: []
|