Class: Geometry::Polyline

Inherits:
Object
  • Object
show all
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

Polygon

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(Edge, Edge, ...) ⇒ Polyline #initialize(Point, Point, ...) ⇒ Polyline

Construct a new Polyline from Points and/or Edges

The constructor will try to convert all of its arguments into {Point}s and
 {Edge}s. Then successive {Point}s will be collpased into {Edge}s. Successive
 {Edge}s that share a common vertex will be added to the new {Polyline}. If
 there's a gap between {Edge}s it will be automatically filled with a new
 {Edge}.

Overloads:

  • #initialize(Edge, Edge, ...) ⇒ Polyline
  • #initialize(Point, Point, ...) ⇒ Polyline


28
29
30
31
32
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
# File 'lib/geometry/polyline.rb', line 28

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

#edgesObject (readonly)

Returns the value of attribute edges.



16
17
18
# File 'lib/geometry/polyline.rb', line 16

def edges
  @edges
end

#verticesObject (readonly)

Returns the value of attribute vertices.



16
17
18
# File 'lib/geometry/polyline.rb', line 16

def vertices
  @vertices
end

Instance Method Details

#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.

Returns:



92
93
94
# File 'lib/geometry/polyline.rb', line 92

def eql?(other)
    @vertices.zip(other.vertices).all? {|a,b| a == b}
end

#offset(distance) ⇒ Polygon Also known as: leftset

Offset the receiver by the specified distance. A positive distance

will offset to the left, and a negative distance to the right.

Parameters:

  • distance (Number)

    The distance to offset by

Returns:



101
102
103
104
105
106
107
108
109
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
# File 'lib/geometry/polyline.rb', line 101

def offset(distance)
    bisectors = offset_bisectors(distance)
    offsets = bisectors.each_cons(2).to_a

    # Create the offset edges and then wrap them in Hashes so the edges
    #  can be altered while walking the array
    active_edges = edges.zip(offsets).map do |e,offset|
  offset = Edge.new(e.first+offset.first.vector, e.last+offset.last.vector)

  # Skip zero-length edges
  {:edge => (offset.first == offset.last) ? nil : offset}
    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

#rightset(distance) ⇒ Polygon

Rightset the receiver by the specified distance

Parameters:

  • distance (Number)

    The distance to offset by

Returns:



147
148
149
# File 'lib/geometry/polyline.rb', line 147

def rightset(distance)
    offset(-distance)
end