lib/geometry/polyline.rb in geometry-6.1 vs lib/geometry/polyline.rb in geometry-6.2
- old
+ new
@@ -46,12 +46,11 @@
previous
else
if @edges.last
new_edge = Edge.new(previous, n)
if @edges.last.parallel?(new_edge)
- popped_edge = @edges.pop # Remove the previous Edge
- @vertices.pop(@edges.size ? 1 : 2) # Remove the now unused vertex, or vertices
+ popped_edge = pop_edge # Remove the previous Edge
if n == popped_edge.first
popped_edge.first
else
push_edge Edge.new(popped_edge.first, n)
push_vertex popped_edge.first
@@ -92,20 +91,68 @@
def eql?(other)
@vertices.zip(other.vertices).all? {|a,b| a == b}
end
alias :== :eql?
+ # @group Attributes
+
+ # @return [Point] The upper-right corner of the bounding rectangle that encloses the {Polyline}
+ def max
+ vertices.reduce {|memo, vertex| Point[[memo.x, vertex.x].max, [memo.y, vertex.y].max] }
+ end
+
+ # @return [Point] The lower-left corner of the bounding rectangle that encloses the {Polyline}
+ def min
+ vertices.reduce {|memo, vertex| Point[[memo.x, vertex.x].min, [memo.y, vertex.y].min] }
+ end
+
+ # @return [Array<Point>] The lower-left and upper-right corners of the enclosing bounding rectangle
+ def minmax
+ vertices.reduce([vertices.first, vertices.first]) {|memo, vertex| [Point[[memo.first.x, vertex.x].min, [memo.first.y, vertex.y].min], Point[[memo.last.x, vertex.x].max, [memo.last.y, vertex.y].max]] }
+ end
+
+ # @endgroup
+
# Clone the receiver, close it, then return it
# @return [Polyline] the closed clone of the receiver
def close
clone.close!
end
# Close the receiver and return it
# @return [Polyline] the receiver after closing
def close!
- push_edge Edge.new(@edges.last.last, @edges.first.first) unless @edges.empty? || closed?
+ unless @edges.empty?
+ # NOTE: parallel? is use here instead of collinear? because the
+ # edges are connected, and will therefore be collinear if
+ # they're parallel
+
+ if closed?
+ if @edges.first.parallel?(@edges.last)
+ unshift_edge Edge.new(@edges.last.first, shift_edge.last)
+ end
+ elsif
+ closing_edge = Edge.new(@edges.last.last, @edges.first.first)
+
+ # If the closing edge is collinear with the last edge, then
+ # simply extened the last edge to fill the gap
+ if @edges.last.parallel?(closing_edge)
+ closing_edge = Edge.new(pop_edge.first, @edges.first.first)
+ end
+
+ # Check that the new closing_edge isn't zero-length
+ if closing_edge.first != closing_edge.last
+ # If the closing edge is collinear with the first edge, then
+ # extend the first edge "backwards" to fill the gap
+ if @edges.first.parallel?(closing_edge)
+ unshift_edge Edge.new(closing_edge.first, shift_edge.last)
+ else
+ push_edge closing_edge
+ end
+ end
+ end
+ end
self
end
# Check to see if the {Polyline} is closed (ie. is it a {Polygon}?)
# @return [Bool] true if the {Polyline} is closed (the first vertex is equal to the last vertex)
@@ -303,16 +350,44 @@
end
[intersection, intersection_at]
end
# @endgroup
+ # Pop the last edge, and its associated vertices
+ # @return [Edge] the popped {Edge}
+ def pop_edge
+ old = @edges.pop # Remove the last Edge
+ if 0 == @edges.length # Remove all vertices if the only Edge was popped
+ @vertices.clear
+ elsif old.last == @vertices.last # Remove the last vertex if it was used by the popped Edge
+ @vertices.pop
+ end
+ old
+ end
+
def push_edge(*e)
@edges.push *e
@edges.uniq!
end
def push_vertex(*v)
@vertices.push *v
@vertices.uniq!
+ end
+
+ # Remove the first {Edge} and its associated vertices
+ # @return [Edge] the shifted {Edge}
+ def shift_edge
+ @vertices.shift((@edges.size > 1) ? 1 : 2)
+ @edges.shift
+ end
+
+ # Prepend an {Edge} and its vertices
+ # @param edge [Edge] the {Edge} to unshift
+ def unshift_edge(edge)
+ @vertices.unshift(edge.last) unless edge.last == @edges.first.first
+ @vertices.unshift(edge.first) # unless edge.first == @edges.last.last
+ @vertices.pop if @vertices.last == @vertices.first
+ @edges.unshift(edge)
end
end
end