Class: Geometry::Polyline
- Inherits:
-
Object
- Object
- Geometry::Polyline
- Defined in:
- lib/geometry/polyline.rb
Overview
A Polyline is like a Polygon in that it only contains straight lines, but also like a Path in that it isn’t necessarily closed.
http://en.wikipedia.org/wiki/Polyline
Usage
Direct Known Subclasses
Instance Attribute Summary collapse
-
#edges ⇒ Object
readonly
Returns the value of attribute edges.
- #options ⇒ Object
-
#vertices ⇒ Object
readonly
Returns the value of attribute vertices.
Bisectors collapse
-
#bisectors ⇒ Array<Vector>
Generate the angle bisector unit vectors for each vertex.
-
#left_bisectors ⇒ Array<Vector>
Generate left-side angle bisector unit vectors for each vertex.
-
#right_bisectors ⇒ Array<Vector>
Generate right-side angle bisector unit vectors for each vertex.
-
#spokes ⇒ Array<Vector>
Generate the spokes for each vertex.
Instance Method Summary collapse
-
#close ⇒ Polyline
Clone the receiver, close it, then return it.
-
#close! ⇒ Polyline
Close the receiver and return it.
- #closed? ⇒ Bool
-
#eql?(other) ⇒ Bool
(also: #==)
Check the equality of two Polylines.
-
#initialize(*args) ⇒ Polyline
constructor
Construct a new Polyline from Points and/or Edges.
-
#offset(distance) ⇒ Polyline
(also: #leftset)
Offset the receiver by the specified distance.
-
#reverse ⇒ Polyline
Clone the receiver, reverse it, then return it.
-
#reverse! ⇒ Polyline
Reverse the receiver and return it.
-
#rightset(distance) ⇒ Polyline
Rightset the receiver by the specified distance.
Constructor Details
#initialize(Edge, Edge, ...) ⇒ Polyline #initialize(Point, Point, ...) ⇒ Polyline
The constructor will try to convert all of its arguments into Geometry::Points and Edges. Then successive Geometry::Points will be collpased into Edges. Successive Edges that share a common vertex will be added to the new Geometry::Polyline. If there’s a gap between Edges it will be automatically filled with a new Edge.
Construct a new Polyline from Points and/or Edges
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/geometry/polyline.rb', line 33 def initialize(*args) args.map! {|a| (a.is_a?(Array) || a.is_a?(Vector)) ? Point[a] : a} args.each {|a| raise ArgumentError, "Unknown argument type #{a.class}" unless a.is_a?(Point) or a.is_a?(Edge) } @edges = []; @vertices = []; first = args.shift if first.is_a?(Point) @vertices.push first elsif first.is_a?(Edge) @edges.push first @vertices.push *(first.to_a) end args.reduce(@vertices.last) do |previous,n| if n.is_a?(Point) if n == previous # Ignore repeated Points 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 if n == popped_edge.first popped_edge.first else push_edge Edge.new(popped_edge.first, n) push_vertex popped_edge.first push_vertex n n end else push_edge Edge.new(previous, n) push_vertex n n end else push_edge Edge.new(previous, n) push_vertex n n end end elsif n.is_a?(Edge) if previous == n.first push_edge n push_vertex n.last elsif previous == n.last push_edge n.reverse! push_vertex n.last else e = Edge.new(previous, n.first) push_edge e, n push_vertex *(e.to_a), *(n.to_a) end n.last end end end |
Instance Attribute Details
#edges ⇒ Object (readonly)
Returns the value of attribute edges.
16 17 18 |
# File 'lib/geometry/polyline.rb', line 16 def edges @edges end |
#options ⇒ Object
18 19 20 21 |
# File 'lib/geometry/polyline.rb', line 18 def @options = {} if !@options @options end |
#vertices ⇒ Object (readonly)
Returns the value of attribute vertices.
16 17 18 |
# File 'lib/geometry/polyline.rb', line 16 def vertices @vertices end |
Instance Method Details
#bisectors ⇒ Array<Vector>
If the Geometry::Polyline isn’t closed (the normal case), then the first and last vertices will be given bisectors that are perpendicular to themselves.
Generate the angle bisector unit vectors for each vertex
141 142 143 144 |
# File 'lib/geometry/polyline.rb', line 141 def bisectors # Multiplying each bisector by the sign of k flips any bisectors that aren't pointing towards the interior of the angle bisector_map {|b, k| k <=> 0 } end |
#close ⇒ Polyline
Clone the receiver, close it, then return it
104 105 106 |
# File 'lib/geometry/polyline.rb', line 104 def close clone.close! end |
#close! ⇒ Polyline
Close the receiver and return it
110 111 112 113 |
# File 'lib/geometry/polyline.rb', line 110 def close! push_edge Edge.new(@edges.last.last, @edges.first.first) unless @edges.empty? || closed? self end |
#closed? ⇒ Bool
Check to see if the Geometry::Polyline is closed (ie. is it a Geometry::Polygon?)
117 118 119 |
# File 'lib/geometry/polyline.rb', line 117 def closed? @edges.last.last == @edges.first.first end |
#eql?(other) ⇒ Bool Also known as: ==
Check the equality of two Geometry::Polylines. Note that if two Geometry::Polylines have
opposite winding, but are otherwise identical, they will be considered unequal.
97 98 99 |
# File 'lib/geometry/polyline.rb', line 97 def eql?(other) @vertices.zip(other.vertices).all? {|a,b| a == b} end |
#left_bisectors ⇒ Array<Vector>
This is similar to the #bisector method, but generates vectors that always point to the left side of the Geometry::Polyline instead of towards the inside of each corner
Generate left-side angle bisector unit vectors for each vertex
149 150 151 |
# File 'lib/geometry/polyline.rb', line 149 def left_bisectors bisector_map end |
#offset(distance) ⇒ Polyline Also known as: leftset
A positive distance will offset to the left, and a negative distance to the right.
Offset the receiver by the specified distance
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/geometry/polyline.rb', line 175 def offset(distance) bisector_pairs = if closed? bisector_edges = offset_bisectors(distance) bisector_edges.push(bisector_edges.first).each_cons(2) else offset_bisectors(distance).each_cons(2) end # Create the offset edges and then wrap them in Hashes so the edges # can be altered while walking the array active_edges = edges.zip(bisector_pairs).map do |e,offset| offset_edge = Edge.new(e.first+offset.first.vector, e.last+offset.last.vector) # Skip zero-length edges {:edge => (offset_edge.first == offset_edge.last) ? nil : offset_edge} end # Walk the array and handle any intersections for i in 0..(active_edges.count-1) do e1 = active_edges[i][:edge] next unless e1 # Ignore deleted edges intersection, j = find_last_intersection(active_edges, i, e1) if intersection e2 = active_edges[j][:edge] if intersection.is_a? Point active_edges[i][:edge] = Edge.new(e1.first, intersection) active_edges[j][:edge] = Edge.new(intersection, e2.last) else # Handle the collinear case active_edges[i][:edge] = Edge.new(e1.first, e2.last) active_edges[j].delete(:edge) end # Delete everything between e1 and e2 for k in i..j do next if (k==i) or (k==j) # Exclude e1 and e2 active_edges[k].delete(:edge) end redo # Recheck the modified edges end end Polyline.new *(active_edges.map {|e| e[:edge]}.compact.map {|e| [e.first, e.last]}.flatten) end |
#reverse ⇒ Polyline
Clone the receiver, reverse it, then return it
123 124 125 |
# File 'lib/geometry/polyline.rb', line 123 def reverse self.class.new *(edges.reverse.map! {|edge| edge.reverse! }) end |
#reverse! ⇒ Polyline
Reverse the receiver and return it
129 130 131 132 133 |
# File 'lib/geometry/polyline.rb', line 129 def reverse! vertices.reverse! edges.reverse!.map! {|edge| edge.reverse! } self end |
#right_bisectors ⇒ Array<Vector>
This is similar to the #bisector method, but generates vectors that always point to the right side of the Geometry::Polyline instead of towards the inside of each corner
Generate right-side angle bisector unit vectors for each vertex
156 157 158 |
# File 'lib/geometry/polyline.rb', line 156 def right_bisectors bisector_map {|b, k| -1 } end |
#rightset(distance) ⇒ Polyline
Rightset the receiver by the specified distance
225 226 227 |
# File 'lib/geometry/polyline.rb', line 225 def rightset(distance) offset(-distance) end |
#spokes ⇒ Array<Vector>
If the Geometry::Polyline isn’t closed (the normal case), then the first and last vertices will be given bisectors that are perpendicular to themselves.
Generate the spokes for each vertex. A spoke is the same as a bisector, but in the oppostire direction (bisectors point towards the inside of each corner; spokes point towards the outside)
164 165 166 167 |
# File 'lib/geometry/polyline.rb', line 164 def spokes # Multiplying each bisector by the negated sign of k flips any bisectors that aren't pointing towards the exterior of the angle bisector_map {|b, k| 0 <=> k } end |