cura 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +15 -0
  3. data/Gemfile.lock +122 -0
  4. data/LICENSE +20 -0
  5. data/README.md +159 -0
  6. data/Rakefile +42 -0
  7. data/cura.gemspec +23 -0
  8. data/examples/box_model/bin/box_model +11 -0
  9. data/examples/box_model/debug.log +0 -0
  10. data/examples/box_model/lib/box_model.rb +21 -0
  11. data/examples/box_model/lib/box_model/application.rb +24 -0
  12. data/examples/hello_world/bin/hello_world +10 -0
  13. data/examples/hello_world/lib/hello_world.rb +201 -0
  14. data/examples/mruby-examples/README.md +10 -0
  15. data/examples/mruby-examples/include/cura_examples.h +6 -0
  16. data/examples/mruby-examples/mrbgem.rake +14 -0
  17. data/examples/mruby-examples/src/gem_init.c +34 -0
  18. data/examples/mruby-examples/tools/hello_world/hello_world.c +6 -0
  19. data/examples/todo_list/app.log +9 -0
  20. data/examples/todo_list/bin/todo_list +26 -0
  21. data/examples/todo_list/data.db +0 -0
  22. data/examples/todo_list/debug.log +0 -0
  23. data/examples/todo_list/lib/todo_list.rb +28 -0
  24. data/examples/todo_list/lib/todo_list/application.rb +54 -0
  25. data/examples/todo_list/lib/todo_list/component/header.rb +27 -0
  26. data/examples/todo_list/lib/todo_list/component/list.rb +50 -0
  27. data/examples/todo_list/lib/todo_list/component/list_item.rb +28 -0
  28. data/examples/todo_list/lib/todo_list/component/list_items.rb +97 -0
  29. data/examples/todo_list/lib/todo_list/component/lists.rb +89 -0
  30. data/examples/todo_list/lib/todo_list/database.rb +50 -0
  31. data/examples/todo_list/lib/todo_list/model/list.rb +9 -0
  32. data/examples/todo_list/lib/todo_list/model/list_item.rb +9 -0
  33. data/examples/todo_list/profile.html +11354 -0
  34. data/lib/cura.rb +13 -0
  35. data/lib/cura/adapter.rb +67 -0
  36. data/lib/cura/application.rb +245 -0
  37. data/lib/cura/attributes/has_ancestry.rb +40 -0
  38. data/lib/cura/attributes/has_application.rb +31 -0
  39. data/lib/cura/attributes/has_attributes.rb +89 -0
  40. data/lib/cura/attributes/has_children.rb +113 -0
  41. data/lib/cura/attributes/has_colors.rb +66 -0
  42. data/lib/cura/attributes/has_coordinates.rb +49 -0
  43. data/lib/cura/attributes/has_dimensions.rb +63 -0
  44. data/lib/cura/attributes/has_events.rb +89 -0
  45. data/lib/cura/attributes/has_focusability.rb +37 -0
  46. data/lib/cura/attributes/has_initialize.rb +15 -0
  47. data/lib/cura/attributes/has_offsets.rb +82 -0
  48. data/lib/cura/attributes/has_orientation.rb +53 -0
  49. data/lib/cura/attributes/has_relative_coordinates.rb +39 -0
  50. data/lib/cura/attributes/has_root.rb +104 -0
  51. data/lib/cura/attributes/has_side_attributes.rb +95 -0
  52. data/lib/cura/attributes/has_windows.rb +70 -0
  53. data/lib/cura/borders.rb +16 -0
  54. data/lib/cura/color.rb +330 -0
  55. data/lib/cura/component/base.rb +180 -0
  56. data/lib/cura/component/button.rb +57 -0
  57. data/lib/cura/component/group.rb +77 -0
  58. data/lib/cura/component/label.rb +224 -0
  59. data/lib/cura/component/listbox.rb +152 -0
  60. data/lib/cura/component/pack.rb +144 -0
  61. data/lib/cura/component/scrollbar.rb +184 -0
  62. data/lib/cura/component/textbox.rb +118 -0
  63. data/lib/cura/cursor.rb +77 -0
  64. data/lib/cura/error/base.rb +10 -0
  65. data/lib/cura/error/invalid_adapter.rb +18 -0
  66. data/lib/cura/error/invalid_application.rb +18 -0
  67. data/lib/cura/error/invalid_color.rb +18 -0
  68. data/lib/cura/error/invalid_component.rb +18 -0
  69. data/lib/cura/error/invalid_middleware.rb +18 -0
  70. data/lib/cura/event.rb +38 -0
  71. data/lib/cura/event/base.rb +108 -0
  72. data/lib/cura/event/click.rb +14 -0
  73. data/lib/cura/event/dispatcher.rb +122 -0
  74. data/lib/cura/event/focus.rb +14 -0
  75. data/lib/cura/event/handler.rb +74 -0
  76. data/lib/cura/event/key_down.rb +68 -0
  77. data/lib/cura/event/middleware/aimer/base.rb +38 -0
  78. data/lib/cura/event/middleware/aimer/dispatcher_target.rb +24 -0
  79. data/lib/cura/event/middleware/aimer/mouse_focus.rb +48 -0
  80. data/lib/cura/event/middleware/aimer/target_option.rb +38 -0
  81. data/lib/cura/event/middleware/base.rb +21 -0
  82. data/lib/cura/event/middleware/dispatch.rb +20 -0
  83. data/lib/cura/event/middleware/translator/base.rb +37 -0
  84. data/lib/cura/event/middleware/translator/mouse_click.rb +44 -0
  85. data/lib/cura/event/mouse.rb +34 -0
  86. data/lib/cura/event/mouse_button.rb +103 -0
  87. data/lib/cura/event/mouse_wheel_down.rb +14 -0
  88. data/lib/cura/event/mouse_wheel_up.rb +14 -0
  89. data/lib/cura/event/resize.rb +17 -0
  90. data/lib/cura/event/selected.rb +14 -0
  91. data/lib/cura/event/unfocus.rb +14 -0
  92. data/lib/cura/focus_controller.rb +89 -0
  93. data/lib/cura/key.rb +313 -0
  94. data/lib/cura/margins.rb +16 -0
  95. data/lib/cura/offsets.rb +91 -0
  96. data/lib/cura/padding.rb +16 -0
  97. data/lib/cura/pencil.rb +29 -0
  98. data/lib/cura/version.rb +6 -0
  99. data/lib/cura/window.rb +85 -0
  100. data/spec/cura/attributes/has_ancestry_spec.rb +108 -0
  101. data/spec/cura/attributes/has_application_spec.rb +59 -0
  102. data/spec/cura/attributes/has_attributes_spec.rb +75 -0
  103. data/spec/cura/attributes/has_children_spec.rb +169 -0
  104. data/spec/cura/attributes/has_colors_spec.rb +20 -0
  105. data/spec/cura/attributes/has_coordinates_spec.rb +19 -0
  106. data/spec/cura/attributes/has_dimensions_spec.rb +19 -0
  107. data/spec/cura/attributes/has_events_spec.rb +18 -0
  108. data/spec/cura/attributes/has_focusability_spec.rb +58 -0
  109. data/spec/cura/attributes/has_offsets_spec.rb +18 -0
  110. data/spec/cura/attributes/has_orientation_spec.rb +102 -0
  111. data/spec/cura/attributes/has_relative_coordinates_spec.rb +18 -0
  112. data/spec/cura/attributes/has_side_attributes_spec.rb +19 -0
  113. data/spec/spec_helper.rb +12 -0
  114. data/spec/support/shared_examples_for_attributes.rb +122 -0
  115. metadata +211 -0
