Class: PerfectShape::Arc

Inherits:
Object
  • Object
show all
Defined in:
lib/perfect_shape/arc.rb

Overview

Constant Summary collapse

TYPES =
[:open, :chord, :pie]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type: :open, x: 0, y: 0, width: 1, height: 1, start: 0, extent: 360) ⇒ Arc

Returns a new instance of Arc.



30
31
32
33
34
35
36
37
38
# File 'lib/perfect_shape/arc.rb', line 30

def initialize(type: :open, x: 0, y: 0, width: 1, height: 1, start: 0, extent: 360)
  @type = type
  self.x = x
  self.y = y
  self.width = width
  self.height = height
  self.start = start
  self.extent = extent
end

Instance Attribute Details

#extentObject

Returns the value of attribute extent.



28
29
30
# File 'lib/perfect_shape/arc.rb', line 28

def extent
  @extent
end

#heightObject

Returns the value of attribute height.



28
29
30
# File 'lib/perfect_shape/arc.rb', line 28

def height
  @height
end

#startObject

Returns the value of attribute start.



28
29
30
# File 'lib/perfect_shape/arc.rb', line 28

def start
  @start
end

#typeObject

Returns the value of attribute type.



28
29
30
# File 'lib/perfect_shape/arc.rb', line 28

def type
  @type
end

#widthObject

Returns the value of attribute width.



28
29
30
# File 'lib/perfect_shape/arc.rb', line 28

def width
  @width
end

#xObject

Returns the value of attribute x.



28
29
30
# File 'lib/perfect_shape/arc.rb', line 28

def x
  @x
end

#yObject

Returns the value of attribute y.



28
29
30
# File 'lib/perfect_shape/arc.rb', line 28

def y
  @y
end

Instance Method Details

#contain?(x_or_point, y = nil) ⇒ Boolean

Checks if arc contains point denoted by point (two-number Array or x, y args)

Returns:

  • (Boolean)


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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/perfect_shape/arc.rb', line 65

def contain?(x_or_point, y = nil)
  x = x_or_point
  x, y = x if y.nil? && x_or_point.is_a?(Array) && x_or_point.size == 2
  x = BigDecimal(x.to_s)
  y = BigDecimal(y.to_s)
  return unless x && y
  # Normalize the coordinates compared to the ellipse
  # having a center at 0,0 and a radius of 0.5.
  ellw = width
  return false if (ellw <= 0.0)
  normx = (x - self.x) / ellw - 0.5
  ellh = height
  return false if (ellh <= 0.0)
  normy = (y - self.y) / ellh - 0.5
  dist_sq = (normx * normx) + (normy * normy)
  return false if (dist_sq >= 0.25)
  ang_ext = self.extent.abs
  return true if (ang_ext >= 360.0)
  inarc = contain_angle?(-1*Math.radians_to_degrees(Math.atan2(normy, normx)))
  
  return inarc if type == :pie
  # CHORD and OPEN behave the same way
  if inarc
    return true if ang_ext >= 180.0
    # point must be outside the "pie triangle"
  else
    return false if ang_ext <= 180.0
    # point must be inside the "pie triangle"
  end
  
  # The point is inside the pie triangle iff it is on the same
  # side of the line connecting the ends of the arc as the center.
  angle = Math.degrees_to_radians(-start)
  x1 = Math.cos(angle)
  y1 = Math.sin(angle)
  angle += Math.degrees_to_radians(-extent)
  x2 = Math.cos(angle)
  y2 = Math.sin(angle)
  inside = (Line.relative_ccw(x1, y1, x2, y2, 2*normx, 2*normy) *
                    Line.relative_ccw(x1, y1, x2, y2, 0, 0) >= 0)
  inarc ? !inside : inside
end

#contain_angle?(angle) ⇒ Boolean

Returns:

  • (Boolean)


108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/perfect_shape/arc.rb', line 108

def contain_angle?(angle)
  ang_ext = self.extent
  backwards = ang_ext < 0.0
  ang_ext = -ang_ext if backwards
  return true if ang_ext >= 360.0

  angle = normalize_degrees(angle) - normalize_degrees(start)
  angle = -angle if backwards
  angle += 360.0 if angle < 0.0

  (angle >= 0.0) && (angle < ang_ext)
end

#normalize_degrees(angle) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/perfect_shape/arc.rb', line 121

def normalize_degrees(angle)
  if angle > 180.0
    if angle <= (180.0 + 360.0)
      angle = angle - 360.0
    else
      angle = Math.ieee_remainder(angle, 360.0)
      # IEEEremainder can return -180 here for some input values...
      angle = 180.0 if angle == -180.0
    end
  elsif angle <= -180.0
    if angle > (-180.0 - 360.0)
      angle = angle + 360.0
    else
      angle = Math.ieee_remainder(angle, 360.0)
      # IEEEremainder can return -180 here for some input values...
      angle = 180.0 if angle == -180.0
    end
  end
  angle
end