cartesius 0.0.1
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/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: []
|