131 lines
No EOL
3.7 KiB
Ruby
131 lines
No EOL
3.7 KiB
Ruby
require 'chroma'
|
|
|
|
module TransitionHelpers
|
|
module Defaults
|
|
PERIOD = 500
|
|
NUM_PERIODS = 20
|
|
DURATION = PERIOD * NUM_PERIODS
|
|
end
|
|
|
|
def highlight_value(a, highlight_ix)
|
|
str = a
|
|
.each_with_index
|
|
.map do |x, i|
|
|
i == highlight_ix ? ">>#{x}<<" : x
|
|
end
|
|
.join(', ')
|
|
"[#{str}]"
|
|
end
|
|
|
|
def color_transitions_are_equal(expected:, seen:)
|
|
%i(hue saturation).each do |label|
|
|
e = expected.map { |x| x[label] }
|
|
s = seen.map { |x| x[label] }
|
|
|
|
transitions_are_equal(expected: e, seen: s, label: label, allowed_variation: label == :saturation ? 5 : 20)
|
|
end
|
|
end
|
|
|
|
def transitions_are_equal(expected:, seen:, allowed_variation: 0, label: nil)
|
|
generate_msg = ->(a, b, i) do
|
|
s = "Transition step value"
|
|
|
|
if !label.nil?
|
|
s << " for #{label} "
|
|
end
|
|
|
|
s << "at index #{i} "
|
|
|
|
s << if allowed_variation == 0
|
|
"should be equal to expected value. Expected: #{a}, saw: #{b}."
|
|
else
|
|
"should be within #{allowed_variation} of expected value. Expected: #{a}, saw: #{b}."
|
|
end
|
|
|
|
s << " Steps:\n"
|
|
s << " Expected : #{highlight_value(expected, i)},\n"
|
|
s << " Seen : #{highlight_value(seen, i)}"
|
|
end
|
|
|
|
expect(expected.length).to eq(seen.length),
|
|
"Transition was a different length than expected.\n" <<
|
|
" Expected : #{expected}\n" <<
|
|
" Seen : #{seen}"
|
|
|
|
expected.zip(seen).each_with_index do |x, i|
|
|
a, b = x
|
|
diff = (a - b).abs
|
|
expect(diff).to be <= allowed_variation, generate_msg.call(a, b, i)
|
|
end
|
|
end
|
|
|
|
def rgb_to_hs(*color)
|
|
if color.length > 1
|
|
r, g, b = color
|
|
else
|
|
r, g, b = coerce_color(color.first)
|
|
end
|
|
|
|
hsv = Chroma::Converters::HsvConverter.convert_rgb(Chroma::ColorModes::Rgb.new(r, g, b))
|
|
{ hue: hsv.h.round, saturation: (100*hsv.s).round }
|
|
end
|
|
|
|
def coerce_color(c)
|
|
c.split(',').map(&:to_i) unless c.is_a?(Array)
|
|
end
|
|
|
|
def calculate_color_transition_steps(start_color:, end_color:, duration: nil, period: nil, num_periods: Defaults::NUM_PERIODS)
|
|
start_color = coerce_color(start_color)
|
|
end_color = coerce_color(end_color)
|
|
|
|
part_transitions = start_color.zip(end_color).map do |c|
|
|
s, e = c
|
|
calculate_transition_steps(start_value: s, end_value: e, duration: duration, period: period, num_periods: num_periods)
|
|
end
|
|
|
|
# If some colors don't transition, they'll stay at the same value while others move.
|
|
# Turn this: [[1,2,3], [0], [4,5,6]]
|
|
# Into this: [[1,2,3], [0,0,0], [4,5,6]]
|
|
longest = part_transitions.max_by { |x| x.length }.length
|
|
part_transitions.map! { |x| x + [x.last]*(longest-x.length) }
|
|
|
|
# Zip individual parts into 3-tuples
|
|
# Turn this: [[1,2,3], [0,0,0], [4,5,6]]
|
|
# Into this: [[1,0,4], [2,0,5], [3,0,6]]
|
|
transition_colors = part_transitions.first.zip(*part_transitions[1..part_transitions.length])
|
|
|
|
# Undergo the RGB -> HSV w/ value = 100
|
|
transition_colors.map do |x|
|
|
r, g, b = x
|
|
rgb_to_hs(r, g, b)
|
|
end
|
|
end
|
|
|
|
def calculate_transition_steps(start_value:, end_value:, duration: nil, period: nil, num_periods: Defaults::NUM_PERIODS)
|
|
if !duration.nil? || !period.nil?
|
|
period ||= Defaults::PERIOD
|
|
duration ||= Defaults::DURATION
|
|
num_periods = [1, (duration / period.to_f).ceil].max
|
|
end
|
|
|
|
diff = end_value - start_value
|
|
step_size = [1, (diff.abs / num_periods.to_f).ceil].max
|
|
step_size = -step_size if end_value < start_value
|
|
|
|
steps = []
|
|
val = start_value
|
|
|
|
while val != end_value
|
|
steps << val
|
|
|
|
if (end_value - val).abs < step_size.abs
|
|
val += (end_value - val)
|
|
else
|
|
val += step_size
|
|
end
|
|
end
|
|
|
|
steps << end_value
|
|
steps
|
|
end
|
|
end |