Class: Unmagic::Color::Units::Degrees

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/unmagic/color/units/degrees.rb,
lib/unmagic/color/units/direction.rb

Overview

Represents an angle in degrees (0-360°).

Supports numeric values, degree strings, and named direction keywords. Values are automatically wrapped to the 0-360 range.

## Supported Formats

  • Numeric: ‘225`, `45.5`

  • Degree strings: ‘“225deg”`, `“45.5deg”`, `“-45deg”`, `“225°”`, `“45.5°”`

  • Named directions: ‘“top”`, `“bottom left”`, `“north”`, `“southwest”`, etc.

## Named Direction Keywords

  • Cardinal: ‘“top”` (0°), `“right”` (90°), `“bottom”` (180°), `“left”` (270°)

  • Diagonal: ‘“top right”` (45°), `“bottom right”` (135°), `“bottom left”` (225°), `“top left”` (315°)

  • Aliases: ‘“north”`, `“south”`, `“east”`, `“west”`, `“northeast”`, etc.

Examples:

Numeric values

Degrees.build(225)           #=> 225.0°
Degrees.build(45.5)          #=> 45.5°
Degrees.build(-45)           #=> 315.0° (wrapped)

Degree strings

Degrees.build("225deg")      #=> 225.0°
Degrees.build("45.5deg")     #=> 45.5°
Degrees.build("225°")        #=> 225.0°

Named directions

Degrees.build("top")         #=> 0.0°
Degrees.build("bottom left") #=> 225.0°
Degrees.build("north")       #=> 0.0° (alias for "top")

Constants

Degrees::TOP                 #=> 0.0°
Degrees::BOTTOM_LEFT         #=> 225.0°

String output

Degrees::TOP.to_s            #=> "top"
Degrees::TOP.to_css          #=> "0.0deg"
Degrees.new(value: 123).to_s #=> "123.0°"

Defined Under Namespace

Classes: Direction, ParseError

Constant Summary collapse

TOP =

Predefined degree constants

new(value: 0, name: "top", aliases: ["north"]).freeze
RIGHT =

Right direction (90°, east)

new(value: 90, name: "right", aliases: ["east"]).freeze
BOTTOM =

Bottom direction (180°, south)

new(value: 180, name: "bottom", aliases: ["south"]).freeze
LEFT =

Left direction (270°, west)

new(value: 270, name: "left", aliases: ["west"]).freeze
TOP_RIGHT =

Top-right diagonal direction (45°, northeast)

new(value: 45, name: "top right", aliases: ["topright", "northeast", "north east"]).freeze
BOTTOM_RIGHT =

Bottom-right diagonal direction (135°, southeast)

new(value: 135, name: "bottom right", aliases: ["bottomright", "southeast", "south east"]).freeze
BOTTOM_LEFT =

Bottom-left diagonal direction (225°, southwest)

new(value: 225, name: "bottom left", aliases: ["bottomleft", "southwest", "south west"]).freeze
TOP_LEFT =

Top-left diagonal direction (315°, northwest)

new(value: 315, name: "top left", aliases: ["topleft", "northwest", "north west"]).freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value:, name: nil, aliases: []) ⇒ Degrees

Create a new Degrees instance.

Parameters:

  • value (Numeric)

    Angle in degrees (wraps to 0-360 range)

  • name (String, nil) (defaults to: nil)

    Optional name for this degree (e.g., “top”, “bottom”)

  • aliases (Array<String>) (defaults to: [])

    Optional aliases for this degree (e.g., [“north”])



151
152
153
154
155
# File 'lib/unmagic/color/units/degrees.rb', line 151

def initialize(value:, name: nil, aliases: [])
  @value = value.to_f % 360
  @name = name
  @aliases = aliases
end

Instance Attribute Details

#aliasesObject (readonly)

Returns the value of attribute aliases.



53
54
55
# File 'lib/unmagic/color/units/degrees.rb', line 53

def aliases
  @aliases
end

#nameObject (readonly)

Returns the value of attribute name.



53
54
55
# File 'lib/unmagic/color/units/degrees.rb', line 53

def name
  @name
end

#valueObject (readonly)

