Class: Ruby2D::Polygon

Inherits:
Object
  • Object
show all
Includes:
Renderable
Defined in:
lib/ruby2d/polygon.rb

Overview

A closed polygon defined by N vertices (N >= 3), filled or stroked.

Constant Summary

Constants included from Interactive

Interactive::OBJECT_EVENTS, Interactive::OBJECT_EVENT_FILTER_PREDICATES

Instance Attribute Summary collapse

Attributes included from Renderable

#color, #padding_bottom, #padding_left, #padding_right, #padding_top, #visible, #x_align, #y_align, #z

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Renderable

#_alignment_anchor_dx, #_alignment_anchor_dy, #_apply_padding, #_extract_alignment, #_point_in_polygon?, #_point_on_segment?, #_require_numeric_position, #_resolve_alignment, #_unrotate, #_validate_dimensions, #add, #colour=, flatten_color, flatten_per_vertex, flatten_points, flatten_resolved_color, #hide, #opacity, #opacity=, #padding=, #remove, resolve_color_or_default, resolve_single_color, #show

Methods included from Interactive

#_fire_event, #interactive?, #off, #on

Constructor Details

#initialize(points:, z: 0, rotate: 0, rx: nil, ry: nil, color: nil, colour: nil, opacity: nil, fill: true, stroke_width: 0, stroke_color: nil, stroke_colour: nil, add: true, visible: true) ⇒ Polygon

Create a polygon points is an array of [x, y] pairs with N >= 3 vertices

Raises:

  • (ArgumentError)


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/ruby2d/polygon.rb', line 14

def initialize(points:, z: 0, rotate: 0, rx: nil, ry: nil,
               color: nil, colour: nil, opacity: nil,
               fill: true, stroke_width: 0, stroke_color: nil, stroke_colour: nil,
               add: true, visible: true)
  raise ArgumentError, 'Polygon requires at least 3 points' if points.length < 3
  raise ArgumentError, 'points must be an array of [x, y] pairs' \
    unless points.all? { |p| p.is_a?(Array) && p.length == 2 }

  @coordinates = points.flat_map { |x, y| [x.to_f, y.to_f] }
  @z = z
  @rotate = rotate
  @_user_rx = rx
  @_user_ry = ry
  self.color = color || colour || 'white'
  self.opacity = opacity unless opacity.nil?
  @fill = fill
  @stroke_width = stroke_width
  self.stroke_color = stroke_color || stroke_colour || (color || colour)
  self.stroke_color.opacity = opacity unless opacity.nil?
  @visible = visible
  self.add if add
end

Instance Attribute Details

#fillObject

Returns the value of attribute fill.



8
9
10
# File 'lib/ruby2d/polygon.rb', line 8

def fill
  @fill
end

#rotateObject

Returns the value of attribute rotate.



8
9
10
# File 'lib/ruby2d/polygon.rb', line 8

def rotate
  @rotate
end

#stroke_colorObject Also known as: stroke_colour

Returns the value of attribute stroke_color.



9
10
11
# File 'lib/ruby2d/polygon.rb', line 9

def stroke_color
  @stroke_color
end

#stroke_widthObject

Returns the value of attribute stroke_width.



8
9
10
# File 'lib/ruby2d/polygon.rb', line 8

def stroke_width
  @stroke_width
end

Class Method Details

.render(points:, rotate: 0, rx: nil, ry: nil, color: nil, colour: nil, opacity: nil, fill: true, stroke_width: 0, stroke_color: nil, stroke_colour: nil) ⇒ Object

Render a polygon without creating an instance

Raises:

  • (ArgumentError)


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/ruby2d/polygon.rb', line 147

def self.render(points:, rotate: 0, rx: nil, ry: nil,
                color: nil, colour: nil, opacity: nil,
                fill: true, stroke_width: 0, stroke_color: nil, stroke_colour: nil)
  Window.render_ready_check
  raise ArgumentError, 'Polygon requires at least 3 points' if points.length < 3

  n = points.length
  coords = Renderable.flatten_points(points)
  fill_input = color || colour
  pvc = Renderable.flatten_color(fill_input, n, opacity, label: self)

  if rotate != 0
    ccx = 0.0; ccy = 0.0
    n.times do |i|
      ccx += coords[i * 2]
      ccy += coords[i * 2 + 1]
    end
    ccx /= n
    ccy /= n
    coords = rotate_coords(coords, rotate, rx || ccx, ry || ccy)
  end

  Ext.draw_polygon(coords, pvc) if fill
  # Flatten the stroke colors only when actually stroking; an explicitly
  # given stroke color is still validated at stroke_width 0 to match the
  # instance constructor.
  if stroke_width > 0
    pvs = Renderable.flatten_color(stroke_color || stroke_colour || fill_input, n, opacity, label: self)
    Ext.stroke_path(coords, stroke_width, pvs, true)
  elsif stroke_color || stroke_colour
    Renderable.flatten_color(stroke_color || stroke_colour, n, opacity, label: self)
  end