@@ -0,0 +1,53 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/attributes/has_attributes"
3
+ end
4
+
5
+ module Cura
6
+ module Attributes
7
+
8
+ # Adds the `orientation` attribute to objects, which can be :vertical or :horizontal.
9
+ module HasOrientation
10
+
11
+ include HasAttributes
12
+
13
+ def initialize(attributes={})
14
+ @orientation = :vertical unless instance_variable_defined?(:@orientation)
15
+
16
+ super
17
+ end
18
+
19
+ # Get the orientation of this object.
20
+ #
21
+ # @return [Symbol]
22
+ attr_reader :orientation
23
+
24
+ # Set the orientation of this object.
25
+ # Must be :vertical or :horizontal.
26
+ #
27
+ # @param [#to_sym] value
28
+ # @return [Symbol]
29
+ def orientation=(value)
30
+ value = value.to_sym
31
+ raise ArgumentError, "orientation must be one of :vertical or :horizontal" unless [:vertical, :horizontal].include?(value)
32
+
33
+ @orientation = value
34
+ end
35
+
36
+ # Check if this object's orientation is set to :horizontal.
37
+ #
38
+ # @return [Boolean]
39
+ def horizontal?
40
+ orientation == :horizontal
41
+ end
42
+
43
+ # Check if this object's orientation is set to :vertical.
44
+ #
45
+ # @return [Boolean]
46
+ def vertical?
47
+ orientation == :vertical
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,39 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/attributes/has_ancestry"
3
+ require "cura/attributes/has_coordinates"
4
+ end
5
+
6
+ module Cura
7
+ module Attributes
8
+
9
+ # Adds the `absolute_x` and `absolute_y` attributes, which are relative to it's parent.
10
+ module HasRelativeCoordinates
11
+
12
+ include HasAncestry
13
+ include HasCoordinates
14
+
15
+ def initialize(attributes={})
16
+ @absolute_x = 0
17
+ @absolute_y = 0
18
+
19
+ super
20
+ end
21
+
22
+ # Get the absolute X coordinate of this object.
23
+ #
24
+ # @return [Integer]
25
+ def absolute_x
26
+ parent? && parent.respond_to?(:absolute_x) ? @x + parent.offsets.left + parent.absolute_x : @x
27
+ end
28
+
29
+ # Get the absolute Y coordinate of this object.
30
+ #
31
+ # @return [Integer]
32
+ def absolute_y
33
+ parent? && parent.respond_to?(:absolute_y) ? @y + parent.offsets.top + parent.absolute_y : @y
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,104 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/attributes/has_attributes"
3
+
4
+ require "cura/component/group"
5
+ end
6
+
7
+ module Cura
8
+ module Attributes
9
+
10
+ # Adds the `root` attribute to an object, which defaults to a Component::Group.
11
+ module HasRoot
12
+
13
+ include Attributes::HasAttributes
14
+
15
+ def initialize(attributes={})
16
+ @root = Component::Group.new(parent: self)
17
+
18
+ super
19
+ end
20
+
21
+ # @method root
22
+ # Get root component for this object.
23
+ #
24
+ # @return [Component::Group]
25
+
26
+ # @method root=(component)
27
+ # Set root component for this object.
28
+ #
29
+ # @param [Component::Group] component
30
+ # @return [Component::Group]
31
+
32
+ attribute(:root) { |component| set_root(component) }
33
+
34
+ # Delegates -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
35
+
36
+ # Get the children of this object.
37
+ #
38
+ # @return [<Component>]
39
+ def children(recursive=false)
40
+ @root.children(recursive)
41
+ end
42
+
43
+ # Add a child to this object's root component.
44
+ #
45
+ # @param [Component] component
46
+ # @return [Component]
47
+ def add_child(component)
48
+ @root.add_child(component)
49
+ end
50
+
51
+ # Add multiple children to this object's root component.
52
+ #
53
+ # @param [<Component>] children
54
+ # @return [<Component>]
55
+ def add_children(*children)
56
+ @root.add_children(*children)
57
+ end
58
+
59
+ # Remove a child from object's root component at the given index.
60
+ #
61
+ # @param [Integer] index
62
+ # @return [Component]
63
+ def delete_child_at(index)
64
+ @root.delete_child_at(index)
65
+ end
66
+
67
+ # Remove a child from this object's root component.
68
+ #
69
+ # @param [Component] component
70
+ # @return [Component]
71
+ def delete_child(component)
72
+ @root.delete_child(component)
73
+ end
74
+
75
+ # Remove all children from object's root component.
76
+ #
77
+ # @return [HasChildren]
78
+ def delete_children
79
+ @root.delete_children
80
+ end
81
+
82
+ # Determine if this object's root component has children.
83
+ #
84
+ # @return [Boolean]
85
+ def children?
86
+ @root.children?
87
+ end
88
+
89
+ protected
90
+
91
+ def set_root(component)
92
+ raise TypeError, "root must be a Component::Group" unless component.is_a?(Component::Group)
93
+
94
+ @root.parent = nil unless @root.nil?
95
+ @root = component
96
+ @root.parent = self
97
+
98
+ @root
99
+ end
100
+
101
+ end
102
+
103
+ end
104
+ end
@@ -0,0 +1,95 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/attributes/has_attributes"
3
+ end
4
+
5
+ module Cura
6
+ module Attributes
7
+
8
+ # Adds the `top`, `right`, `bottom`, `left`, `width`, and `height` attributes to objects.
9
+ module HasSideAttributes
10
+
11
+ include HasAttributes
12
+
13
+ # @method top
14
+ # Get the top attribute.
15
+ #
16
+ # @return [Integer]
17
+
18
+ # @method top=(value)
19
+ # Set the top attribute.
20
+ #
21
+ # @param [#to_i] value
22
+ # @return [Integer]
23
+
24
+ attribute(:top) { |value| validate_size_attribute(value) }
25
+
26
+ # @method right
27
+ # Get the right attribute.
28
+ #
29
+ # @return [Integer]
30
+
31
+ # @method right=(value)
32
+ # Set the right attribute.
33
+ #
34
+ # @param [#to_i] value
35
+ # @return [Integer]
36
+
37
+ attribute(:right) { |value| validate_size_attribute(value) }
38
+
39
+ # @method bottom
40
+ # Get the bottom attribute.
41
+ #
42
+ # @return [Integer]
43
+
44
+ # @method bottom=(value)
45
+ # Set the bottom attribute.
46
+ #
47
+ # @param [#to_i] value
48
+ # @return [Integer]
49
+
50
+ attribute(:bottom) { |value| validate_size_attribute(value) }
51
+
52
+ # @method left
53
+ # Get the left attribute.
54
+ #
55
+ # @return [Integer]
56
+
57
+ # @method left=(value)
58
+ # Set the left attribute.
59
+ #
60
+ # @param [#to_i] value
61
+ # @return [Integer]
62
+
63
+ attribute(:left) { |value| validate_size_attribute(value) }
64
+
65
+ def initialize(attributes={})
66
+ @top = 0 unless instance_variable_defined?(:@top)
67
+ @right = 0 unless instance_variable_defined?(:@right)
68
+ @bottom = 0 unless instance_variable_defined?(:@bottom)
69
+ @left = 0 unless instance_variable_defined?(:@left)
70
+
71
+ unless attributes.respond_to?(:to_hash) || attributes.respond_to?(:to_h)
72
+ attributes = { top: attributes, right: attributes, bottom: attributes, left: attributes } # Set all side attributes to the argument given
73
+ end
74
+
75
+ super
76
+ end
77
+
78
+ # Get the total height of the attributes.
79
+ #
80
+ # @return [Integer]
81
+ def height
82
+ @top + @bottom
83
+ end
84
+
85
+ # Get the total width of the attributes.
86
+ #
87
+ # @return [Integer]
88
+ def width
89
+ @left + @right
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,70 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/window"
3
+ end
4
+
5
+ module Cura
6
+ module Attributes
7
+
8
+ # Allows an object to have windows.
9
+ # TODO: Lots of code is the same as HasChildren
10
+ module HasWindows
11
+
12
+ def initialize(*arguments)
13
+ @windows = []
14
+
15
+ super
16
+ end
17
+
18
+ # Get the windows of this object.
19
+ attr_reader :windows
20
+
21
+ # Add a window to this object.
22
+ #
23
+ # @param [Window] window
24
+ # @return [Window]
25
+ def add_window(window)
26
+ raise TypeError, "window must be a Cura::Window" unless window.is_a?(Window)
27
+
28
+ @windows << window
29
+
30
+ window
31
+ end
32
+
33
+ # Remove a window from this object's windows at the given index.
34
+ #
35
+ # @param [#to_i] index
36
+ # @return [Window]
37
+ def delete_window_at(index)
38
+ @windows.delete_at(index.to_i)
39
+ end
40
+
41
+ # Remove a window from this object's windows.
42
+ #
43
+ # @param [Window] window
44
+ # @return [Window]
45
+ def delete_window(window)
46
+ raise TypeError, "window must be a Cura::Window" unless window.is_a?(Window)
47
+ index = @windows.index(window)
48
+
49
+ delete_window_at(index)
50
+ end
51
+
52
+ # Remove all windows.
53
+ def delete_windows
54
+ (0...@windows.count).to_a.each { |index| delete_window_at(index) }
55
+ end
56
+
57
+ protected # TODO: These should be protected?
58
+
59
+ def update_windows
60
+ windows.each(&:update)
61
+ end
62
+
63
+ def draw_windows
64
+ windows.each(&:draw)
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,16 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/attributes/has_initialize"
3
+ require "cura/attributes/has_side_attributes"
4
+ end
5
+
6
+ module Cura
7
+
8
+ # The border side attributes of a component.
9
+ class Borders
10
+
11
+ include Attributes::HasInitialize
12
+ include Attributes::HasSideAttributes
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,330 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/attributes/has_initialize"
3
+ require "cura/attributes/has_attributes"
4
+ end
5
+
6
+ module Cura
7
+
8
+ # Colors.
9
+ class Color
10
+
11
+ include Attributes::HasInitialize
12
+ include Attributes::HasAttributes
13
+
14
+ class << self
15
+
16
+ # The default color to be overidden by adapters.
17
+ # Usually, for TUI's to use the terminal theme's colors.
18
+ # TODO: Remove.
19
+ def default
20
+ super
21
+ end
22
+
23
+ def black
24
+ new
25
+ end
26
+
27
+ def white
28
+ new(255, 255, 255)
29
+ end
30
+
31
+ def red
32
+ new(255, 0, 0)
33
+ end
34
+
35
+ def green
36
+ new(0, 255, 0)
37
+ end
38
+
39
+ def blue
40
+ new(0, 0, 255)
41
+ end
42
+
43
+ end
44
+
45
+ def initialize(r=0, g=0, b=0, a=255)
46
+ if r.respond_to?(:to_h)
47
+ super(r.to_h)
48
+ else
49
+ @red = r
50
+ @green = g
51
+ @blue = b
52
+ @alpha = a
53
+ end
54
+
55
+ @lab = rgb_to_lab([@red, @green, @blue]) # TODO: Update on rgb setters?
56
+ end
57
+
58
+ # @method red
59
+ # Get the red channel of this color.
60
+ #
61
+ # @return [Integer]
62
+
63
+ # @method red=(value)
64
+ # Set the red channel of this color.
65
+ #
66
+ # @param [#to_i] value
67
+ # @return [Integer]
68
+
69
+ # @method green
70
+ # Get the green channel of this color.
71
+ #
72
+ # @return [Integer]
73
+
74
+ # @method green=(value)
75
+ # Set the green channel of this color.
76
+ #
77
+ # @param [#to_i] value
78
+ # @return [Integer]
79
+
80
+ # @method blue=(value)
81
+ # Get the blue channel of this color.
82
+ #
83
+ # @return [Integer]
84
+
85
+ # @method blue=(value)
86
+ # Set the blue channel of this color.
87
+ #
88
+ # @param [#to_i] value
89
+ # @return [Integer]
90
+
91
+ # @method alpha
92
+ # Get the alpha channel of this color.
93
+ #
94
+ # @return [Integer]
95
+
96
+ # @method alpha=(value)
97
+ # Set the alpha channel of this color.
98
+ #
99
+ # @param [#to_i] value
100
+ # @return [Integer]
101
+
102
+ [:red, :green, :blue, :alpha].each do |channel|
103
+ attribute(channel) { |value| convert_and_constrain_value(value) }
104
+ end
105
+
106
+ attr_reader :lab
107
+
108
+ def -(other)
109
+ delta_e_2000(@lab, other.lab)
110
+ end
111
+
112
+ def <=>(other)
113
+ self.hsl[0] <=> other.hsl[0]
114
+ end
115
+
116
+ # Determing if this color is equivalent to another object.
117
+ #
118
+ # @param [Object] other
119
+ # @return [Boolean]
120
+ def ==(other)
121
+ other.is_a?(Color) ? matches_color?(other) : super
122
+ end
123
+
124
+ def hsl
125
+ @hsl ||= rgb_to_hsl(@rgb)
126
+ end
127
+
128
+ def yiq
129
+ @yiq ||= rgb_to_yiq(@rgb)
130
+ end
131
+
132
+ def to_a
133
+ [@red, @green, @blue, @alpha]
134
+ end
135
+
136
+ def hex
137
+ to_a.each_with_object("") { |part, memo| memo << "%02x" % part }
138
+ end
139
+
140
+ protected
141
+
142
+ def matches_color?(other)
143
+ @alpha == other.alpha && @red == other.red && @green == other.green && @blue && other.blue
144
+ end
145
+
146
+ # Convert the input to an Integer and constrain in or between 0 and 255.
147
+ def convert_and_constrain_value(value)
148
+ value = value.to_i
149
+
150
+ [255, [0, value].max].min
151
+ end
152
+
153
+ # source: http://www.easyrgb.com/index.php?X=MATH&H=02#text2
154
+ def rgb_to_xyz(color)
155
+ r, g, b = color.map do |v|
156
+ v /= 255.0
157
+ if v > 0.04045
158
+ v = ((v + 0.055 ) / 1.055)**2.4
159
+ else
160
+ v = v / 12.92
161
+ end
162
+ v *= 100
163
+ end
164
+
165
+ #Observer = 2°, Illuminant = D65
166
+ x = r * 0.4124 + g * 0.3576 + b * 0.1805
167
+ y = r * 0.2126 + g * 0.7152 + b * 0.0722
168
+ z = r * 0.0193 + g * 0.1192 + b * 0.9505
169
+
170
+ return [x, y, z]
171
+ end
172
+
173
+ def xyz_to_lab(color)
174
+ f = lambda { |t|
175
+ return t**(1.0/3) if t > (6.0 / 29)**3
176
+ return (1.0 / 3) * ((29.0 / 6)**2) * t + (4.0 / 29)
177
+ }
178
+
179
+ x, y, z = color
180
+ xn, yn, zn = rgb_to_xyz([255, 255, 255])
181
+ l = 116 * f.call(y/yn) - 16
182
+ a = 500 * (f.call(x/xn) - f.call(y/yn))
183
+ b = 200 * (f.call(y/yn) - f.call(z/zn))
184
+
185
+ return [l, a, b]
186
+ end
187
+
188
+ def rgb_to_lab(rgb_val)
189
+ xyz_to_lab rgb_to_xyz rgb_val
190
+ end
191
+
192
+ def rgb_to_hsl(rgb_val)
193
+ r, g, b = rgb_val.map { |v| v / 255.0 }
194
+
195
+ min, max = [r, g, b].minmax
196
+ delta = max - min
197
+
198
+ lig = (max + min) / 2.0
199
+
200
+ if delta == 0
201
+ hue = 0
202
+ sat = 0
203
+ else
204
+ sat = if lig < 0.5
205
+ delta / (0.0 + (max + min))
206
+ else
207
+ delta / (2.0 - max - min)
208
+ end
209
+
210
+ delta_r = (((max - r) / 6.0 ) + (delta / 2.0)) / delta
211
+ delta_g = (((max - g) / 6.0 ) + (delta / 2.0)) / delta
212
+ delta_b = (((max - b) / 6.0 ) + (delta / 2.0)) / delta
213
+
214
+ hue = case max
215
+ when r then delta_b - delta_g
216
+ when g then (1.0/3) + delta_r - delta_b
217
+ when b then (2.0/3) + delta_g - delta_r
218
+ end
219
+
220
+ hue += 1 if hue < 0
221
+ hue -= 1 if hue > 1
222
+ end
223
+
224
+ [360 * hue, 100 * sat, 100 * lig]
225
+ end
226
+
227
+ def rgb_to_yiq(rgb_val)
228
+ r, g, b = rgb_val
229
+
230
+ y = 0.299*r + 0.587*g + 0.114*b
231
+ i = 0.569*r - 0.275*g - 0.321*b
232
+ q = 0.212*r - 0.523*g + 0.311*b
233
+
234
+ [y, i, q]
235
+ end
236
+
237
+ def rad_to_deg(v)
238
+ (v * 180) / Math::PI
239
+ end
240
+
241
+ def deg_to_rad(v)
242
+ (v * Math::PI) / 180
243
+ end
244
+
245
+ def lab_to_hue(a, b)
246
+ bias = 0
247
+ return 0 if (a >= 0 && b == 0)
248
+ return 180 if (a < 0 && b == 0)
249
+ return 90 if (a == 0 && b > 0)
250
+ return 270 if (a == 0 && b < 0)
251
+
252
+ bias = case
253
+ when a > 0 && b > 0 then 0
254
+ when a < 0 then 180
255
+ when a > 0 && b < 0 then 360
256
+ end
257
+
258
+ rad_to_deg(Math.atan(b / a)) + bias
259
+ end
260
+
261
+ def delta_e_2000(lab1, lab2)
262
+ l1, a1, b1 = lab1
263
+ l2, a2, b2 = lab2
264
+ kl, kc, kh = [1, 1, 1]
265
+
266
+ xC1 = Math.sqrt(a1**2 + b1**2)
267
+ xC2 = Math.sqrt(a2**2 + b2**2)
268
+ xCX = (xC1 + xC2) / 2
269
+ xGX = 0.5 * (1 - Math.sqrt(xCX**7 / (xCX**7 + 25**7)))
270
+ xNN = (1 + xGX) * a1
271
+ xC1 = Math.sqrt(xNN**2 + b1**2)
272
+ xH1 = lab_to_hue(xNN, b1)
273
+ xNN = (1 + xGX) * a2
274
+ xC2 = Math.sqrt(xNN**2 + b2**2)
275
+ xH2 = lab_to_hue(xNN, b2)
276
+ xDL = l2 - l1
277
+ xDC = xC2 - xC1
278
+ if (xC1 * xC2) == 0
279
+ xDH = 0
280
+ else
281
+ xNN = (xH2 - xH1).round(12)
282
+ if xNN.abs <= 180
283
+ xDH = xH2 - xH1
284
+ else
285
+ if xNN > 180
286
+ xDH = xH2 - xH1 - 360
287
+ else
288
+ xDH = xH2 - xH1 + 360
289
+ end
290
+ end
291
+ end
292
+ xDH = 2 * Math.sqrt(xC1 * xC2) * Math.sin(deg_to_rad(xDH / 2.0))
293
+ xLX = (l1 + l2) / 2.0
294
+ xCY = (xC1 + xC2) / 2.0
295
+ if xC1 * xC2 == 0
296
+ xHX = xH1 + xH2
297
+ else
298
+ xNN = (xH1 - xH2).round(12).abs
299
+ if xNN > 180
300
+ if xH2 + xH1 < 360
301
+ xHX = xH1 + xH2 + 360
302
+ else
303
+ xHX = xH1 + xH2 - 360
304
+ end
305
+ else
306
+ xHX = xH1 + xH2
307
+ end
308
+ xHX /= 2.0
309
+ end
310
+ xTX = 1 - 0.17 * Math.cos(deg_to_rad(xHX - 30)) + 0.24 *
311
+ Math.cos(deg_to_rad(2 * xHX)) + 0.32 *
312
+ Math.cos(deg_to_rad(3 * xHX + 6)) - 0.20 *
313
+ Math.cos(deg_to_rad(4 * xHX - 63 ))
314
+ xPH = 30 * Math.exp(-((xHX - 275) / 25.0) * ((xHX - 275) / 25.0))
315
+ xRC = 2 * Math.sqrt(xCY**7 / (xCY**7 + 25**7))
316
+ xSL = 1 + ((0.015 * ((xLX - 50) * (xLX - 50))) /
317
+ Math.sqrt(20 + ((xLX - 50) * (xLX - 50))))
318
+ xSC = 1 + 0.045 * xCY
319
+ xSH = 1 + 0.015 * xCY * xTX
320
+ xRT = -Math.sin(deg_to_rad(2 * xPH)) * xRC
321
+ xDL = xDL / (kl * xSL)
322
+ xDC = xDC / (kc * xSC)
323
+ xDH = xDH / (kh * xSH)
324
+
325
+ Math.sqrt(xDL**2 + xDC**2 + xDH**2 + xRT * xDC * xDH)
326
+ end
327
+
328
+ end
329
+
330
+ end