cartesius 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/cartesius/circumference.rb +95 -0
- data/lib/cartesius/ellipse.rb +173 -0
- data/lib/cartesius/hyperbola.rb +168 -0
- data/lib/cartesius/line.rb +159 -0
- data/lib/cartesius/parabola.rb +118 -0
- data/lib/cartesius/point.rb +62 -0
- data/lib/cartesius/segment.rb +60 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 95fd724a37b3fd9e2e072f649cd34ffcd6ade1bd
|
4
|
+
data.tar.gz: 304ab894b81c52b1d02c63076be66879a20319f7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5f3b5b319332a590bcd796952e58328cbafa0b6857fea724bc796cbdc72520c715a167d063bf3510a5db5617cb52c49e55b75938c42c69c484a47a82a817471e
|
7
|
+
data.tar.gz: 3abcc3dc0cdc72e7878dbb1c55cf7c4d887f1fe464e228083e993e0d11f4952ee9769c9afaf34cfc69212e51f848f56c32bc08b808cf1f2c259b941006bdc50d
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require('cartesius/line')
|
2
|
+
require('cartesius/determinator')
|
3
|
+
require('cartesius/numerificator')
|
4
|
+
require('cartesius/cramer')
|
5
|
+
|
6
|
+
module Cartesius
|
7
|
+
|
8
|
+
class Circumference
|
9
|
+
include Determinator, Numerificator
|
10
|
+
|
11
|
+
# Conic
|
12
|
+
# Conic equation type: x^2 + y^2 + dx + ey + f = 0
|
13
|
+
def initialize(x:, y:, k:)
|
14
|
+
@x2_coeff, @y2_coeff, @x_coeff, @y_coeff, @k_coeff = 1, 1, x.to_r, y.to_r, k.to_r
|
15
|
+
validation
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.by_definition(focus:, radius:)
|
19
|
+
alfa = -2 * focus.x
|
20
|
+
beta = -2 * focus.y
|
21
|
+
gamma = focus.x ** 2 + focus.y ** 2 - radius.to_r ** 2
|
22
|
+
|
23
|
+
self.new(x: alfa, y: beta, k: gamma)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.by_canonical(focus:, radius:)
|
27
|
+
by_definition(focus: focus, radius: radius)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.by_points(point1:, point2:, point3:)
|
31
|
+
if point1 == point2 or point1 == point3 or point2 == point3
|
32
|
+
raise ArgumentError.new('Points must be distinct!')
|
33
|
+
end
|
34
|
+
|
35
|
+
line = Line.by_points(point1: point1, point2: point2)
|
36
|
+
if line.include?(point3)
|
37
|
+
raise ArgumentError.new('Points must not be aligned!')
|
38
|
+
end
|
39
|
+
|
40
|
+
alfa, beta, gamma = Cramer.solution3(
|
41
|
+
[point1.x, point1.y, 1],
|
42
|
+
[point2.x, point2.y, 1],
|
43
|
+
[point3.x, point3.y, 1],
|
44
|
+
[-(point1.x ** 2 + point1.y ** 2), -(point2.x ** 2 + point2.y ** 2), -(point3.x ** 2 + point3.y ** 2)]
|
45
|
+
)
|
46
|
+
|
47
|
+
self.new(x: alfa, y: beta, k: gamma)
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def self.unitary
|
52
|
+
new(x: 0, y: 0, k: -1)
|
53
|
+
end
|
54
|
+
|
55
|
+
def center
|
56
|
+
Point.new(x: centrum[:xc], y: centrum[:yc])
|
57
|
+
end
|
58
|
+
|
59
|
+
def radius
|
60
|
+
Math.sqrt(a2)
|
61
|
+
end
|
62
|
+
|
63
|
+
def unitary?
|
64
|
+
self == Circumference.unitary
|
65
|
+
end
|
66
|
+
|
67
|
+
def == (circumference)
|
68
|
+
circumference.instance_of?(Circumference) and
|
69
|
+
circumference.center == self.center and circumference.radius == self.radius
|
70
|
+
end
|
71
|
+
|
72
|
+
def congruent?(circumference)
|
73
|
+
unless circumference.instance_of?(self.class)
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
|
77
|
+
circumference.radius == self.radius
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_equation
|
81
|
+
equationfy(
|
82
|
+
'x^2' => @x2_coeff, 'y^2' => @y2_coeff, 'x' => @x_coeff, 'y' => @y_coeff, '1' => @k_coeff
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def validation
|
89
|
+
if determinator <= @k_coeff
|
90
|
+
raise ArgumentError.new('Invalid coefficients!')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require('cartesius/segment')
|
2
|
+
require('cartesius/determinator')
|
3
|
+
require('cartesius/numerificator')
|
4
|
+
require('cartesius/cramer')
|
5
|
+
|
6
|
+
module Cartesius
|
7
|
+
|
8
|
+
class Ellipse
|
9
|
+
include Determinator, Numerificator
|
10
|
+
|
11
|
+
# Conic
|
12
|
+
# Conic equation type: ax^2 + by^2 + dx + ey + f = 0
|
13
|
+
def initialize(x2:, y2:, x:, y:, k:)
|
14
|
+
x2, y2, x, y, k = normalize(x2, y2, x, y, k)
|
15
|
+
@x2_coeff, @y2_coeff, @x_coeff, @y_coeff, @k_coeff = x2.to_r, y2.to_r, x.to_r, y.to_r, k.to_r
|
16
|
+
validation
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.by_definition(focus1:, focus2:, distance:)
|
20
|
+
if focus1 == focus2
|
21
|
+
raise ArgumentError.new('Focus points must be different!')
|
22
|
+
end
|
23
|
+
|
24
|
+
unless focus1.aligned_horizontally_with?(focus2) or focus1.aligned_vertically_with?(focus2)
|
25
|
+
raise ArgumentError.new('Focus must be aligned to axis!')
|
26
|
+
end
|
27
|
+
|
28
|
+
focal_distance = Point.distance(focus1, focus2)
|
29
|
+
if distance <= focal_distance
|
30
|
+
raise ArgumentError.new('Sum of distances must be greater than focal distance!')
|
31
|
+
end
|
32
|
+
|
33
|
+
center = Segment.new(extreme1: focus1, extreme2: focus2).mid
|
34
|
+
c2 = Rational(focal_distance, 2) ** 2
|
35
|
+
if focus1.aligned_horizontally_with?(focus2)
|
36
|
+
a2 = Rational(distance, 2) ** 2
|
37
|
+
b2 = a2 - c2
|
38
|
+
end
|
39
|
+
if focus1.aligned_vertically_with?(focus2)
|
40
|
+
b2 = Rational(distance, 2) ** 2
|
41
|
+
a2 = b2 - c2
|
42
|
+
end
|
43
|
+
|
44
|
+
self.new(x2: b2, y2: a2, x: -2 * b2 * center.x, y: -2 * a2 * center.y, k: b2 * center.x ** 2 + a2 * center.y ** 2 - a2 * b2)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.by_canonical(center:, x_semi_axis:, y_semi_axis:)
|
48
|
+
if x_semi_axis <= 0 or y_semi_axis <= 0
|
49
|
+
raise ArgumentError.new('Semi axis length must be positive!')
|
50
|
+
end
|
51
|
+
|
52
|
+
if x_semi_axis == y_semi_axis
|
53
|
+
raise ArgumentError.new('Semi axis length must be different!')
|
54
|
+
end
|
55
|
+
|
56
|
+
b2 = y_semi_axis ** 2
|
57
|
+
a2 = x_semi_axis ** 2
|
58
|
+
|
59
|
+
self.new(x2: b2, y2: a2, x: -2 * b2 * center.x, y: -2 * a2 * center.y, k: b2 * center.x ** 2 + a2 * center.y ** 2 - a2 * b2)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.by_points(center:, point1:, point2:)
|
63
|
+
if center == point1 or center == point2 or point1 == point2
|
64
|
+
raise ArgumentError.new('Points must be different!')
|
65
|
+
end
|
66
|
+
|
67
|
+
shifted1 = Point.new(x: point1.x - center.x, y: point1.y - center.y)
|
68
|
+
shifted2 = Point.new(x: point2.x - center.x, y: point2.y - center.y)
|
69
|
+
|
70
|
+
begin
|
71
|
+
alfa, beta = Cramer.solution2(
|
72
|
+
[shifted1.x ** 2, shifted1.y ** 2],
|
73
|
+
[shifted2.x ** 2, shifted2.y ** 2],
|
74
|
+
[1, 1]
|
75
|
+
)
|
76
|
+
rescue
|
77
|
+
raise ArgumentError.new('No Ellipse for these points!')
|
78
|
+
end
|
79
|
+
|
80
|
+
a2 = Rational(1, alfa)
|
81
|
+
b2 = Rational(1, beta)
|
82
|
+
|
83
|
+
self.new(x2: b2, y2: a2, x: -2 * b2 * center.x, y: -2 * a2 * center.y, k: b2 * center.x ** 2 + a2 * center.y ** 2 - a2 * b2)
|
84
|
+
end
|
85
|
+
|
86
|
+
def focus1
|
87
|
+
if a2 > b2
|
88
|
+
Point.new(x: center.x + Math.sqrt(a2 - b2), y: center.y)
|
89
|
+
else
|
90
|
+
Point.new(x: center.x, y: center.y + Math.sqrt(b2 - a2))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def focus2
|
95
|
+
if a2 > b2
|
96
|
+
Point.new(x: center.x - Math.sqrt(a2 - b2), y: center.y)
|
97
|
+
else
|
98
|
+
Point.new(x: center.x, y: center.y - Math.sqrt(b2 - a2))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def focal_distance
|
103
|
+
Point.distance(focus1, focus2)
|
104
|
+
end
|
105
|
+
|
106
|
+
def center
|
107
|
+
Point.new(x: centrum[:xc], y: centrum[:yc])
|
108
|
+
end
|
109
|
+
|
110
|
+
def sum_of_distances
|
111
|
+
if a2 > b2
|
112
|
+
2 * Math.sqrt(a2)
|
113
|
+
else
|
114
|
+
2 * Math.sqrt(b2)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def x_semi_axis_length
|
119
|
+
Math.sqrt(a2)
|
120
|
+
end
|
121
|
+
|
122
|
+
def y_semi_axis_length
|
123
|
+
Math.sqrt(b2)
|
124
|
+
end
|
125
|
+
|
126
|
+
def major_semi_axis
|
127
|
+
[x_semi_axis_length, y_semi_axis_length].max
|
128
|
+
end
|
129
|
+
|
130
|
+
def minor_semi_axis
|
131
|
+
[x_semi_axis_length, y_semi_axis_length].min
|
132
|
+
end
|
133
|
+
|
134
|
+
def vertices
|
135
|
+
[
|
136
|
+
Point.new(x: center.x + x_semi_axis_length, y: center.y),
|
137
|
+
Point.new(x: center.x, y: center.y - y_semi_axis_length),
|
138
|
+
Point.new(x: center.x - x_semi_axis_length, y: center.y),
|
139
|
+
Point.new(x: center.x, y: center.y + y_semi_axis_length)
|
140
|
+
]
|
141
|
+
end
|
142
|
+
|
143
|
+
def eccentricity
|
144
|
+
Rational(focal_distance, 2 * major_semi_axis)
|
145
|
+
end
|
146
|
+
|
147
|
+
def congruent?(ellipse)
|
148
|
+
ellipse.instance_of?(Ellipse) and
|
149
|
+
ellipse.eccentricity == self.eccentricity
|
150
|
+
end
|
151
|
+
|
152
|
+
def == (ellipse)
|
153
|
+
ellipse.instance_of?(Ellipse) and
|
154
|
+
ellipse.focus1 == self.focus1 and ellipse.focus2 == self.focus2 and ellipse.sum_of_distances == self.sum_of_distances
|
155
|
+
end
|
156
|
+
|
157
|
+
def to_equation
|
158
|
+
equationfy(
|
159
|
+
'x^2' => @x2_coeff, 'y^2' => @y2_coeff, 'x' => @x_coeff, 'y' => @y_coeff, '1' => @k_coeff
|
160
|
+
)
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
def validation
|
166
|
+
if @x2_coeff <= 0 or @y2_coeff <= 0 or @x2_coeff == @y2_coeff or determinator <= @k_coeff
|
167
|
+
raise ArgumentError.new('Invalid coefficients!')
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require('cartesius/segment')
|
2
|
+
require('cartesius/determinator')
|
3
|
+
require('cartesius/numerificator')
|
4
|
+
require('cartesius/cramer')
|
5
|
+
|
6
|
+
module Cartesius
|
7
|
+
|
8
|
+
class Hyperbola
|
9
|
+
include Determinator, Numerificator
|
10
|
+
|
11
|
+
# Conic
|
12
|
+
# Conic equation type: ax^2 + by^2 + dx + ey + f = 0
|
13
|
+
def initialize(x2:, y2:, x:, y:, k:)
|
14
|
+
x2, y2, x, y, k = normalize(x2, y2, x, y, k)
|
15
|
+
@x2_coeff, @y2_coeff, @x_coeff, @y_coeff, @k_coeff = x2.to_r, y2.to_r, x.to_r, y.to_r, k.to_r
|
16
|
+
validation
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.by_definition(focus1:, focus2:, distance:)
|
20
|
+
if focus1 == focus2
|
21
|
+
raise ArgumentError.new('Focus points must be different!')
|
22
|
+
end
|
23
|
+
|
24
|
+
unless focus1.aligned_horizontally_with?(focus2) or focus1.aligned_vertically_with?(focus2)
|
25
|
+
raise ArgumentError.new('Focus must be aligned to axis!')
|
26
|
+
end
|
27
|
+
|
28
|
+
focal_distance = Point.distance(focus1, focus2)
|
29
|
+
if distance >= focal_distance
|
30
|
+
raise ArgumentError.new('Difference of distances must be less than focal distance!')
|
31
|
+
end
|
32
|
+
|
33
|
+
center = Segment.new(extreme1: focus1, extreme2: focus2).mid
|
34
|
+
c2 = Rational(focal_distance, 2) ** 2
|
35
|
+
if focus1.aligned_horizontally_with?(focus2)
|
36
|
+
a2 = Rational(distance, 2) ** 2
|
37
|
+
b2 = c2 - a2
|
38
|
+
position = 1
|
39
|
+
end
|
40
|
+
if focus1.aligned_vertically_with?(focus2)
|
41
|
+
b2 = Rational(distance, 2) ** 2
|
42
|
+
a2 = c2 - b2
|
43
|
+
position = -1
|
44
|
+
end
|
45
|
+
|
46
|
+
self.new(x2: b2, y2: -a2, x: -2 * b2 * center.x, y: 2 * a2 * center.y, k: b2 * center.x ** 2 - a2 * center.y ** 2 + -position * a2 * b2)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.by_canonical(center:, transverse_axis:, not_transverse_axis:, position:)
|
50
|
+
if transverse_axis <= 0 or not_transverse_axis <= 0
|
51
|
+
raise ArgumentError.new('Axis length must be positive!')
|
52
|
+
end
|
53
|
+
|
54
|
+
unless [-1, 1].include?(position)
|
55
|
+
raise ArgumentError.new('Position must be up or right!')
|
56
|
+
end
|
57
|
+
|
58
|
+
if position == -1
|
59
|
+
a2 = not_transverse_axis ** 2
|
60
|
+
b2 = transverse_axis ** 2
|
61
|
+
end
|
62
|
+
|
63
|
+
if position == 1
|
64
|
+
a2 = transverse_axis ** 2
|
65
|
+
b2 = not_transverse_axis ** 2
|
66
|
+
end
|
67
|
+
|
68
|
+
self.new(x2: b2, y2: -a2, x: -2 * b2 * center.x, y: 2 * a2 * center.y, k: b2 * center.x ** 2 - a2 * center.y ** 2 + -position * a2 * b2)
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.by_points(center:, vertex:, point:)
|
72
|
+
if center == vertex or center == point or vertex == point
|
73
|
+
raise ArgumentError.new('Points must be different!')
|
74
|
+
end
|
75
|
+
|
76
|
+
unless vertex.aligned_horizontally_with?(center) or vertex.aligned_vertically_with?(center)
|
77
|
+
raise ArgumentError.new('Vertex must be aligned with center!')
|
78
|
+
end
|
79
|
+
|
80
|
+
if vertex.aligned_horizontally_with?(center)
|
81
|
+
position = 1
|
82
|
+
end
|
83
|
+
|
84
|
+
if vertex.aligned_vertically_with?(center)
|
85
|
+
position = -1
|
86
|
+
end
|
87
|
+
|
88
|
+
shifted1 = Point.new(x: vertex.x - center.x, y: vertex.y - center.y)
|
89
|
+
shifted2 = Point.new(x: point.x - center.x, y: point.y - center.y)
|
90
|
+
|
91
|
+
begin
|
92
|
+
alfa, beta = Cramer.solution2(
|
93
|
+
[shifted1.x ** 2, shifted1.y ** 2],
|
94
|
+
[shifted2.x ** 2, shifted2.y ** 2],
|
95
|
+
[1, 1]
|
96
|
+
)
|
97
|
+
rescue
|
98
|
+
raise ArgumentError.new('No Hyperbola for these points!')
|
99
|
+
end
|
100
|
+
|
101
|
+
a2 = Rational(1, alfa)
|
102
|
+
b2 = Rational(1, beta)
|
103
|
+
|
104
|
+
self.new(x2: b2, y2: -a2, x: -2 * b2 * center.x, y: 2 * a2 * center.y, k: b2 * center.x ** 2 - a2 * center.y ** 2 + -position * a2 * b2)
|
105
|
+
end
|
106
|
+
|
107
|
+
def focus1
|
108
|
+
if position == 1
|
109
|
+
return Point.new(x: center.x + Math.sqrt(a2 + b2), y: center.y)
|
110
|
+
end
|
111
|
+
if position == -1
|
112
|
+
return Point.new(x: center.x, y: center.y + Math.sqrt(a2 + b2))
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def focus2
|
117
|
+
if position == 1
|
118
|
+
return Point.new(x: center.x - Math.sqrt(a2 + b2), y: center.y)
|
119
|
+
end
|
120
|
+
if position == -1
|
121
|
+
return Point.new(x: center.x, y: center.y - Math.sqrt(a2 + b2))
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def focal_distance
|
126
|
+
Point.distance(focus1, focus2)
|
127
|
+
end
|
128
|
+
|
129
|
+
def center
|
130
|
+
Point.new(x: centrum[:xc], y: centrum[:yc])
|
131
|
+
end
|
132
|
+
|
133
|
+
def difference_of_distances
|
134
|
+
if position == 1
|
135
|
+
return 2 * Math.sqrt(a2)
|
136
|
+
end
|
137
|
+
if position == -1
|
138
|
+
return 2 * Math.sqrt(b2)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def congruent?(hyperbola)
|
143
|
+
hyperbola.instance_of?(Hyperbola) and
|
144
|
+
hyperbola.eccentricity == self.eccentricity
|
145
|
+
end
|
146
|
+
|
147
|
+
def == (hyperbola)
|
148
|
+
hyperbola.instance_of?(Hyperbola) and
|
149
|
+
hyperbola.focus1 == self.focus1 and hyperbola.focus2 == self.focus2 and hyperbola.difference_of_distances == self.difference_of_distances
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def validation
|
155
|
+
|
156
|
+
if signum(@x2_coeff * @y2_coeff) >= 0 or determinator == @k_coeff
|
157
|
+
raise ArgumentError.new('Invalid coefficients!')
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
def position
|
163
|
+
signum(determinator - @k_coeff)
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require('cartesius/numerificator')
|
2
|
+
|
3
|
+
module Cartesius
|
4
|
+
|
5
|
+
class Line
|
6
|
+
include Numerificator
|
7
|
+
VERTICAL_SLOPE = Float::INFINITY
|
8
|
+
HORIZONTAL_SLOPE = 0
|
9
|
+
|
10
|
+
def initialize(x:, y:, k:)
|
11
|
+
@x_coeff, @y_coeff, @k_coeff = x.to_r, y.to_r, k.to_r
|
12
|
+
validation
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.create(slope:, known_term:)
|
16
|
+
new(x: -slope.to_r, y: 1, k: -known_term.to_r)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.horizontal(known_term:)
|
20
|
+
new(x: 0, y: 1, k: -known_term.to_r)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.vertical(known_term:)
|
24
|
+
new(x: 1, y: 0, k: -known_term.to_r)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.by_points(point1:, point2:)
|
28
|
+
if point1 == point2
|
29
|
+
raise ArgumentError.new('Points must be different!')
|
30
|
+
end
|
31
|
+
|
32
|
+
if point1.aligned_horizontally_with?(point2)
|
33
|
+
return horizontal(known_term: point1.y)
|
34
|
+
end
|
35
|
+
|
36
|
+
if point1.aligned_vertically_with?(point2)
|
37
|
+
return vertical(known_term: point1.x)
|
38
|
+
end
|
39
|
+
|
40
|
+
slope = Rational(point2.y - point1.y, point2.x - point1.x)
|
41
|
+
known_term = point1.y - slope * point1.x
|
42
|
+
|
43
|
+
create(slope: slope, known_term: known_term)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.x_axis
|
47
|
+
horizontal(known_term: 0)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.y_axis
|
51
|
+
vertical(known_term: 0)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.ascending_bisector
|
55
|
+
new(x: -1, y: 1, k: 0)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.descending_bisector
|
59
|
+
new(x: 1, y: 1, k: 0)
|
60
|
+
end
|
61
|
+
|
62
|
+
def slope
|
63
|
+
@y_coeff == 0 ? VERTICAL_SLOPE : Rational(-@x_coeff, @y_coeff)
|
64
|
+
end
|
65
|
+
|
66
|
+
def known_term
|
67
|
+
@y_coeff == 0 ? Rational(-@k_coeff, @x_coeff) : Rational(-@k_coeff, @y_coeff)
|
68
|
+
end
|
69
|
+
|
70
|
+
def x_axis?
|
71
|
+
self == Line.x_axis
|
72
|
+
end
|
73
|
+
|
74
|
+
def y_axis?
|
75
|
+
self == Line.y_axis
|
76
|
+
end
|
77
|
+
|
78
|
+
def ascending_bisector?
|
79
|
+
self == Line.ascending_bisector
|
80
|
+
end
|
81
|
+
|
82
|
+
def descending_bisector?
|
83
|
+
self == Line.descending_bisector
|
84
|
+
end
|
85
|
+
|
86
|
+
def horizontal?
|
87
|
+
slope == HORIZONTAL_SLOPE
|
88
|
+
end
|
89
|
+
|
90
|
+
def vertical?
|
91
|
+
slope == VERTICAL_SLOPE
|
92
|
+
end
|
93
|
+
|
94
|
+
def inclined?
|
95
|
+
(not horizontal?) and (not vertical?)
|
96
|
+
end
|
97
|
+
|
98
|
+
def ascending?
|
99
|
+
slope != VERTICAL_SLOPE and slope > 0
|
100
|
+
end
|
101
|
+
|
102
|
+
def descending?
|
103
|
+
slope < 0
|
104
|
+
end
|
105
|
+
|
106
|
+
def parallel?(line)
|
107
|
+
slope == line.slope
|
108
|
+
end
|
109
|
+
|
110
|
+
def perpendicular?(line)
|
111
|
+
if slope == 0
|
112
|
+
return line.slope == VERTICAL_SLOPE
|
113
|
+
end
|
114
|
+
if slope == VERTICAL_SLOPE
|
115
|
+
return line.slope == 0
|
116
|
+
end
|
117
|
+
slope * line.slope == -1
|
118
|
+
end
|
119
|
+
|
120
|
+
def include?(point)
|
121
|
+
if vertical?
|
122
|
+
return known_term == point.x
|
123
|
+
end
|
124
|
+
point.y == slope * point.x + known_term
|
125
|
+
end
|
126
|
+
|
127
|
+
def x_intercept
|
128
|
+
@x_coeff.zero? ? nil : -Rational(@k_coeff, @x_coeff)
|
129
|
+
end
|
130
|
+
|
131
|
+
def y_intercept
|
132
|
+
@y_coeff.zero? ? nil : -Rational(@k_coeff, @y_coeff)
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_equation
|
136
|
+
equationfy(
|
137
|
+
'x' => @x_coeff, 'y' => @y_coeff, '1' => @k_coeff
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
141
|
+
def congruent?(line)
|
142
|
+
line.instance_of?(Line)
|
143
|
+
end
|
144
|
+
|
145
|
+
def == (line)
|
146
|
+
congruent?(line) and
|
147
|
+
line.slope == self.slope and line.known_term == self.known_term
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def validation
|
153
|
+
if (@x_coeff == 0 and @y_coeff == 0)
|
154
|
+
raise ArgumentError.new('Invalid coefficients!')
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require('cartesius/point')
|
2
|
+
require('cartesius/line')
|
3
|
+
require('cartesius/numerificator')
|
4
|
+
require('cartesius/cramer')
|
5
|
+
|
6
|
+
module Cartesius
|
7
|
+
|
8
|
+
class Parabola
|
9
|
+
include Numerificator
|
10
|
+
|
11
|
+
# Conic
|
12
|
+
# Conic equation type: ax^2 + dx - y + f = 0
|
13
|
+
def initialize(x2:, x:, k:)
|
14
|
+
@x2_coeff, @x_coeff, @y_coeff, @k_coeff = x2.to_r, x.to_r, -1, k.to_r
|
15
|
+
validation
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.unitary_convex
|
19
|
+
new(x2: 1, x: 0, k: 0)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.unitary_concave
|
23
|
+
new(x2: -1, x: 0, k: 0)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.by_definition(directrix:, focus:)
|
27
|
+
if directrix.include?(focus)
|
28
|
+
raise ArgumentError.new('Focus belongs to directrix!')
|
29
|
+
end
|
30
|
+
|
31
|
+
unless directrix.horizontal?
|
32
|
+
raise ArgumentError.new('Directrix is not parallel to x-axis!')
|
33
|
+
end
|
34
|
+
|
35
|
+
a = Rational(1, 2 * (focus.y - directrix.y_intercept))
|
36
|
+
b = -2 * a * focus.x
|
37
|
+
c = a * (focus.x ** 2) + focus.y - Rational(1, 4 * a)
|
38
|
+
|
39
|
+
self.new(x2: -a, x: -b, k: -c)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.by_canonical(focus:, gap:)
|
43
|
+
if gap.zero?
|
44
|
+
raise ArgumentError.new('Gap must not be zero!')
|
45
|
+
end
|
46
|
+
|
47
|
+
a = gap
|
48
|
+
b = -2 * a * focus.x
|
49
|
+
c = (focus.x ** 2) + focus.y - Rational(1, 4 * a)
|
50
|
+
|
51
|
+
self.new(x2: -a, x: -b, k: -c)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.by_points(point1:, point2:, point3:)
|
55
|
+
|
56
|
+
if point1 == point2 or point1 == point3 or point2 == point3
|
57
|
+
raise ArgumentError.new('Points must be distinct!')
|
58
|
+
end
|
59
|
+
|
60
|
+
a, b, c = Cramer.solution3(
|
61
|
+
[point1.x ** 2, point1.x, 1],
|
62
|
+
[point2.x ** 2, point2.x, 1],
|
63
|
+
[point3.x ** 2, point3.x, 1],
|
64
|
+
[point1.y, point2.y, point3.y]
|
65
|
+
)
|
66
|
+
|
67
|
+
self.new(x2: -a, x: -b, k: -c)
|
68
|
+
end
|
69
|
+
|
70
|
+
def directrix
|
71
|
+
Line.horizontal(known_term: Rational(-delta - 1, 4 * @x2_coeff))
|
72
|
+
end
|
73
|
+
|
74
|
+
def focus
|
75
|
+
Point.new(x: Rational(-@x_coeff, 2 * @x2_coeff), y: Rational(1 - delta, 4 * @x2_coeff))
|
76
|
+
end
|
77
|
+
|
78
|
+
def vertex
|
79
|
+
Point.new(x: Rational(-@x_coeff, 2 * @x2_coeff), y: Rational(-delta, 4 * @x2_coeff))
|
80
|
+
end
|
81
|
+
|
82
|
+
def symmetry_axis
|
83
|
+
Line.vertical(known_term: Rational(-@x_coeff, 2* @x2_coeff))
|
84
|
+
end
|
85
|
+
|
86
|
+
def unitary_convex?
|
87
|
+
self == Parabola.unitary_convex
|
88
|
+
end
|
89
|
+
|
90
|
+
def unitary_concave?
|
91
|
+
self == Parabola.unitary_concave
|
92
|
+
end
|
93
|
+
|
94
|
+
def == (parabola)
|
95
|
+
parabola.instance_of?(Parabola) and
|
96
|
+
parabola.focus == self.focus and parabola.directrix == self.directrix
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_equation
|
100
|
+
equationfy(
|
101
|
+
'x^2' => @x2_coeff, 'x' => @x_coeff, 'y' => @y_coeff, '1' => @k_coeff
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def validation
|
108
|
+
if @x2_coeff == 0
|
109
|
+
raise ArgumentError.new('Invalid coefficients!')
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def delta
|
114
|
+
(@x_coeff ** 2) - (4 * @x2_coeff * @k_coeff)
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require('cartesius/numerificator')
|
2
|
+
|
3
|
+
module Cartesius
|
4
|
+
|
5
|
+
class Point
|
6
|
+
include Numerificator
|
7
|
+
attr_reader :x, :y
|
8
|
+
|
9
|
+
def initialize(x:, y:)
|
10
|
+
@x, @y = x.to_r, y.to_r
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.origin
|
14
|
+
new(x: 0, y: 0)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.distance(point1, point2)
|
18
|
+
Math.sqrt(
|
19
|
+
(point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def origin?
|
24
|
+
self == Point.origin
|
25
|
+
end
|
26
|
+
|
27
|
+
def aligned_horizontally_with?(point)
|
28
|
+
@y == point.y
|
29
|
+
end
|
30
|
+
|
31
|
+
def aligned_vertically_with?(point)
|
32
|
+
@x == point.x
|
33
|
+
end
|
34
|
+
|
35
|
+
def distance_from(point)
|
36
|
+
Math.sqrt(
|
37
|
+
(@x - point.x) ** 2 + (@y - point.y) ** 2
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_coordinates
|
42
|
+
"(#{stringfy(x)}; #{stringfy(y)})"
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_equation
|
46
|
+
equationfy(
|
47
|
+
'x^2' => 1, 'y^2' => 1, 'x' => -2 * @x, 'y' => -2 * @y, '1' => @x ** 2 + @y ** 2
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def congruent?(point)
|
52
|
+
point.instance_of?(Point)
|
53
|
+
end
|
54
|
+
|
55
|
+
def == (point)
|
56
|
+
congruent?(point) and
|
57
|
+
point.x == @x and point.y == @y
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require('cartesius/point')
|
2
|
+
require('cartesius/line')
|
3
|
+
|
4
|
+
module Cartesius
|
5
|
+
|
6
|
+
class Segment
|
7
|
+
extend Forwardable
|
8
|
+
attr_reader :extreme1, :extreme2
|
9
|
+
def_delegators(:@line, :horizontal?, :vertical?, :inclined?, :ascending?, :descending?)
|
10
|
+
|
11
|
+
def initialize(extreme1:, extreme2:)
|
12
|
+
@extreme1, @extreme2 = extreme1, extreme2
|
13
|
+
validation
|
14
|
+
@line = Line.by_points(point1: @extreme1, point2: @extreme2)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_line
|
18
|
+
@line
|
19
|
+
end
|
20
|
+
|
21
|
+
def length
|
22
|
+
Point.distance(@extreme1, @extreme2)
|
23
|
+
end
|
24
|
+
|
25
|
+
def mid
|
26
|
+
Point.new(
|
27
|
+
x: Rational(@extreme1.x + @extreme2.x, 2),
|
28
|
+
y: Rational(@extreme1.y + @extreme2.y, 2)
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def extremes
|
33
|
+
[@extreme1, @extreme2]
|
34
|
+
end
|
35
|
+
|
36
|
+
def congruent?(segment)
|
37
|
+
segment.instance_of?(Segment) and
|
38
|
+
segment.length == self.length
|
39
|
+
end
|
40
|
+
|
41
|
+
def == (segment)
|
42
|
+
unless segment.instance_of?(Segment)
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
|
46
|
+
(segment.extreme1 == self.extreme1 and segment.extreme2 == self.extreme2) or
|
47
|
+
(segment.extreme1 == self.extreme2 and segment.extreme2 == self.extreme1)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def validation
|
53
|
+
if @extreme1 == @extreme2
|
54
|
+
raise ArgumentError.new('Extremes cannot be the same!')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cartesius
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mauro Quaglia
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-11-07 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: The cartesian plan and its elements.
|
14
|
+
email: mauroquaglia@libero.it
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/cartesius/circumference.rb
|
20
|
+
- lib/cartesius/ellipse.rb
|
21
|
+
- lib/cartesius/hyperbola.rb
|
22
|
+
- lib/cartesius/line.rb
|
23
|
+
- lib/cartesius/parabola.rb
|
24
|
+
- lib/cartesius/point.rb
|
25
|
+
- lib/cartesius/segment.rb
|
26
|
+
homepage: https://github.com/MauroQuaglia/cartesius
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 2.6.13
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: The cartesian coordinate system.
|
50
|
+
test_files: []
|