Get position of color in gradient?

I have a ColorSequence which has multiple different colors in it. I would like to make a function which would take 2 arguments, the first being a color3 and the second being a list of ColorSequenceKeyPoints from the ColorSequence.

function getPositionOfColorInGradient(color, keypoints)
    --Do stuff
end

The function would return a number between 0 and 1. For example, if I called the function like this:

--myColorSequence is between yellow and red. At some point in between yellow and red there is orange. This function should return the point of where the orange is. (Or whatever color I specify).
getPositionOfColorInGradient(Color3.fromRGB(255, 165, 0), myColorSequence.Color.Keypoints)

It should return something near 0.5. How could I do this?

TLDR: I want to make a function that returns the position of a color in a ColorSequence as a decimal.

1 Like

This might not be exact, but it’s pretty close.

If it’s not exact enough, tweak the 0.001 to be more/less lenient.
A higher value means it’s more lenient, a lesser value means it has to be more exact.

If a color can’t be found this function returns -1, but you can also change that as you might need.

function checkColorSimilarity(colorA, colorB)
	local r, g, b = colorA.R - colorB.R, colorA.G - colorB.G, colorA.B - colorB.B;
	
	return math.sqrt(r^2 + g^2 + b^2)
end

function findFirstColor(color, keypoints)
	for i = 1, #keypoints - 1 do
		local keypointA = keypoints[i];
		local keypointB = keypoints[i + 1];
		
		for i = keypointA.Time, keypointB.Time, 0.01 do
			local currentColor = keypointA.Value:Lerp(keypointB.Value, (i / keypointB.Time) - keypointA.Time);
			if checkColorSimilarity(color, currentColor) <= 0.001 then
				return i;
			end
		end
	end
	
	return -1;
end

Thank you so much for the response! I tried this with a color that is definitely in the list of keypoints, but it still returns -1. Any idea what’s wrong? (I also tried tweaking 0.001)

What colors did you use? Also what values instead of 0.001 did you try?

I used Color3.fromRGB(0, 255, 0) which was one of the keypoints in my ColorSequence. I tried 0.01 and 0.1.

Sorry for the late reply, been racking my brain for a while trying to solve this. I’m not sure if this is a foolproof solution, but I did rewrite the checkColorSimilarity function to compare the hue values between colors.

function checkColorSimilarity(colorA, colorB)
	local colorAHue = colorA:ToHSV();
	local colorBHue = colorB:ToHSV();
	
	return math.abs(colorAHue - colorBHue);
end

function findFirstColor(color, keypoints)
	for i = 1, #keypoints - 1 do
		local keypointA = keypoints[i];
		local keypointB = keypoints[i + 1];
		
		for i = keypointA.Time, keypointB.Time, 0.01 do
			local lerpAlpha = (i / keypointB.Time) - keypointA.Time;
			local currentColor = keypointA.Value:Lerp(keypointB.Value, lerpAlpha);
			local similarity = checkColorSimilarity(color, currentColor);
			
			if similarity <= 0.005 then
				return i;
			end
		end
	end
	
	return -1;
end

This obviously doesn’t account for saturation, but if that’s a concern I can also write a function which does. For now, test it and see how well it works.

IMO, the best way to do this is to format all the colors to HSV format and after calculate alpha between 2 points. Here’s an example:

local function get_time_on_color_gradient(chosen_color : Color3, grad_color : ColorSequence) : number
     local start_t : ColorSequenceKeyPoint = grad_color[0];
     local end_t : ColorSequenceKeyPoint = grad_color[1];
    
     local start_color : Color3 = start_t.Value:ToHSV();
     local end_color : Color3 = end_t.Value:ToHSV();
     local now_color : Color3 = chosen_color:ToHSV();

     local alpha : number = (now_color.r - start_color.r) / (end_color.r - start_color.r);
     return math.clamp(alpha, 0, 1);
end

I haven’t tested the code, so it might not work.