Class: Sevgi::Geometry::Ellipse
- Inherits:
-
Sevgi::Geometry::Element::Arced
- Object
- Element
- Sevgi::Geometry::Element::Arced
- Sevgi::Geometry::Ellipse
- Defined in:
- lib/sevgi/geometry/elements/ellipse.rb,
lib/sevgi/geometry/elements/ellipse/affine.rb,
lib/sevgi/geometry/elements/ellipse/length.rb
Overview
Immutable ellipse with positive radii, a center, and clockwise axis rotation. Parameter angles belong to the local ellipse axes, not to polar directions from the center.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#position ⇒ Sevgi::Geometry::Point
(also: #center)
readonly
Ellipse center.
-
#rotation ⇒ Float
readonly
Clockwise rotation of the local axes in degrees.
-
#rx ⇒ Float
readonly
Positive local x radius.
-
#ry ⇒ Float
readonly
Positive local y radius.
Class Method Summary collapse
-
.[](rx, ry, position: Origin, rotation: 0) ⇒ Sevgi::Geometry::Ellipse
Builds an ellipse from local radii and its position.
Instance Method Summary collapse
-
#approx(precision = nil) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Rebuilds an ellipse from rounded canonical fields.
-
#arc(extent:, starting_angle: 0) ⇒ Sevgi::Geometry::Arc
Selects a finite, directed arc on this ellipse.
-
#box ⇒ Sevgi::Geometry::Rect
Returns the axis-aligned bounds without display rounding.
-
#circular? ⇒ Boolean
Reports whether the radii are exactly equal.
-
#draw(node, **attributes) ⇒ Object
Draws an SVG ellipse using original geometric values.
-
#empty? ⇒ Boolean
Reports whether the ellipse has zero angular extent.
-
#equation ⇒ Sevgi::Geometry::Equation::Quadratic
Returns the complete quadratic carrier.
-
#equations ⇒ Array<Sevgi::Geometry::Equation::Quadratic>
Returns the immutable collection containing the complete carrier.
-
#initialize(rx, ry, position:, rotation:) ⇒ void
constructor
Creates an ellipse.
-
#inside?(point) ⇒ Boolean
Reports whether a point is inside or on the boundary.
-
#length ⇒ Float
Returns the perimeter with thread-independent numerical accuracy.
-
#on?(point) ⇒ Boolean
Reports whether a point matches its radial boundary reference at the current coordinate precision.
-
#perimeter ⇒ Float
Returns the closed boundary length.
-
#point(angle) ⇒ Sevgi::Geometry::Point
Evaluates the boundary at a local parameter angle.
-
#reflect(x: true, y: true) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Returns a reflected copy, preserving Circle where applicable.
-
#rotate(angle) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Rotates the center around the origin and the ellipse axes by the same angle.
-
#scale(sx, sy = Undefined) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Scales the ellipse from the origin.
-
#skew(ax, ay = Undefined) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Skews the ellipse from the origin.
-
#skew_x(angle) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Skews the ellipse along x.
-
#skew_y(angle) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Skews the ellipse along y.
-
#translate(dx, dy = Undefined) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Returns a translated copy.
Methods inherited from Sevgi::Geometry::Element::Arced
Methods inherited from Element
#at, #closed?, #ignorable?, #intersection, lined
Constructor Details
#initialize(rx, ry, position:, rotation:) ⇒ void
Creates an ellipse. Use the bracket constructor.
42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 42 def initialize(rx, ry, position:, rotation:) super() @rx, @ry = [[:rx, rx], [:ry, ry]].map do |field, value| value = Real[field, value] Error.("Ellipse #{field} must be positive") unless value.positive? value end @position = Tuple[Point, position] @rotation = Real[:rotation, rotation] end |
Instance Attribute Details
#position ⇒ Sevgi::Geometry::Point (readonly) Also known as: center
Returns ellipse center.
27 28 29 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 27 def position @position end |
#rotation ⇒ Float (readonly)
Returns clockwise rotation of the local axes in degrees.
29 30 31 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 29 def rotation @rotation end |
#rx ⇒ Float (readonly)
Returns positive local x radius.
31 32 33 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 31 def rx @rx end |
#ry ⇒ Float (readonly)
Returns positive local y radius.
33 34 35 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 33 def ry @ry end |
Class Method Details
.[](rx, ry, position: Origin, rotation: 0) ⇒ Sevgi::Geometry::Ellipse
Builds an ellipse from local radii and its position.
21 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 21 def self.[](rx, ry, position: Origin, rotation: 0) = new(rx, ry, position:, rotation:) |
Instance Method Details
#approx(precision = nil) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Rebuilds an ellipse from rounded canonical fields.
59 60 61 62 63 64 65 66 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 59 def approx(precision = nil) rebuild( F.approx(rx, precision), F.approx(ry, precision), position.approx(precision), F.approx(rotation, precision) ) end |
#arc(extent:, starting_angle: 0) ⇒ Sevgi::Geometry::Arc
Selects a finite, directed arc on this ellipse.
73 74 75 76 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 73 def arc(extent:, starting_angle: 0) parent = is_a?(Circle) ? Ellipse[rx, ry, position:] : self Arc.send(:new, parent, starting_angle:, extent:) end |
#box ⇒ Sevgi::Geometry::Rect
Returns the axis-aligned bounds without display rounding.
82 83 84 85 86 87 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 82 def box cosine, sine = F.cos(rotation), F.sin(rotation) dx = ::Math.hypot(rx * cosine, ry * sine) dy = ::Math.hypot(rx * sine, ry * cosine) Rect.from_corners([position.x - dx, position.y - dy], [position.x + dx, position.y + dy]) end |
#circular? ⇒ Boolean
Reports whether the radii are exactly equal.
93 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 93 def circular? = rx == ry |
#draw(node, **attributes) ⇒ Object
Draws an SVG ellipse using original geometric values.
101 102 103 104 105 106 107 108 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 101 def draw(node, **attributes) unless rotation.zero? transform = "rotate(#{rotation} #{position.x} #{position.y})" attributes = attributes.merge(transform: [attributes[:transform], transform].compact.join(" ")) end node.ellipse(cx: position.x, cy: position.y, rx:, ry:, **attributes) end |
#empty? ⇒ Boolean
Reports whether the ellipse has zero angular extent. A complete ellipse is never empty.
114 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 114 def empty? = false |
#equation ⇒ Sevgi::Geometry::Equation::Quadratic
Returns the complete quadratic carrier.
118 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 118 def equation = @equation ||= Equation.quadratic(*coefficients, origin: position) |
#equations ⇒ Array<Sevgi::Geometry::Equation::Quadratic>
Returns the immutable collection containing the complete carrier.
122 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 122 def equations = @equations ||= [equation].freeze |
#inside?(point) ⇒ Boolean
Reports whether a point is inside or on the boundary.
128 129 130 131 132 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 128 def inside?(point) point = Tuple[Point, point] local = local(point) ::Math.hypot(local.x / rx, local.y / ry) < 1.0 || on?(point) end |
#length ⇒ Float
Returns the perimeter with thread-independent numerical accuracy.
137 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 137 def length = @length ||= arc_length(0, 360) |
#on?(point) ⇒ Boolean
Reports whether a point matches its radial boundary reference at the current coordinate precision.
143 144 145 146 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 143 def on?(point) point = Tuple[Point, point] point != position && point.eq?(self.point(parameter(point))) end |
#perimeter ⇒ Float
Returns the closed boundary length.
150 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 150 def perimeter = length |
#point(angle) ⇒ Sevgi::Geometry::Point
Evaluates the boundary at a local parameter angle.
156 157 158 159 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 156 def point(angle) angle = Real[:angle, angle] % 360.0 Point[rx * F.cos(angle), ry * F.sin(angle)].rotate(rotation).translate(position.x, position.y) end |
#reflect(x: true, y: true) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Returns a reflected copy, preserving Circle where applicable.
166 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 166 def reflect(x: true, y: true) = affine(:reflect, x:, y:).first |
#rotate(angle) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Rotates the center around the origin and the ellipse axes by the same angle.
172 173 174 175 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 172 def rotate(angle) angle = Real[:angle, angle] rebuild(rx, ry, position.rotate(angle), rotation + angle) end |
#scale(sx, sy = Undefined) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Scales the ellipse from the origin. Unequal factors can widen Circle to Ellipse.
184 185 186 187 188 189 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 184 def scale(sx, sy = Undefined) sx, sy = Real[:sx, sx], Real[:sy, Undefined.default(sy, sx)] return affine(:scale, sx, sy).first unless sx == sy rebuild(rx * sx.abs, ry * sy.abs, position.scale(sx, sy), rotation + (sx.negative? ? 180 : 0)) end |
#skew(ax, ay = Undefined) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Skews the ellipse from the origin.
198 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 198 def skew(ax, ay = Undefined) = affine(:skew, ax, ay).first |
#skew_x(angle) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Skews the ellipse along x.
204 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 204 def skew_x(angle) = affine(:skew_x, angle).first |
#skew_y(angle) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Skews the ellipse along y.
210 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 210 def skew_y(angle) = affine(:skew_y, angle).first |
#translate(dx, dy = Undefined) ⇒ Sevgi::Geometry::Ellipse, Sevgi::Geometry::Circle
Returns a translated copy.
217 |
# File 'lib/sevgi/geometry/elements/ellipse.rb', line 217 def translate(dx, dy = Undefined) = rebuild(rx, ry, position.translate(dx, dy), rotation) |