Class: Sevgi::Geometry::Segment
- Inherits:
-
Data
- Object
- Data
- Sevgi::Geometry::Segment
- Includes:
- Comparable
- Defined in:
- lib/sevgi/geometry/segment.rb,
lib/sevgi/geometry/segment.rb
Overview
Immutable polar displacement in SVG/screen coordinates.
A Segment has no position: length is a distance and angle is a
clockwise direction. Use #ending to apply it to a starting point or
#line when a placed, finite line is required. Segment[length, angle]
starts from polar components; Segment.(starting, ending) derives them
from two points.
Instance Attribute Summary collapse
-
#angle ⇒ Float
readonly
Clockwise angle in degrees.
-
#length ⇒ Float
readonly
Segment length.
Class Method Summary collapse
-
.[](length, angle) ⇒ Sevgi::Geometry::Segment
Creates a segment from polar components.
-
.call(starting, ending) ⇒ Sevgi::Geometry::Segment
Creates a segment from start and end points.
-
.downward(length) ⇒ Sevgi::Geometry::Segment
(also: vertical)
Returns a downward segment.
-
.eq?(this, that, precision: nil) ⇒ Boolean
Compares two segments with optional numeric precision.
-
.leftward(length) ⇒ Sevgi::Geometry::Segment
Returns a leftward segment.
-
.rightward(length) ⇒ Sevgi::Geometry::Segment
(also: horizontal)
Returns a rightward segment.
-
.upward(length) ⇒ Sevgi::Geometry::Segment
Returns an upward segment.
Instance Method Summary collapse
-
#<=>(other) ⇒ Integer?
Compares segments by length.
-
#approx(precision = nil) ⇒ Sevgi::Geometry::Segment
Returns a segment rounded to precision.
-
#ending(starting) ⇒ Sevgi::Geometry::Point
Returns the endpoint reached from a starting point.
-
#eq?(other, precision: nil) ⇒ Boolean
Compares this segment with optional numeric precision.
-
#eql?(other) ⇒ Boolean
Reports strict segment equality.
-
#hash ⇒ Integer
Returns a hash compatible with strict equality.
-
#initialize(length:, angle:) ⇒ void
constructor
Creates a segment.
-
#line(point = Origin) ⇒ Sevgi::Geometry::Line
Converts the segment into a line at a point.
-
#reverse ⇒ Sevgi::Geometry::Segment
Returns the opposite segment.
-
#x ⇒ Float
Returns the x component of the segment.
-
#y ⇒ Float
Returns the y component of the segment.
Constructor Details
#initialize(length:, angle:) ⇒ void
Creates a segment.
97 98 99 100 101 102 |
# File 'lib/sevgi/geometry/segment.rb', line 97 def initialize(length:, angle:) length = Real[:length, length] Error.("Segment length cannot be negative") if length.negative? super(length:, angle: Real[:angle, angle]) end |
Instance Attribute Details
#angle ⇒ Float (readonly)
Returns clockwise angle in degrees.
30 31 32 |
# File 'lib/sevgi/geometry/segment.rb', line 30 def angle @angle end |
#length ⇒ Float (readonly)
Returns segment length.
30 31 32 |
# File 'lib/sevgi/geometry/segment.rb', line 30 def length @length end |
Class Method Details
.[](length, angle) ⇒ Sevgi::Geometry::Segment
Creates a segment from polar components.
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 88 89 90 91 92 93 94 95 96 97 98 99 100 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 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/sevgi/geometry/segment.rb', line 30 Segment = Data.define(:length, :angle) do include Comparable # @!attribute [r] length # @return [Float] segment length # @!attribute [r] angle # @return [Float] clockwise angle in degrees # Creates a segment from start and end points. # @param starting [Sevgi::Geometry::Point, Array<Numeric>] start point # @param ending [Sevgi::Geometry::Point, Array<Numeric>] end point # @return [Sevgi::Geometry::Segment] # @raise [Sevgi::Geometry::Error] when either point cannot be coerced def self.call(starting, ending) starting, ending = Tuples[Point, starting, ending] self[Point.length(starting, ending), Point.angle(starting, ending)] end # Compares two segments with optional numeric precision. # @param this [Sevgi::Geometry::Segment, Array<Numeric>] first segment # @param that [Sevgi::Geometry::Segment, Array<Numeric>] second segment # @param precision [Integer, nil] decimal precision, or nil for the current function default # @return [Boolean] # @raise [Sevgi::Geometry::Error] when either segment cannot be coerced def self.eq?(this, that, precision: nil) this, that = Tuples[self, this, that] F.eq?(this.length, that.length, precision:) && F.eq?(this.angle, that.angle, precision:) end # Returns a downward segment. # @param length [Numeric] segment length # @return [Sevgi::Geometry::Segment] def self.downward(length) = self[length, 90.0] # Returns a leftward segment. # @param length [Numeric] segment length # @return [Sevgi::Geometry::Segment] def self.leftward(length) = self[length, 180.0] # Returns a rightward segment. # @param length [Numeric] segment length # @return [Sevgi::Geometry::Segment] def self.rightward(length) = self[length, 0.0] # Returns an upward segment. # @param length [Numeric] segment length # @return [Sevgi::Geometry::Segment] def self.upward(length) = self[length, -90.0] class << self # @overload horizontal(length) # Returns a rightward segment. # @param length [Numeric] segment length # @return [Sevgi::Geometry::Segment] alias_method :horizontal, :rightward # @overload vertical(length) # Returns a downward segment. # @param length [Numeric] segment length # @return [Sevgi::Geometry::Segment] alias_method :vertical, :downward end # Creates a segment. # @param length [Numeric] segment length # @param angle [Numeric] clockwise angle in degrees # @return [void] # @raise [Sevgi::Geometry::Error] when a component is not finite or length is negative def initialize(length:, angle:) length = Real[:length, length] Error.("Segment length cannot be negative") if length.negative? super(length:, angle: Real[:angle, angle]) end # Compares segments by length. # @param other [Object] segment or two-item length/angle array to compare # @return [Integer, nil] comparison result, or nil when other is not a valid segment value def <=>(other) length <=> Tuple[Segment, other].length rescue Error nil end # Returns a segment rounded to precision. # @param precision [Integer, nil] decimal precision, or nil for the current function default # @return [Sevgi::Geometry::Segment] def approx(precision = nil) = with(length: F.approx(length, precision), angle: F.approx(angle, precision)) # Returns the endpoint reached from a starting point. # @param starting [Sevgi::Geometry::Point, Array<Numeric>] start point # @return [Sevgi::Geometry::Point] # @raise [Sevgi::Geometry::Error] when starting cannot be coerced def ending(starting) = Tuple[Point, starting].translate(x, y) # Compares this segment with optional numeric precision. # @param other [Sevgi::Geometry::Segment, Array<Numeric>] segment to compare # @param precision [Integer, nil] decimal precision, or nil for the current function default # @return [Boolean] # @raise [Sevgi::Geometry::Error] when other cannot be coerced def eq?(other, precision: nil) = self.class.eq?(self, other, precision:) # Reports strict segment equality. # @param other [Object] object to compare # @return [Boolean] def eql?(other) = self.class == other.class && deconstruct == other.deconstruct # Returns a hash compatible with strict equality. # @return [Integer] def hash = [self.class, *deconstruct].hash # Converts the segment into a line at a point. # @param point [Sevgi::Geometry::Point, Array<Numeric>] line start point # @return [Sevgi::Geometry::Line] # @raise [Sevgi::Geometry::Error] when point cannot be coerced def line(point = Origin) = Line[length, angle, position: Tuple[Point, point]] # Returns the opposite segment. # @return [Sevgi::Geometry::Segment] def reverse = with(angle: angle + 180.0) # Returns the x component of the segment. # @return [Float] def x = length * F.cos(angle) # Returns the y component of the segment. # @return [Float] def y = length * F.sin(angle) end |
.call(starting, ending) ⇒ Sevgi::Geometry::Segment
Creates a segment from start and end points.
43 44 45 46 |
# File 'lib/sevgi/geometry/segment.rb', line 43 def self.call(starting, ending) starting, ending = Tuples[Point, starting, ending] self[Point.length(starting, ending), Point.angle(starting, ending)] end |
.downward(length) ⇒ Sevgi::Geometry::Segment Also known as: vertical
Returns a downward segment.
62 |
# File 'lib/sevgi/geometry/segment.rb', line 62 def self.downward(length) = self[length, 90.0] |
.eq?(this, that, precision: nil) ⇒ Boolean
Compares two segments with optional numeric precision.
54 55 56 57 |
# File 'lib/sevgi/geometry/segment.rb', line 54 def self.eq?(this, that, precision: nil) this, that = Tuples[self, this, that] F.eq?(this.length, that.length, precision:) && F.eq?(this.angle, that.angle, precision:) end |
.leftward(length) ⇒ Sevgi::Geometry::Segment
Returns a leftward segment.
67 |
# File 'lib/sevgi/geometry/segment.rb', line 67 def self.leftward(length) = self[length, 180.0] |
.rightward(length) ⇒ Sevgi::Geometry::Segment Also known as: horizontal
Returns a rightward segment.
72 |
# File 'lib/sevgi/geometry/segment.rb', line 72 def self.rightward(length) = self[length, 0.0] |
.upward(length) ⇒ Sevgi::Geometry::Segment
Returns an upward segment.
77 |
# File 'lib/sevgi/geometry/segment.rb', line 77 def self.upward(length) = self[length, -90.0] |
Instance Method Details
#<=>(other) ⇒ Integer?
Compares segments by length.
107 108 109 110 111 |
# File 'lib/sevgi/geometry/segment.rb', line 107 def <=>(other) length <=> Tuple[Segment, other].length rescue Error nil end |
#approx(precision = nil) ⇒ Sevgi::Geometry::Segment
Returns a segment rounded to precision.
116 |
# File 'lib/sevgi/geometry/segment.rb', line 116 def approx(precision = nil) = with(length: F.approx(length, precision), angle: F.approx(angle, precision)) |
#ending(starting) ⇒ Sevgi::Geometry::Point
Returns the endpoint reached from a starting point.
122 |
# File 'lib/sevgi/geometry/segment.rb', line 122 def ending(starting) = Tuple[Point, starting].translate(x, y) |
#eq?(other, precision: nil) ⇒ Boolean
Compares this segment with optional numeric precision.
129 |
# File 'lib/sevgi/geometry/segment.rb', line 129 def eq?(other, precision: nil) = self.class.eq?(self, other, precision:) |
#eql?(other) ⇒ Boolean
Reports strict segment equality.
134 |
# File 'lib/sevgi/geometry/segment.rb', line 134 def eql?(other) = self.class == other.class && deconstruct == other.deconstruct |
#hash ⇒ Integer
Returns a hash compatible with strict equality.
138 |
# File 'lib/sevgi/geometry/segment.rb', line 138 def hash = [self.class, *deconstruct].hash |
#line(point = Origin) ⇒ Sevgi::Geometry::Line
Converts the segment into a line at a point.
144 |
# File 'lib/sevgi/geometry/segment.rb', line 144 def line(point = Origin) = Line[length, angle, position: Tuple[Point, point]] |
#reverse ⇒ Sevgi::Geometry::Segment
Returns the opposite segment.
148 |
# File 'lib/sevgi/geometry/segment.rb', line 148 def reverse = with(angle: angle + 180.0) |
#x ⇒ Float
Returns the x component of the segment.
152 |
# File 'lib/sevgi/geometry/segment.rb', line 152 def x = length * F.cos(angle) |
#y ⇒ Float
Returns the y component of the segment.
156 |
# File 'lib/sevgi/geometry/segment.rb', line 156 def y = length * F.sin(angle) |