end

Instance Method Details

#color=(color) ⇒ Object

Set the fill color (single or per-vertex array). Per-vertex array length must match vertex_count.



128
129
130
131
132
133
134
135
136
# File 'lib/ruby2d/polygon.rb', line 128

def color=(color)
  cs = Color.set(color)
  if cs.is_a?(Color::Set) && cs.length != vertex_count
    raise ArgumentError,
          "`#{self.class}` requires #{vertex_count} colors, one for each vertex. #{cs.length} were given."
  end
  @color = cs
  @_cc = nil
end

#contains?(x, y) ⇒ Boolean

Test whether (x, y) lies inside the polygon, using the even-odd ray-cast rule (shared by every filled polygonal shape — see _point_in_polygon?). Matches the rendered fill for non-self-intersecting polygons (convex and concave); self-intersecting input isn't fully supported by the renderer, so the test can diverge from the drawn pixels there.

Returns:

  • (Boolean)


63
64
65
66
# File 'lib/ruby2d/polygon.rb', line 63

def contains?(x, y)
  x, y = _unrotate(x, y)
  _point_in_polygon?(@coordinates, x, y)
end

#heightObject



53
54
55
56
# File 'lib/ruby2d/polygon.rb', line 53

def height
  ys = @coordinates.each_slice(2).map(&:last)
  ys.max - ys.min
end

#pointsObject

The vertices as an array of [x, y] pairs



43
44
45
# File 'lib/ruby2d/polygon.rb', line 43

def points
  @coordinates.each_slice(2).to_a
end

#rxObject

Get the rotation center x coordinate



107
108
109
# File 'lib/ruby2d/polygon.rb', line 107

def rx
  @_user_rx.nil? ? x : @_user_rx
end

#rx=(val) ⇒ Object

Set the rotation center x coordinate



117
118
119
# File 'lib/ruby2d/polygon.rb', line 117

def rx=(val)
  @_user_rx = val
end

#ryObject

Get the rotation center y coordinate



112
113
114
# File 'lib/ruby2d/polygon.rb', line 112

def ry
  @_user_ry.nil? ? y : @_user_ry
end

#ry=(val) ⇒ Object

Set the rotation center y coordinate



122
123
124
# File 'lib/ruby2d/polygon.rb', line 122

def ry=(val)
  @_user_ry = val
end

#vertex_countObject

Number of vertices



38
39
40
# File 'lib/ruby2d/polygon.rb', line 38

def vertex_count
  @coordinates.length / 2
end

#widthObject

Bounding-box width and height (extent across all vertices)



48
49
50
51
# File 'lib/ruby2d/polygon.rb', line 48

def width
  xs = @coordinates.each_slice(2).map(&:first)
  xs.max - xs.min
end

#xObject

Centroid x (average of vertex x coordinates)



69
70
71
72
73
74
# File 'lib/ruby2d/polygon.rb', line 69

def x
  sum = 0.0
  n = vertex_count
  n.times { |i| sum += @coordinates[i * 2] }
  sum / n
end

#x=(new_x) ⇒ Object

Set the centroid x coordinate, translating all vertices



85
86
87
88
89
90
91
92
93
# File 'lib/ruby2d/polygon.rb', line 85

def x=(new_x)
  _require_numeric_position(:x, new_x)
  dx = new_x - x
  i = 0
  while i < @coordinates.length
    @coordinates[i] += dx
    i += 2
  end
end

#yObject

Centroid y (average of vertex y coordinates)



77
78
79
80
81
82
# File 'lib/ruby2d/polygon.rb', line 77

def y
  sum = 0.0
  n = vertex_count
  n.times { |i| sum += @coordinates[i * 2 + 1] }
  sum / n
end

#y=(new_y) ⇒ Object

Set the centroid y coordinate, translating all vertices



96
97
98
99
100
101
102
103
104
# File 'lib/ruby2d/polygon.rb', line 96

def y=(new_y)
  _require_numeric_position(:y, new_y)
  dy = new_y - y
  i = 1
  while i < @coordinates.length
    @coordinates[i] += dy
    i += 2
  end
end