Class: Gifenc::Geometry::Point
- Inherits:
-
Object
- Object
- Gifenc::Geometry::Point
- Defined in:
- lib/geometry.rb
Overview
Represents a point in the plane. It's essentially a wrapper for an Float array with 2 elements (the coordinates) and many geometric methods that aid working with them. It is used indistinctly for both points and vectors, and will be denoted as such throughout the code, depending on which interpretation is more relevant.
Instance Attribute Summary collapse
-
#x ⇒ Integer
The X coordinate of the point.
-
#y ⇒ Integer
The Y coordinate of the point.
Class Method Summary collapse
-
.parse(point, sys = :cartesian) ⇒ Point
Parse a point from an arbitrary argument.
-
.polar2rect(mod, arg) ⇒ Array<Float>
Convert polar coordinates to rectangular (Cartesian) coordinates.
-
.rect2polar(x, y) ⇒ Array<Float>
Convert rectangular (Cartesian) coordinates to polar coordinates.
Instance Method Summary collapse
-
#&(p) ⇒ Point
Compute the midpoint between this point and the given one.
-
#*(arg) ⇒ Point, Float
(also: #scale)
Scale a point or compute the dot product of two points.
-
#+(p) ⇒ Point
(also: #translate)
Add another point to this one.
-
#+@ ⇒ Point
Make all coordinates positive.
-
#-(p) ⇒ Point
Subtract another point to this one.
-
#-@ ⇒ Point
Take the opposite point with respect to the origin.
-
#/(s) ⇒ Point
Scale the point by the inverse of a factor.
-
#^(p) ⇒ Point
Reflect the point with respect to the given one.
-
#angle(p) ⇒ Float
Find the angle between this point and the given one.
-
#arg ⇒ Float
Return the angle (argument) of the point.
-
#ceil ⇒ Point
Convert to integer point by taking the ceiling part of the coordinates.
-
#coords_cartesian ⇒ Array<Float>
(also: #coords)
Return the standard (Cartesian) coordinates of the point.
-
#coords_polar ⇒ Array<Float>
(also: #polar)
Return the polar coordinates of the point.
-
#distance(p) ⇒ Object
Compute the Euclidean distance between two points.
-
#floor ⇒ Point
Convert to integer point by taking the floor part of the coordinates.
-
#initialize(x, y) ⇒ Point
constructor
Create a new point given its coordinates.
-
#negative?(p) ⇒ Boolean
Find whether the points are negatively aligned.
-
#norm ⇒ Float
(also: #norm_2, #mod)
Shortcut to compute the euclidean norm of the vector.
-
#norm_1 ⇒ Float
Shortcut to compute the 1-norm of the vector.
-
#norm_inf ⇒ Float
Shortcut to compute the infinity (maximum) norm of the vector.
-
#norm_p(p = 2) ⇒ Float
Compute the p-norm of the vector.
-
#normal_left ⇒ Point
Compute the left-hand (CCW) normal vector.
-
#normal_right ⇒ Point
(also: #normal)
Compute the right-hand (CW) normal vector.
-
#normalize ⇒ Point
(also: #normalize_2)
Shotcut to normalize the vector with respect to the euclidean norm.
-
#normalize_1 ⇒ Point
Shotcut to normalize the vector with respect to the 1-norm.
-
#normalize_inf ⇒ Point
Shotcut to normalize the vector with respect to the infinity norm.
-
#normalize_p(p) ⇒ Point
Normalize the vector with respect to the p-norm.
-
#orthogonal?(p) ⇒ Boolean
(also: #perpendicular?)
Find whether the given point is perpendicular to this one.
-
#parallel?(p) ⇒ Boolean
Find whether the given point / vector is parallel (proportional) to this one.
-
#positive?(p) ⇒ Boolean
Find whether the points are positively aligned.
-
#project(p1: nil, p2: nil, direction: nil, angle: nil) ⇒ Point
Project the point onto a line.
-
#reflect(t = 1, p1: nil, p2: nil, direction: nil, angle: nil) ⇒ Point
Reflect the point with respect to a line.
-
#rotate(angle, center = ORIGIN) ⇒ Point
Rotate the point by a certain angle about a given center.
-
#rotate_180(center = ORIGIN) ⇒ Point
Shortcut to rotate the point 180 degrees about a given center.
-
#rotate_left(center = ORIGIN) ⇒ Point
Shortcut to rotate the point 90 degrees counterclockwise about a given center.
-
#rotate_right(center = ORIGIN) ⇒ Point
Shortcut to rotate the point 90 degrees clockwise about a given center.
-
#round ⇒ Point
Convert to integer point by rounding the coordinates.
-
#to_i ⇒ Point
(also: #truncate)
Convert to integer point by taking the integer part of the coordinates.
-
#to_s ⇒ String
Format the point's coordinates in the usual form.
-
#zero? ⇒ Boolean
Whether the point is null, i.e., close enough to the origin.
-
#|(p) ⇒ Point
Project the point onto the given vector.
Constructor Details
#initialize(x, y) ⇒ Point
Create a new point given its coordinates. The coordinates are assumed to be the Cartesian coordinates with respect to the same axes as the desired image, and they need not be integers (though they'll be casted as such when actually drawing them).
71 72 73 74 |
# File 'lib/geometry.rb', line 71 def initialize(x, y) @x = x.to_f @y = y.to_f end |
Instance Attribute Details
#x ⇒ Integer
The X coordinate of the point.
21 22 23 |
# File 'lib/geometry.rb', line 21 def x @x end |
#y ⇒ Integer
The Y coordinate of the point.
25 26 27 |
# File 'lib/geometry.rb', line 25 def y @y end |
Class Method Details
.parse(point, sys = :cartesian) ⇒ Point
Parse a point from an arbitrary argument. It accepts either:
- A point object, in which case it returns itself.
- An array, in which case it creates a new point whose coordinates are the values of the array.
54 55 56 57 58 59 60 61 62 63 |
# File 'lib/geometry.rb', line 54 def self.parse(point, sys = :cartesian) if point.is_a?(Point) point elsif point.is_a?(Array) point = polar2rect(*point) if sys == :polar new(*point) else raise Exception::GeometryError, "Couldn't parse point from argument." end end |
.polar2rect(mod, arg) ⇒ Array<Float>
Convert polar coordinates to rectangular (Cartesian) coordinates.
32 33 34 |
# File 'lib/geometry.rb', line 32 def self.polar2rect(mod, arg) [mod * Math.cos(arg), mod * Math.sin(arg)] end |
.rect2polar(x, y) ⇒ Array<Float>
Convert rectangular (Cartesian) coordinates to polar coordinates.
40 41 42 |
# File 'lib/geometry.rb', line 40 def self.rect2polar(x, y) [(x ** 2 + y ** 2) ** 0.5, Math.atan2(y, x)] end |
Instance Method Details
#&(p) ⇒ Point
Compute the midpoint between this point and the given one.
148 149 150 |
# File 'lib/geometry.rb', line 148 def &(p) (self + Point.parse(p)) / 2 end |
#*(arg) ⇒ Point, Float Also known as: scale
Scale a point or compute the dot product of two points.
- If
arg
is Numeric, the point will be scaled by that factor. The return value will then be a new Point. - If
arg
is a Point, the scalar product of the two points will be computed. The return value will then be a Float.
113 114 115 116 117 118 119 120 |
# File 'lib/geometry.rb', line 113 def *(arg) if Numeric === arg Point.new(@x * arg, @y * arg) else p = Point.parse(arg) @x * p.x + @y * p.y end end |
#+(p) ⇒ Point Also known as: translate
Add another point to this one.
79 80 81 82 |
# File 'lib/geometry.rb', line 79 def +(p) p = Point.parse(p) Point.new(@x + p.x, @y + p.y) end |
#+@ ⇒ Point
Make all coordinates positive. This is equivalent to reflecting the point about the coordinate axes until it is in the first quadrant.
87 88 89 |
# File 'lib/geometry.rb', line 87 def +@ Point.new(@x.abs, @y.abs) end |
#-(p) ⇒ Point
Subtract another point to this one.
94 95 96 97 |
# File 'lib/geometry.rb', line 94 def -(p) p = Point.parse(p) Point.new(@x - p.x, @y - p.y) end |
#-@ ⇒ Point
Take the opposite point with respect to the origin. This is equivalent to performing half a rotation about the origin.
102 103 104 |
# File 'lib/geometry.rb', line 102 def -@ Point.new(-@x, -@y) end |
#/(s) ⇒ Point
Scale the point by the inverse of a factor.
125 126 127 |
# File 'lib/geometry.rb', line 125 def /(s) Point.new(@x / s, @y / s) end |
#^(p) ⇒ Point
Reflect the point with respect to the given one.
141 142 143 |
# File 'lib/geometry.rb', line 141 def ^(p) self + (Point.parse(p) - self) * 2 end |
#angle(p) ⇒ Float
Find the angle between this point and the given one. The angle will be in the interval [0, PI].
320 321 322 323 |
# File 'lib/geometry.rb', line 320 def angle(p) p = Point.parse(p) Math.acos((self * p) / (norm * p.norm)) end |
#arg ⇒ Float
Return the angle (argument) of the point. It is expressed in radian, between -PI and PI.
306 307 308 |
# File 'lib/geometry.rb', line 306 def arg Math.atan2(@y, @x) end |
#ceil ⇒ Point
Convert to integer point by taking the ceiling part of the coordinates.
434 435 436 |
# File 'lib/geometry.rb', line 434 def ceil Point.new(@x.ceil, @y.ceil) end |
#coords_cartesian ⇒ Array<Float> Also known as: coords
Return the standard (Cartesian) coordinates of the point. This consists on the X and Y values.
155 156 157 |
# File 'lib/geometry.rb', line 155 def coords_cartesian [@x, @y] end |
#coords_polar ⇒ Array<Float> Also known as: polar
Return the polar coordinates of the point. This consists on the module (euclidean norm) and argument (angle between -PI and PI).
164 165 166 |
# File 'lib/geometry.rb', line 164 def coords_polar [mod, arg] end |
#distance(p) ⇒ Object
Compute the Euclidean distance between two points.
260 261 262 |
# File 'lib/geometry.rb', line 260 def distance(p) (self - Point.parse(p)).norm end |
#floor ⇒ Point
Convert to integer point by taking the floor part of the coordinates.
425 426 427 |
# File 'lib/geometry.rb', line 425 def floor Point.new(@x.floor, @y.floor) end |
#negative?(p) ⇒ Boolean
Find whether the points are negatively aligned. This means that their scalar product is negative, and implies that they form an obtuse angle, i.e., they go roughly in the opposite direction.
356 357 358 |
# File 'lib/geometry.rb', line 356 def negative?(p) self * p < 0 end |
#norm ⇒ Float Also known as: norm_2, mod
Shortcut to compute the euclidean norm of the vector.
206 207 208 |
# File 'lib/geometry.rb', line 206 def norm norm_p(2) end |
#norm_1 ⇒ Float
Shortcut to compute the 1-norm of the vector.
199 200 201 |
# File 'lib/geometry.rb', line 199 def norm_1 (@x.abs + @y.abs).to_f end |
#norm_inf ⇒ Float
Shortcut to compute the infinity (maximum) norm of the vector.
216 217 218 |
# File 'lib/geometry.rb', line 216 def norm_inf [@x.abs, @y.abs].max.to_f end |
#norm_p(p = 2) ⇒ Float
Compute the p-norm of the vector. It should be p>0
.
192 193 194 |
# File 'lib/geometry.rb', line 192 def norm_p(p = 2) (@x.abs ** p + @y.abs ** p) ** (1.0 / p) end |
#normal_left ⇒ Point
Compute the left-hand (CCW) normal vector.
173 174 175 |
# File 'lib/geometry.rb', line 173 def normal_left Point.new(@y, -@x) end |
#normal_right ⇒ Point Also known as: normal
Compute the right-hand (CW) normal vector.
180 181 182 |
# File 'lib/geometry.rb', line 180 def normal_right Point.new(-@y, @x) end |
#normalize ⇒ Point Also known as: normalize_2
Shotcut to normalize the vector with respect to the euclidean norm.
243 244 245 |
# File 'lib/geometry.rb', line 243 def normalize normalize_p(2) end |
#normalize_1 ⇒ Point
Shotcut to normalize the vector with respect to the 1-norm.
235 236 237 |
# File 'lib/geometry.rb', line 235 def normalize_1 normalize_p(1) end |
#normalize_inf ⇒ Point
Shotcut to normalize the vector with respect to the infinity norm.
253 254 255 |
# File 'lib/geometry.rb', line 253 def normalize_inf normalize_gen(norm_inf) end |
#normalize_p(p) ⇒ Point
Normalize the vector with respect to the p-norm. It should be p>0
.
227 228 229 |
# File 'lib/geometry.rb', line 227 def normalize_p(p) normalize_gen(norm_p(p)) end |
#orthogonal?(p) ⇒ Boolean Also known as: perpendicular?
Find whether the given point is perpendicular to this one.
328 329 330 |
# File 'lib/geometry.rb', line 328 def orthogonal?(p) (self * Point.parse(p)).abs < PRECISION end |
#parallel?(p) ⇒ Boolean
Find whether the given point / vector is parallel (proportional) to this one.
338 339 340 |
# File 'lib/geometry.rb', line 338 def parallel?(p) angle(p).abs < PRECISION end |
#positive?(p) ⇒ Boolean
Find whether the points are positively aligned. This means that their scalar product is positive, and implies that they form an acute angle, i.e., they go roughly in the same direction.
347 348 349 |
# File 'lib/geometry.rb', line 347 def positive?(p) self * p > 0 end |
#project(p1: nil, p2: nil, direction: nil, angle: nil) ⇒ Point
Project the point onto a line. The line might be supplied by providing either of the following 3 options:
- Two different points from the line.
- A point and a direction vector (not necessarily normalized).
- A point and an angle. At least one point is therefore always required.
277 278 279 280 281 282 283 |
# File 'lib/geometry.rb', line 277 def project(p1: nil, p2: nil, direction: nil, angle: nil) raise Exception::GeometryError, "Couldn't determine line,\ at least one point must be supplied." if !p1 && !p2 point = Point.parse(p1 || p2) direction = Geometry.direction(p1: p1, p2: p2, angle: angle) unless direction point - ((point - self) | direction) end |
#reflect(t = 1, p1: nil, p2: nil, direction: nil, angle: nil) ⇒ Point
Reflect the point with respect to a line. The line might be supplied by providing either of the following 3 options:
- Two different points from the line.
- A point and a direction vector (not necessarily normalized).
- A point and an angle. At least one point is therefore always required.
298 299 300 301 |
# File 'lib/geometry.rb', line 298 def reflect(t = 1, p1: nil, p2: nil, direction: nil, angle: nil) proj = self.project(p1: p1, p2: p2, direction: direction, angle: angle) self + (proj - self) * (t + 1) end |
#rotate(angle, center = ORIGIN) ⇒ Point
Rotate the point by a certain angle about a given center.
364 365 366 367 368 369 370 371 372 373 |
# File 'lib/geometry.rb', line 364 def rotate(angle, center = ORIGIN) center = Point.parse(center) x_old = @x - center.x y_old = @y - center.y sin = Math.sin(angle) cos = Math.cos(angle) x = x_old * cos - y_old * sin y = x_old * sin + y_old * cos Point.new(x + center.x, y + center.y) end |
#rotate_180(center = ORIGIN) ⇒ Point
Shortcut to rotate the point 180 degrees about a given center.
393 394 395 |
# File 'lib/geometry.rb', line 393 def rotate_180(center = ORIGIN) rotate(Math::PI, center) end |
#rotate_left(center = ORIGIN) ⇒ Point
Shortcut to rotate the point 90 degrees counterclockwise about a given center.
379 380 381 |
# File 'lib/geometry.rb', line 379 def rotate_left(center = ORIGIN) rotate(-Math::PI / 2, center) end |
#rotate_right(center = ORIGIN) ⇒ Point
Shortcut to rotate the point 90 degrees clockwise about a given center.
386 387 388 |
# File 'lib/geometry.rb', line 386 def rotate_right(center = ORIGIN) rotate(Math::PI / 2, center) end |
#round ⇒ Point
Convert to integer point by rounding the coordinates.
405 406 407 |
# File 'lib/geometry.rb', line 405 def round Point.new(@x.round, @y.round) end |
#to_i ⇒ Point Also known as: truncate
Convert to integer point by taking the integer part of the coordinates.
414 415 416 |
# File 'lib/geometry.rb', line 414 def to_i Point.new(@x.to_i, @y.to_i) end |
#to_s ⇒ String
Format the point's coordinates in the usual form.
440 441 442 |
# File 'lib/geometry.rb', line 440 def to_s "(#{@x}, #{@y})" end |
#zero? ⇒ Boolean
Whether the point is null, i.e., close enough to the origin.
312 313 314 |
# File 'lib/geometry.rb', line 312 def zero? norm < PRECISION end |