geom2d 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/CONTRIBUTERS +3 -0
- data/LICENSE +21 -0
- data/README.md +49 -0
- data/Rakefile +101 -0
- data/VERSION +1 -0
- data/lib/geom2d.rb +70 -0
- data/lib/geom2d/algorithms.rb +35 -0
- data/lib/geom2d/algorithms/polygon_operation.rb +435 -0
- data/lib/geom2d/bounding_box.rb +84 -0
- data/lib/geom2d/point.rb +145 -0
- data/lib/geom2d/polygon.rb +108 -0
- data/lib/geom2d/polygon_set.rb +67 -0
- data/lib/geom2d/segment.rb +202 -0
- data/lib/geom2d/utils.rb +38 -0
- data/lib/geom2d/utils/sorted_linked_list.rb +154 -0
- data/lib/geom2d/version.rb +16 -0
- data/test/geom2d/algorithms/test_polygon_operation.rb +229 -0
- data/test/geom2d/test_algorithms.rb +26 -0
- data/test/geom2d/test_bounding_box.rb +37 -0
- data/test/geom2d/test_point.rb +148 -0
- data/test/geom2d/test_polygon.rb +69 -0
- data/test/geom2d/test_polygon_set.rb +41 -0
- data/test/geom2d/test_segment.rb +253 -0
- data/test/geom2d/utils/test_sorted_linked_list.rb +72 -0
- data/test/test_helper.rb +15 -0
- metadata +68 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
# -*- frozen_string_literal: true -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# geom2d - 2D Geometric Objects and Algorithms
|
5
|
+
# Copyright (C) 2018 Thomas Leitner <t_leitner@gmx.at>
|
6
|
+
#
|
7
|
+
# This software may be modified and distributed under the terms
|
8
|
+
# of the MIT license. See the LICENSE file for details.
|
9
|
+
#++
|
10
|
+
|
11
|
+
module Geom2D
|
12
|
+
|
13
|
+
# Represents an axis aligned bounding box.
|
14
|
+
#
|
15
|
+
# An empty bounding box contains just the point at origin.
|
16
|
+
class BoundingBox
|
17
|
+
|
18
|
+
# The minimum x-coordinate.
|
19
|
+
attr_reader :min_x
|
20
|
+
|
21
|
+
# The minimum y-coordinate.
|
22
|
+
attr_reader :min_y
|
23
|
+
|
24
|
+
# The maximum x-coordinate.
|
25
|
+
attr_reader :max_x
|
26
|
+
|
27
|
+
# The maximum y-coordinate.
|
28
|
+
attr_reader :max_y
|
29
|
+
|
30
|
+
# Creates a new BoundingBox.
|
31
|
+
def initialize(min_x = 0, min_y = 0, max_x = 0, max_y = 0)
|
32
|
+
@min_x = min_x
|
33
|
+
@min_y = min_y
|
34
|
+
@max_x = max_x
|
35
|
+
@max_y = max_y
|
36
|
+
end
|
37
|
+
|
38
|
+
# Updates this bounding box to also contain the given bounding box or point.
|
39
|
+
def add!(other)
|
40
|
+
case other
|
41
|
+
when BoundingBox
|
42
|
+
@min_x = [min_x, other.min_x].min
|
43
|
+
@min_y = [min_y, other.min_y].min
|
44
|
+
@max_x = [max_x, other.max_x].max
|
45
|
+
@max_y = [max_y, other.max_y].max
|
46
|
+
when Point
|
47
|
+
@min_x = [min_x, other.x].min
|
48
|
+
@min_y = [min_y, other.y].min
|
49
|
+
@max_x = [max_x, other.x].max
|
50
|
+
@max_y = [max_y, other.y].max
|
51
|
+
else
|
52
|
+
raise ArgumentError, "Can only use another BoundingBox or Point"
|
53
|
+
end
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the width of the bounding box.
|
58
|
+
def width
|
59
|
+
@max_x - @min_x
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns the height of the bounding box.
|
63
|
+
def height
|
64
|
+
@max_y - @min_y
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns a bounding box containing this bounding box and the argument.
|
68
|
+
def add(other)
|
69
|
+
dup.add!(other)
|
70
|
+
end
|
71
|
+
alias + add
|
72
|
+
|
73
|
+
# Returns the bounding box as an array of the form [min_x, min_y, max_x, max_y].
|
74
|
+
def to_a
|
75
|
+
[@min_x, @min_y, @max_x, @max_y]
|
76
|
+
end
|
77
|
+
|
78
|
+
def inspect #:nodoc:
|
79
|
+
"BBox[#{min_x}, #{min_y}, #{max_x}, #{max_y}]"
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/lib/geom2d/point.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
# -*- frozen_string_literal: true -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# geom2d - 2D Geometric Objects and Algorithms
|
5
|
+
# Copyright (C) 2018 Thomas Leitner <t_leitner@gmx.at>
|
6
|
+
#
|
7
|
+
# This software may be modified and distributed under the terms
|
8
|
+
# of the MIT license. See the LICENSE file for details.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'geom2d'
|
12
|
+
|
13
|
+
module Geom2D
|
14
|
+
|
15
|
+
# Represents a point.
|
16
|
+
class Point
|
17
|
+
|
18
|
+
include Utils
|
19
|
+
|
20
|
+
# The x-coordinate.
|
21
|
+
attr_reader :x
|
22
|
+
|
23
|
+
# The y-coordinate.
|
24
|
+
attr_reader :y
|
25
|
+
|
26
|
+
# Creates a new Point from the given coordinates.
|
27
|
+
def initialize(x, y)
|
28
|
+
@x = x
|
29
|
+
@y = y
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the point's bounding box (i.e. a bounding box containing only the point itself).
|
33
|
+
def bbox
|
34
|
+
BoundingBox.new(x, y, x, y)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the distance from this point to the given point.
|
38
|
+
def distance(point)
|
39
|
+
Math.hypot(point.x - x, point.y - y)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns self.
|
43
|
+
def +@
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the point mirrored in the origin.
|
48
|
+
def -@
|
49
|
+
Point.new(-x, -y)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Depending on the type of the argument, either adds a number to each coordinate or adds two
|
53
|
+
# points.
|
54
|
+
def +(other)
|
55
|
+
case other
|
56
|
+
when Point
|
57
|
+
Point.new(x + other.x, y + other.y)
|
58
|
+
when Numeric
|
59
|
+
Point.new(x + other, y + other)
|
60
|
+
when Array
|
61
|
+
self + Geom2D::Point(other)
|
62
|
+
else
|
63
|
+
raise ArgumentError, "Invalid argument class, must be Numeric or Point"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Depending on the type of the argument, either subtracts a number from each coordinate or
|
68
|
+
# subtracts the other point from this one.
|
69
|
+
def -(other)
|
70
|
+
case other
|
71
|
+
when Point
|
72
|
+
Point.new(x - other.x, y - other.y)
|
73
|
+
when Numeric
|
74
|
+
Point.new(x - other, y - other)
|
75
|
+
when Array
|
76
|
+
self - Geom2D::Point(other)
|
77
|
+
else
|
78
|
+
raise ArgumentError, "Invalid argument class, must be Numeric or Point"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Depending on the type of the argument, either multiplies this point with the other point (dot
|
83
|
+
# product) or multiplies each coordinate with the given number.
|
84
|
+
def *(other)
|
85
|
+
case other
|
86
|
+
when Point
|
87
|
+
x * other.x + y * other.y
|
88
|
+
when Numeric
|
89
|
+
Point.new(x * other, y * other)
|
90
|
+
when Array
|
91
|
+
self * Geom2D::Point(other)
|
92
|
+
else
|
93
|
+
raise ArgumentError, "Invalid argument class, must be Numeric or Point"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Multiplies this point with the other point using the dot product.
|
98
|
+
def dot(other)
|
99
|
+
self * other
|
100
|
+
end
|
101
|
+
|
102
|
+
# Performs the wedge product of this point with the other point.
|
103
|
+
def wedge(other)
|
104
|
+
other = Geom2D::Point(other)
|
105
|
+
x * other.y - other.x * y
|
106
|
+
end
|
107
|
+
|
108
|
+
# Divides each coordinate by the given number.
|
109
|
+
def /(other)
|
110
|
+
case other
|
111
|
+
when Numeric
|
112
|
+
Point.new(x / other.to_f, y / other.to_f)
|
113
|
+
else
|
114
|
+
raise ArgumentError, "Invalid argument class, must be Numeric"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Compares this point to the other point, using floating point equality.
|
119
|
+
#
|
120
|
+
# See Utils#float_equal.
|
121
|
+
def ==(other)
|
122
|
+
case other
|
123
|
+
when Point
|
124
|
+
float_equal(x, other.x) && float_equal(y, other.y)
|
125
|
+
when Array
|
126
|
+
self == Geom2D::Point(other)
|
127
|
+
else
|
128
|
+
false
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Allows destructuring of a point into an array.
|
133
|
+
def to_ary
|
134
|
+
[x, y]
|
135
|
+
end
|
136
|
+
alias to_a to_ary
|
137
|
+
|
138
|
+
def inspect #:nodoc:
|
139
|
+
"(#{x}, #{y})"
|
140
|
+
end
|
141
|
+
alias to_s inspect
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# -*- frozen_string_literal: true -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# geom2d - 2D Geometric Objects and Algorithms
|
5
|
+
# Copyright (C) 2018 Thomas Leitner <t_leitner@gmx.at>
|
6
|
+
#
|
7
|
+
# This software may be modified and distributed under the terms
|
8
|
+
# of the MIT license. See the LICENSE file for details.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'geom2d'
|
12
|
+
|
13
|
+
module Geom2D
|
14
|
+
|
15
|
+
# Represents a polygon.
|
16
|
+
class Polygon
|
17
|
+
|
18
|
+
# Creates a new Polygon object. The +vertices+ argument has to be an array of point-like
|
19
|
+
# objects.
|
20
|
+
def initialize(vertices = [])
|
21
|
+
@vertices = []
|
22
|
+
vertices.each {|value| @vertices << Geom2D::Point(value) }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns one since a polygon object represents a single polygon.
|
26
|
+
def nr_of_contours
|
27
|
+
1
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the number of vertices in the polygon.
|
31
|
+
def nr_of_vertices
|
32
|
+
@vertices.size
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the i-th vertex of the polygon.
|
36
|
+
def [](i)
|
37
|
+
@vertices[i]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Adds a new vertex to the end of the polygon.
|
41
|
+
def add(x, y = nil)
|
42
|
+
@vertices << Geom2D::Point(x, y)
|
43
|
+
self
|
44
|
+
end
|
45
|
+
alias << add
|
46
|
+
|
47
|
+
# Removes the last vertex of the polygon.
|
48
|
+
def pop
|
49
|
+
@vertices.pop
|
50
|
+
end
|
51
|
+
|
52
|
+
# Calls the given block once for each vertex of the polygon.
|
53
|
+
#
|
54
|
+
# If no block is given, an Enumerator is returned.
|
55
|
+
def each_vertex(&block)
|
56
|
+
return to_enum(__method__) unless block_given?
|
57
|
+
@vertices.each(&block)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Calls the given block once for each segment in the polygon.
|
61
|
+
#
|
62
|
+
# If no block is given, an Enumerator is returned.
|
63
|
+
def each_segment
|
64
|
+
return to_enum(__method__) unless block_given?
|
65
|
+
return unless @vertices.size > 1
|
66
|
+
|
67
|
+
0.upto(@vertices.size - 2) do |i|
|
68
|
+
yield(Geom2D::Segment(@vertices[i], @vertices[i + 1]))
|
69
|
+
end
|
70
|
+
yield(Geom2D::Segment(@vertices[-1], @vertices[0]))
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the BoundingBox of this polygon, or an empty BoundingBox if the polygon has no
|
74
|
+
# vertices.
|
75
|
+
def bbox
|
76
|
+
return BoundingBox.new if @vertices.empty?
|
77
|
+
result = @vertices.first.bbox
|
78
|
+
@vertices[1..-1].each {|v| result.add!(v) }
|
79
|
+
result
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns +true+ if the vertices of the polygon are ordered in a counterclockwise fashion.
|
83
|
+
def ccw?
|
84
|
+
return true if @vertices.empty?
|
85
|
+
area = @vertices[-1].wedge(@vertices[0])
|
86
|
+
1.upto(@vertices.size - 2) {|i| area += @vertices[i].wedge(@vertices[i + 1]) }
|
87
|
+
area >= 0
|
88
|
+
end
|
89
|
+
|
90
|
+
# Reverses the direction of the vertices (and therefore the segments).
|
91
|
+
def reverse!
|
92
|
+
@vertices.reverse!
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns an array with the vertices of the polygon.
|
96
|
+
def to_ary
|
97
|
+
@vertices.dup
|
98
|
+
end
|
99
|
+
alias to_a to_ary
|
100
|
+
|
101
|
+
def inspect #:nodoc:
|
102
|
+
"Polygon#{@vertices}"
|
103
|
+
end
|
104
|
+
alias to_s inspect
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# -*- frozen_string_literal: true -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# geom2d - 2D Geometric Objects and Algorithms
|
5
|
+
# Copyright (C) 2018 Thomas Leitner <t_leitner@gmx.at>
|
6
|
+
#
|
7
|
+
# This software may be modified and distributed under the terms
|
8
|
+
# of the MIT license. See the LICENSE file for details.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'geom2d/polygon'
|
12
|
+
|
13
|
+
module Geom2D
|
14
|
+
|
15
|
+
# Represents a set of polygons.
|
16
|
+
class PolygonSet
|
17
|
+
|
18
|
+
# The array of polygons.
|
19
|
+
attr_reader :polygons
|
20
|
+
|
21
|
+
# Creates a new PolygonSet with the given polygons.
|
22
|
+
def initialize(polygons = [])
|
23
|
+
@polygons = polygons
|
24
|
+
end
|
25
|
+
|
26
|
+
# Adds a polygon to this set.
|
27
|
+
def add(polygon)
|
28
|
+
@polygons << polygon
|
29
|
+
self
|
30
|
+
end
|
31
|
+
alias << add
|
32
|
+
|
33
|
+
# Creates a new polygon set by combining the polygons from this set and the other one.
|
34
|
+
def join(other)
|
35
|
+
PolygonSet.new(@polygons + other.polygons)
|
36
|
+
end
|
37
|
+
alias + join
|
38
|
+
|
39
|
+
# Calls the given block once for each segment of each polygon in the set.
|
40
|
+
#
|
41
|
+
# If no block is given, an Enumerator is returned.
|
42
|
+
def each_segment(&block)
|
43
|
+
return to_enum(__method__) unless block_given?
|
44
|
+
@polygons.each {|polygon| polygon.each_segment(&block) }
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the number of polygons in this set.
|
48
|
+
def nr_of_contours
|
49
|
+
@polygons.size
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the BoundingBox of all polygons in the set, or +nil+ if it contains no polygon.
|
53
|
+
def bbox
|
54
|
+
return BoundingBox.new if @polygons.empty?
|
55
|
+
result = @polygons.first.bbox
|
56
|
+
@polygons[1..-1].each {|v| result.add!(v.bbox) }
|
57
|
+
result
|
58
|
+
end
|
59
|
+
|
60
|
+
def inspect #:nodoc:
|
61
|
+
"PolygonSet#{@polygons}"
|
62
|
+
end
|
63
|
+
alias to_s inspect
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
# -*- frozen_string_literal: true -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# geom2d - 2D Geometric Objects and Algorithms
|
5
|
+
# Copyright (C) 2018 Thomas Leitner <t_leitner@gmx.at>
|
6
|
+
#
|
7
|
+
# This software may be modified and distributed under the terms
|
8
|
+
# of the MIT license. See the LICENSE file for details.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'geom2d'
|
12
|
+
|
13
|
+
module Geom2D
|
14
|
+
|
15
|
+
# Represents a line segment.
|
16
|
+
class Segment
|
17
|
+
|
18
|
+
include Utils
|
19
|
+
|
20
|
+
# The start point of the segment.
|
21
|
+
attr_reader :start_point
|
22
|
+
|
23
|
+
# The end point of the segment.
|
24
|
+
attr_reader :end_point
|
25
|
+
|
26
|
+
# Creates a new Segment from the start to the end point. The arguments are converted to proper
|
27
|
+
# Geom2D::Point objects if needed.
|
28
|
+
def initialize(start_point, end_point)
|
29
|
+
@start_point = Geom2D::Point(start_point)
|
30
|
+
@end_point = Geom2D::Point(end_point)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns +true+ if the segment is degenerate, i.e. if it consists only of a point.
|
34
|
+
def degenerate?
|
35
|
+
@start_point == @end_point
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns +true+ if the segment is vertical.
|
39
|
+
def vertical?
|
40
|
+
float_equal(start_point.x, end_point.x)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns +true+ if the segment is horizontal.
|
44
|
+
def horizontal?
|
45
|
+
float_equal(start_point.y, end_point.y)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the left-most bottom-most point of the segment (either the start or the end point).
|
49
|
+
def min
|
50
|
+
if start_point.x < end_point.x ||
|
51
|
+
(float_equal(start_point.x, end_point.x) && start_point.y < end_point.y)
|
52
|
+
start_point
|
53
|
+
else
|
54
|
+
end_point
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the right-most top-most point of the segment (either the start or the end point).
|
59
|
+
def max
|
60
|
+
if start_point.x > end_point.x ||
|
61
|
+
(float_equal(start_point.x, end_point.x) && start_point.y > end_point.y)
|
62
|
+
start_point
|
63
|
+
else
|
64
|
+
end_point
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns the length of the segment.
|
69
|
+
def length
|
70
|
+
start_point.distance(end_point)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the direction vector of the segment as Geom2D::Point object.
|
74
|
+
def direction
|
75
|
+
end_point - start_point
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns the slope of the segment.
|
79
|
+
#
|
80
|
+
# If the segment is vertical, Float::INFINITY is returned.
|
81
|
+
def slope
|
82
|
+
if float_equal(start_point.x, end_point.x)
|
83
|
+
Float::INFINITY
|
84
|
+
else
|
85
|
+
(end_point.y - start_point.y).to_f / (end_point.x - start_point.x)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns the y-intercept, i.e. the point on the y-axis where the segment does/would intercept
|
90
|
+
# it.
|
91
|
+
def y_intercept
|
92
|
+
slope = self.slope
|
93
|
+
if slope == Float::INFINITY
|
94
|
+
nil
|
95
|
+
else
|
96
|
+
-start_point.x * slope + start_point.y
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Reverses the start and end point.
|
101
|
+
def reverse!
|
102
|
+
@start_point, @end_point = @end_point, @start_point
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns the intersection of this segment with the given one:
|
106
|
+
#
|
107
|
+
# +nil+:: No intersections
|
108
|
+
# Geom2D::Point:: Exactly one point
|
109
|
+
# Geom2D::Segment:: The segment overlapping both other segments.
|
110
|
+
def intersect(segment)
|
111
|
+
p0 = start_point
|
112
|
+
p1 = segment.start_point
|
113
|
+
d0 = direction
|
114
|
+
d1 = segment.direction
|
115
|
+
e = p1 - p0
|
116
|
+
|
117
|
+
cross = d0.wedge(d1).to_f # cross product of direction vectors
|
118
|
+
|
119
|
+
if cross.abs > Utils.precision # segments are not parallel
|
120
|
+
s = e.wedge(d1) / cross
|
121
|
+
return nil if s < 0 || s > 1
|
122
|
+
t = e.wedge(d0) / cross
|
123
|
+
return nil if t < 0 || t > 1
|
124
|
+
|
125
|
+
result = p0 + [s * d0.x, s * d0.y]
|
126
|
+
result = start_point if result == start_point
|
127
|
+
result = end_point if result == end_point
|
128
|
+
result = segment.start_point if result == segment.start_point
|
129
|
+
result = segment.end_point if result == segment.end_point
|
130
|
+
return result
|
131
|
+
end
|
132
|
+
|
133
|
+
return nil if e.wedge(d0).abs > Utils.precision # non-intersecting parallel segment lines
|
134
|
+
|
135
|
+
e0 = end_point
|
136
|
+
e1 = segment.end_point
|
137
|
+
|
138
|
+
# sort segment points by x-value
|
139
|
+
p0, e0 = e0, p0 if float_compare(p0.x, e0.x) > 0
|
140
|
+
p1, e1 = e1, p1 if float_compare(p1.x, e1.x) > 0
|
141
|
+
if float_compare(p0.x, p1.x) > 0
|
142
|
+
_p0, p1, e0, e1 = p1, p0, e1, e0
|
143
|
+
end
|
144
|
+
|
145
|
+
# p0 before or equal to p1
|
146
|
+
if float_compare(e0.x, p1.x) < 0 # e0 before p1
|
147
|
+
nil # no common point
|
148
|
+
elsif float_compare(e1.x, e0.x) <= 0 # e1 before or equal to e0
|
149
|
+
self.class.new(p1, e1) # p1-e1 inside p0-e0
|
150
|
+
elsif float_compare(p1.x, e0.x) == 0 # common endpoint p1=e0
|
151
|
+
p1
|
152
|
+
else
|
153
|
+
self.class.new(p1, e0) # s1 overlaps end of s0
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Returns self.
|
158
|
+
def +@
|
159
|
+
self
|
160
|
+
end
|
161
|
+
|
162
|
+
# Returns the segment mirrored in the origin.
|
163
|
+
def -@
|
164
|
+
Segment.new(-start_point, -end_point)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Adds the given vector (given as array or Geom2D::Point) to the segment, i.e. performs a
|
168
|
+
# translation.
|
169
|
+
def +(other)
|
170
|
+
case other
|
171
|
+
when Point, Array
|
172
|
+
Segment.new(start_point + other, end_point + other)
|
173
|
+
else
|
174
|
+
raise ArgumentError, "Invalid argument class, must be Point"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Subtracts the given vector (given as array or Geom2D::Point) from the segment, i.e. performs a
|
179
|
+
# translation.
|
180
|
+
def -(other)
|
181
|
+
case other
|
182
|
+
when Point, Array
|
183
|
+
Segment.new(start_point - other, end_point - other)
|
184
|
+
else
|
185
|
+
raise ArgumentError, "Invalid argument class, must be Point"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Compares this segment to the other, returning true if the end points match.
|
190
|
+
def ==(other)
|
191
|
+
return false unless other.kind_of?(Segment)
|
192
|
+
start_point == other.start_point && end_point == other.end_point
|
193
|
+
end
|
194
|
+
|
195
|
+
def inspect #:nodoc:
|
196
|
+
"Segment[#{start_point}-#{end_point}]"
|
197
|
+
end
|
198
|
+
alias to_s inspect
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|