Class: Geom2D::Segment

Inherits:
Object
  • Object
show all
Includes:
Utils
Defined in:
lib/geom2d/segment.rb

Overview

Represents a line segment.

Instance Attribute Summary collapse

Attributes included from Utils

#precision

Instance Method Summary collapse

Constructor Details

#initialize(start_point, end_point) ⇒ Segment

Creates a new Segment from the start to the end point. The arguments are converted to proper Geom2D::Point objects if needed.



28
29
30
31
# File 'lib/geom2d/segment.rb', line 28

def initialize(start_point, end_point)
  @start_point = Geom2D::Point(start_point)
  @end_point = Geom2D::Point(end_point)
end

Instance Attribute Details

#end_pointObject (readonly)

The end point of the segment.



24
25
26
# File 'lib/geom2d/segment.rb', line 24

def end_point
  @end_point
end

#start_pointObject (readonly)

The start point of the segment.



21
22
23
# File 'lib/geom2d/segment.rb', line 21

def start_point
  @start_point
end

Instance Method Details

#+(other) ⇒ Object

Adds the given vector (given as array or Geom2D::Point) to the segment, i.e. performs a translation.



174
175
176
177
178
179
180
181
# File 'lib/geom2d/segment.rb', line 174

def +(other)
  case other
  when Point, Array
    Segment.new(start_point + other, end_point + other)
  else
    raise ArgumentError, "Invalid argument class, must be Point"
  end
end

#+@Object

Returns self.



163
164
165
# File 'lib/geom2d/segment.rb', line 163

def +@
  self
end

#-(other) ⇒ Object

Subtracts the given vector (given as array or Geom2D::Point) from the segment, i.e. performs a translation.



185
186
187
188
189
190
191
192
# File 'lib/geom2d/segment.rb', line 185

def -(other)
  case other
  when Point, Array
    Segment.new(start_point - other, end_point - other)
  else
    raise ArgumentError, "Invalid argument class, must be Point"
  end
end

#-@Object

Returns the segment mirrored in the origin.



168
169
170
# File 'lib/geom2d/segment.rb', line 168

def -@
  Segment.new(-start_point, -end_point)
end

#==(other) ⇒ Object

Compares this segment to the other, returning true if the end points match.



195
196
197
198
# File 'lib/geom2d/segment.rb', line 195

def ==(other)
  return false unless other.kind_of?(Segment)
  start_point == other.start_point && end_point == other.end_point
end

#degenerate?Boolean

Returns true if the segment is degenerate, i.e. if it consists only of a point.

Returns:

  • (Boolean)


34
35
36
# File 'lib/geom2d/segment.rb', line 34

def degenerate?
  @start_point == @end_point
end

#directionObject

Returns the direction vector of the segment as Geom2D::Point object.



74
75
76
# File 'lib/geom2d/segment.rb', line 74

def direction
  end_point - start_point
end

#horizontal?Boolean

Returns true if the segment is horizontal.

Returns:

  • (Boolean)


44
45
46
# File 'lib/geom2d/segment.rb', line 44

def horizontal?
  float_equal(start_point.y, end_point.y)
end

#inspectObject Also known as: to_s

:nodoc:



200
201
202
# File 'lib/geom2d/segment.rb', line 200

def inspect #:nodoc:
  "Segment[#{start_point}-#{end_point}]"
end

#intersect(segment) ⇒ Object

Returns the intersection of this segment with the given one:

nil

No intersections

Geom2D::Point

Exactly one point

Geom2D::Segment

The segment overlapping both other segments.



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/geom2d/segment.rb', line 110

def intersect(segment)
  p0 = start_point
  p1 = segment.start_point
  d0x = end_point.x - start_point.x
  d0y = end_point.y - start_point.y
  d1x = segment.end_point.x - segment.start_point.x
  d1y = segment.end_point.y - segment.start_point.y
  ex = p1.x - p0.x
  ey = p1.y - p0.y

  cross = (d0x * d1y - d1x * d0y).to_f # cross product of direction vectors

  if cross.abs > Utils.precision # segments are not parallel
    s = (ex * d1y - d1x * ey) / cross
    return nil if s < 0 || s > 1
    t = (ex * d0y - d0x * ey) / cross
    return nil if t < 0 || t > 1

    result = p0 + Point.new(s * d0x, s * d0y)
    return case result
           when start_point then start_point
           when end_point then end_point
           when segment.start_point then segment.start_point
           when segment.end_point then segment.end_point
           else result
           end
  end

  return nil if (ex * d0y - d0x * ey).abs > Utils.precision # non-intersecting parallel segment lines

  e0 = end_point
  e1 = segment.end_point

  # sort segment points by x-value
  p0, e0 = e0, p0 if float_compare(p0.x, e0.x) > 0
  p1, e1 = e1, p1 if float_compare(p1.x, e1.x) > 0
  if float_compare(p0.x, p1.x) > 0
    _p0, p1, e0, e1 = p1, p0, e1, e0
  end

  # p0 before or equal to p1
  if float_compare(e0.x, p1.x) < 0     # e0 before p1
    nil                                # no common point
  elsif float_compare(e1.x, e0.x) <= 0 # e1 before or equal to e0
    self.class.new(p1, e1)             # p1-e1 inside p0-e0
  elsif float_compare(p1.x, e0.x) == 0 # common endpoint p1=e0
    p1
  else
    self.class.new(p1, e0)             # s1 overlaps end of s0
  end
end

#lengthObject

Returns the length of the segment.



69
70
71
# File 'lib/geom2d/segment.rb', line 69

def length
  start_point.distance(end_point)
end

#maxObject

Returns the right-most top-most point of the segment (either the start or the end point).



59
60
61
62
63
64
65
66
# File 'lib/geom2d/segment.rb', line 59

def max
  if start_point.x > end_point.x ||
      (float_equal(start_point.x, end_point.x) && start_point.y > end_point.y)
    start_point
  else
    end_point
  end
end

#minObject

Returns the left-most bottom-most point of the segment (either the start or the end point).



49
50
51
52
53
54
55
56
# File 'lib/geom2d/segment.rb', line 49

def min
  if start_point.x < end_point.x ||
      (float_equal(start_point.x, end_point.x) && start_point.y < end_point.y)
    start_point
  else
    end_point
  end
end

#reverse!Object

Reverses the start and end point.



101
102
103
# File 'lib/geom2d/segment.rb', line 101

def reverse!
  @start_point, @end_point = @end_point, @start_point
end

#slopeObject

Returns the slope of the segment.

If the segment is vertical, Float::INFINITY is returned.



81
82
83
84
85
86
87
# File 'lib/geom2d/segment.rb', line 81

def slope
  if float_equal(start_point.x, end_point.x)
    Float::INFINITY
  else
    (end_point.y - start_point.y).to_f / (end_point.x - start_point.x)
  end
end

#vertical?Boolean

Returns true if the segment is vertical.

Returns:

  • (Boolean)


39
40
41
# File 'lib/geom2d/segment.rb', line 39

def vertical?
  float_equal(start_point.x, end_point.x)
end

#y_interceptObject

Returns the y-intercept, i.e. the point on the y-axis where the segment does/would intercept it.



91
92
93
94
95
96
97
98
# File 'lib/geom2d/segment.rb', line 91

def y_intercept
  slope = self.slope
  if slope == Float::INFINITY
    nil
  else
    -start_point.x * slope + start_point.y
  end
end