compat_geom2d 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CONTRIBUTERS +4 -0
- data/LICENSE +21 -0
- data/README.md +49 -0
- data/Rakefile +102 -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 +40 -0
- data/lib/geom2d/utils/sorted_linked_list.rb +156 -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 +88 -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
|