Returns the value of attribute value.



53
54
55
# File 'lib/unmagic/color/units/degrees.rb', line 53

def value
  @value
end

Class Method Details

.allArray<Degrees>

All predefined degree constants

Returns:

  • (Array<Degrees>)

    All constant degree values



59
60
61
# File 'lib/unmagic/color/units/degrees.rb', line 59

def all
  all_by_name.values.uniq
end

.build(input) ⇒ Degrees

Build a Degrees instance from various input formats.

Examples:

From number

Degrees.build(225)

From degree string

Degrees.build("225deg")

From CSS direction

Degrees.build("to left top")

Parameters:

Returns:

  • (Degrees)

    Normalized degrees instance

Raises:



107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/unmagic/color/units/degrees.rb', line 107

def build(input)
  case input
  when Degrees
    input
  when ::Numeric
    new(value: input)
  when ::String
    parse(input)
  else
    raise ParseError, "Expected Numeric, String, or Degrees, got #{input.class}"
  end
end

.find_by_name(search) ⇒ Degrees?

Find a constant by name or alias

Parameters:

  • search (String)

    Name or alias to search for

Returns:

  • (Degrees, nil)

    Matching constant or nil



67
68
69
70
71
72
# File 'lib/unmagic/color/units/degrees.rb', line 67

def find_by_name(search)
  normalized = search.strip.downcase
  all_by_name.fetch(normalized)
rescue KeyError
  nil
end

.parse(input) ⇒ Degrees

Parse a degrees string.

Parameters:

  • input (String)

    The string to parse

Returns:

  • (Degrees)

    Parsed degrees instance

Raises:



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/unmagic/color/units/degrees.rb', line 125

def parse(input)
  raise ParseError, "Input must be a string" unless input.is_a?(::String)

  input = input.strip

  # Try to find a named constant first
  constant = find_by_name(input)
  return constant if constant

  # Remove "deg" or "°" suffix if present
  input = input.sub(/deg\z/i, "").sub(/°\z/, "")

  # Try parsing as number
  if input.match?(/\A-?\d+(?:\.\d+)?\z/)
    return new(value: input.to_f)
  end

  raise ParseError, "Invalid degrees format: #{input.inspect}"
end

Instance Method Details

#<=>(other) ⇒ Integer?

Compare two Degrees instances.

Parameters:

  • other (Degrees, Numeric)

    Value to compare

Returns:

  • (Integer, nil)

    Comparison result



190
191
192
193
194
195
196
197
# File 'lib/unmagic/color/units/degrees.rb', line 190

def <=>(other)
  case other
  when Degrees
    @value <=> other.value
  when ::Numeric
    @value <=> other.to_f
  end
end

#==(other) ⇒ Boolean

Check equality.

Parameters:

  • other (Object)

    Value to compare

Returns:

  • (Boolean)

    true if values are equal



203
204
205
206
207
208
209
210
211
212
# File 'lib/unmagic/color/units/degrees.rb', line 203

def ==(other)
  case other
  when Degrees
    @value == other.value
  when ::Numeric
    @value == other.to_f
  else
    false
  end
end

#oppositeDegrees

Get the opposite direction (180 degrees away).

Returns:



167
168
169
170
# File 'lib/unmagic/color/units/degrees.rb', line 167

def opposite
  opposite_value = (@value + 180) % 360
  self.class.all.find { |d| d.value == opposite_value } || self.class.new(value: opposite_value)
end

#to_cssString

Convert to CSS string format.

Returns:

  • (String)

    CSS degree string (e.g., “225.0deg”)



175
176
177
# File 'lib/unmagic/color/units/degrees.rb', line 175

def to_css
  "#{@value}deg"
end

#to_fFloat

Convert to float value.

Returns:

  • (Float)

    Degrees value (0-360)



160
161
162
# File 'lib/unmagic/color/units/degrees.rb', line 160

def to_f
  @value
end

#to_sString

Convert to string representation.

Returns:

  • (String)

    Canonical string format that can be parsed back



182
183
184
# File 'lib/unmagic/color/units/degrees.rb', line 182

def to_s
  @name || "#{@value}°"
end