It is currently May 8th, 2024, 3:38 pm

raincolors.lua [1.2.18.03.24]

Skins that control functions in Windows or Rainmeter
RicardoTM
Posts: 272
Joined: December 28th, 2022, 9:30 pm
Location: México

raincolors.lua [1.2.18.03.24]

Post by RicardoTM »

[ORIGINAL POST DELETED] for new readers first 3 pages of this post may not make sense.

RAINCOLORS.LUA
A color manipualtion library that includes color theory and harmony functions

So, after some days working in this, finally I feel comfortable to share what is the first version of the (now named) raincolors.lua.

This is my first time doing stuff in lua so there might be things that can be optimized and I would love anyone who points at any error, bug, or possible improvement, also new functions ideas are welcomed. I prepared 5 basic skins as examples.

What if I told you that you can create themes choosing only one color?
ezgif.com-video-to-gif-converter (7).gif
adjustContrast() and complementary() functions in use

Raincolors.lua contains a handful of functions to control how a skin looks, from simple color manipulation functions like shiftHue, shiftSat, and shiftLight to functions that read the contrast between 2 colors and easily return an enhanced color for better readability, others that will generate a quantity of shades, tints and tones from any color and others that will generate colors according to color theory and harmony. While by themselves won't do magic, with your helping hand I'm pretty sure they'll do miracles (Aight, I might be exaggerating.. or not? :)).

Content
  • INTERNAL USE
    These are used internally for the other functions, as they return numbers and not strings is recommended to use the functions on "Conversion' section instead.
  • round(number, ndp)
    round a number to any optional number of decimal places (ndp).
  • hex2rgb('HEX') and rgb2hex('color')
    Converts Hex to RGB and viceversa.
  • rgb2hsl(r, g, b) and hsl2rgb(h, s, l)
    Convert HSL to RGB and viceversa.
  • split('color')
    Splits rgb string into r,g,b
  • rgbORhex('color')
    Returns RGB if the color is RGB and HEX if it is HEX.
  • brightness('color')
    Returns color's relative luminance value.
  • contrastRatio('color1','color2')
    Returns the contrast ratio between 2 colors.


    CONVERSION
  • RGB2HSL('color')
    Converts RGB or HEX to HSL
  • HSL2RGB('HSL')
    Converts HSL to RGB
  • RGB2HEX('color')
    Converts RGB to HEX.
  • HEX2RGB('HEX')
    Converts HEX to RGB.

    SPLITTING
    These functions return a single value.
  • h('color')
  • s('color')
  • l('color')
  • red('color')
  • green('color')
  • blue('color')

    MANIPULATION
  • setHue('color', newHue)
    Sets Hue to new Hue
  • setSat('color', newSat)
    Sets Saturation to new Saturation
  • setLight('color', newLight)
    Sets Lightness to new Lightness
  • shiftHue('color', angle)
    Shifts Hue by +- a given angle, or 60.
  • shiftSat('color',amount)
    Shifts Saturation by +- a given amount, or 30.
  • shiftLight('color',amount)
    Shifts Lightness by +- a given amount, or 30.
  • blackORwhite('color')
    Returns black or white depending on the color's brightness.
  • adjustContrast('color1', 'color2', desiredContrast)
    Shifts color2's lightness based on color1 to a desired contrast between them.
  • invert('color')
    Inverts a color (not "true" complementary).
  • neighborR('color')
    Shifts color's hue 30 degrees.
  • neighborL('color')
    Shifts color's hue -30 degrees.

    COLOR THEORY
  • complementary('color')
    Returns the complementary (180 degrees) of a given color.
  • splitComplementary('color')
    Returns 2 colors - and +150 degrees.
  • triadic('color')
    Returns 2 colors - and +120 degrees.
  • analogous('color')
    Returns 2 colors - and +30 degrees.
  • tetradic('color')
    Returns 3 colors + 60, 180 and 240 degrees.
  • square('color')
    Returns 3 colors + 90, 180 and 270 degrees.

    COLOR GENERATION
  • shades('color', count, amount)
    Generates a quantity (count) of colors with lowered lightness by an amount.
  • tints('color', count, amount)
    Generates a quantity (count) of colors with increased lightness by an amount.
  • tones('color', count, amount)
    Generates a quantity (count) of colors with decreased saturation by an amount.
adjustContrast and blackORwhite functions will let you control the contrast between colors, or automatically set a black or white color to the font depending on the background's brightness.
sidebyside2.jpg

Functions like shades tints and tones will let you generate any quantity of color variations to do whatever you want to do with them.

ezgif.com-video-to-gif-converter (3).gif

Functions like splitComplementary, analogous, triadic, tetradic, and square will let you use color theory to give some life to your skins.

ezgif.com-video-to-gif-converter (4).gif

Using only conversion and splitting functions you can make a functional color selector.

ezgif.com-video-to-gif-converter (6).gif


What you can or can't do with these is up to your creativity. So, stay creative!

On this package you'll find 5 examples and raincolors.lua script.
RainColorsExamples_1.2.18.03.24.rmskin
Changelog
  • Version1.2.18.03.24
  • Added function round(number, ndp)
  • Now returns are rounded to 2 decimal places for better accuracy.
  • Renamed functions saturation(color, amount), lightness(color, amount) and hue(color, angle) to setSat(color, newSat), setLight(color, newLight) and setHue(color,newHue)
  • Renamed functions RGB(HEX), HEX(color), HSL(color) to RGB2HSL(color), HSL2RGB(HSL), RGB2HEX(color), HEX2RGB(HEX)
  • Added functions red(color),green(color),blue(color), h(color), s(color) and l(color) which return a single value.
  • Added BasicColorSelector example skin.
  • Improved Themes example skin.
  • Adapted all other example skins to new changes.

    Version1.1.11.03.24
  • Fixed typo on hue('color') function that was causing it to not behave properly.

    Version 1.1.10.03.24
  • Deleted functions saturate(), desaturate(), brighten() and darken()
  • Added functions hue('color',newHue), saturation('color',newSat) and lightness('color',newLight)
    Those functions were doing practically the same as the shift functions, these new functions will fix the hue, saturation or lightness values to a new given value which allows for better control.
  • Renamed colorBrightness() function to brightness()
  • Renamed colorHSL(), colorRGB() and colorHEX() functions to HSL(),RGB() and HEX()
  • Small changes to example skins.
  • Added new Themes example skin.
  • Added support to input HEX colors.

    Version 1.0.09.03.24
  • Released
To do list:
Not sure yet.
You do not have the required permissions to view the files attached to this post.
Last edited by RicardoTM on March 18th, 2024, 1:16 pm, edited 14 times in total.
User avatar
jsmorley
Developer
Posts: 22631
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: colors.lua

Post by jsmorley »

You might look at this:

Code: Select all

-- HSBLib
-- Library of functions to covert between RGB and HSB color models
-- Convert between 'rrr,ggg,bbb' and HEX forms of RGB
-- Return the luminance (perceived lightness) of an RGB color
-- Compiled and modified from various sources by JSMorley
-- October 30, 2017

-- Function :		RGBtoHSB(arg)
-- Argument :	A string RGB color value in the form 'rrr,ggg,bbb'
-- Returns :		Three HSB values
-- 						HSB hue as a percentage from 0.0-1.0
-- 						*	Multiply 360 with this value to obtain the degrees of hue
--							While hue is most often expressed as an integer, the fractional
--							amount can be important for precision in calculations
--						*	Note that 0.0 (0 degrees) and 1.0 (360 degrees) are the same red hue
--							It's a counter-clockwise 360 degree "cylinder" of color
--							red->purple->blue->cyan->green->yellow->orange->red
-- 						HSB saturation as a percentage from 0.0-1.0
-- 						HSB brightness as a percentage from 0.0-1.0
--						*	Note that this is the "brightness" of the color, and not the 
--							"lightness" as in the HSL model. A color, particularly in the 
--							blue range, can have 100% brightness and still be visibly quite dark.
--							Use the Luminance() function below to obtain "visible lightness"
function RGBtoHSB(colorArg)
	
	colorArg = string.gsub(colorArg, ' ', '')
	inRed, inGreen, inBlue = string.match(colorArg, '(%d+),(%d+),(%d+)')
	
	percentR = ( inRed / 255 )
	percentG = ( inGreen / 255 )
	percentB = ( inBlue / 255 )

	colorMin = math.min( percentR, percentG, percentB )
	colorMax = math.max( percentR, percentG, percentB )
	deltaMax = colorMax - colorMin

	colorBrightness = colorMax

	if (deltaMax == 0) then
		colorHue = 0
		colorSaturation = 0
	else
		colorSaturation = deltaMax / colorMax

		deltaR = (((colorMax - percentR) / 6) + (deltaMax / 2)) / deltaMax
		deltaG = (((colorMax - percentG) / 6) + (deltaMax / 2)) / deltaMax
		deltaB = (((colorMax - percentB) / 6) + (deltaMax / 2)) / deltaMax

		if (percentR == colorMax) then
			colorHue = deltaB - deltaG
		elseif (percentG == colorMax) then 
			colorHue = ( 1 / 3 ) + deltaR - deltaB
		elseif (percentB == colorMax) then 
			colorHue = ( 2 / 3 ) + deltaG - deltaR
		end

		if ( colorHue < 0 ) then colorHue = colorHue + 1 end
		if ( colorHue > 1 ) then colorHue = colorHue - 1 end
		
	end

	return colorHue, colorSaturation, colorBrightness

end

-- Function :		HSBtoRGB(arg1, arg2, arg3)
-- Argument:		Three HSB values
-- 						HSB hue as a percentage from 0.0-1.0
-- 						HSB saturation as a percentage from 0.0-1.0
-- 						HSB brightness as a percentage from 0.0-1.0
-- Returns:		Three RGB values
-- 						Red value from 0-255
-- 						Green value from 0-255
-- 						Blue value from 0-255
function HSBtoRGB(colorHue, colorSaturation, colorBrightness)
	
		degreesHue = colorHue * 6
		if (degreesHue == 6) then degreesHue = 0 end
		degreesHue_int = math.floor(degreesHue)
		percentSaturation1 = colorBrightness * (1 - colorSaturation)
		percentSaturation2 = colorBrightness * (1 - colorSaturation * (degreesHue - degreesHue_int))
		percentSaturation3 = colorBrightness * (1 - colorSaturation * (1 - (degreesHue - degreesHue_int)))
		if (degreesHue_int == 0)  then
			percentR = colorBrightness
			percentG = percentSaturation3
			percentB = percentSaturation1
		elseif (degreesHue_int == 1) then
			percentR = percentSaturation2
			percentG = colorBrightness
			percentB = percentSaturation1
		elseif (degreesHue_int == 2) then
			percentR = percentSaturation1
			percentG = colorBrightness
			percentB = percentSaturation3
		elseif (degreesHue_int == 3) then
			percentR = percentSaturation1
			percentG = percentSaturation2
			percentB = colorBrightness
		elseif (degreesHue_int == 4) then
			percentR = percentSaturation3
			percentG = percentSaturation1
			percentB = colorBrightness
		else
			percentR = colorBrightness
			percentG = percentSaturation1
			percentB = percentSaturation2
		end
 
		outRed = Round(percentR * 255)
		outGreen = Round(percentG * 255)
		outBlue = Round(percentB * 255)
		
		return outRed, outGreen, outBlue
	
end

-- Function :		HEXtoRGB(arg)
-- Argument:		Hex string value in the form '#cccccc' or 'cccccc'
-- 						* HEX shorthand is supported
-- Returns:		Three RGB values
-- 						Red value from 0-255
-- 						Green value from 0-255
-- 						Blue value from 0-255
function HEXtoRGB(hexArg)

	hexArg = hexArg:gsub('#','')

	if(string.len(hexArg) == 3) then
		return tonumber('0x'..hexArg:sub(1,1)) * 17, tonumber('0x'..hexArg:sub(2,2)) * 17, tonumber('0x'..hexArg:sub(3,3)) * 17
	elseif(string.len(hexArg) == 6) then
		return tonumber('0x'..hexArg:sub(1,2)), tonumber('0x'..hexArg:sub(3,4)), tonumber('0x'..hexArg:sub(5,6))
	else
		return 0, 0, 0
	end

end

-- Function :		RGBtoHEX(arg1, arg2, arg3)
-- Argument:		Three RGB values
-- 						Red value from 0-255
-- 						Green value from 0-255
-- 						Blue value from 0-255
-- Returns:		String hex value in the form 'cccccc'
function RGBtoHEX(redArg, greenArg, blueArg)

	return string.format('%.2x%.2x%.2x', redArg, greenArg, blueArg)

end

-- Function:		ColorLumens(arg)
-- Argument:		A string RGB color value in the form 'rrr,ggg,bbb'
-- Returns:		Luminance (perceived lightness) as a percentage from 0.0-1.0 
--						*	A ratio of 5:1 on larger items and 10:1 on smaller text is
--							recommended as a "contrast" between foreground and
--							background colors to be comfortable to the human eye
function ColorLumens(colorArg)

	colorArg = string.gsub(colorArg, ' ', '')
	inRed, inGreen, inBlue = string.match(colorArg, '(%d+),(%d+),(%d+)')
	
	curLumens = Round((math.sqrt(0.299 * inRed^2 + 0.587 * inGreen^2 + 0.114 * inBlue^2) / 255) * 100)
	
	return curLumens

end

-- Utility functions

function Clamp(numArg, lowArg, highArg)

	return math.max(lowArg, math.min(highArg, numArg))
	
end

function Round(numArg, decimalsArg)
	
	local mult = 10 ^ (decimalsArg or 0)
	if numArg >= 0 then
		return math.floor(numArg * mult + 0.5) / mult
	else
		return math.ceil(numArg * mult - 0.5) / mult
	end
	
end
RicardoTM
Posts: 272
Joined: December 28th, 2022, 9:30 pm
Location: México

Re: colors.lua

Post by RicardoTM »

jsmorley wrote: March 5th, 2024, 10:57 pm You might look at this:
Thanks JS, I'll test that when I get home later tonight. However, after taking a quick look, it seems to lack HSL (it's my preferred color profile) and the actual color theory functions like getting complementary colors, shades, etc. Which is why I'm interested in the colors.lua code.
RicardoTM
Posts: 272
Joined: December 28th, 2022, 9:30 pm
Location: México

Re: colors.lua

Post by RicardoTM »

Update: yep, sadly it doesn't do what I need. What the code I found provides, apart from converting RGB to HSL, is an easy way to calculate color harmonies, such as complementary colors, tints, shades, triads, desaturate, lighten, etc. which is why I want to port it into Rainmeter.
User avatar
eclectic-tech
Rainmeter Sage
Posts: 5408
Joined: April 12th, 2012, 9:40 pm
Location: Cedar Point, Ohio, USA

Re: colors.lua

Post by eclectic-tech »

RicardoTM wrote: March 6th, 2024, 2:05 am Update: yep, sadly it doesn't do what I need. What the code I found provides, apart from converting RGB to HSL, is an easy way to calculate color harmonies, such as complementary colors, tints, shades, triads, desaturate, lighten, etc. which is why I want to port it into Rainmeter.
Color Triad is an old skin that can create 'complimentary' colors, but it also uses the HSLib that JSMorley posted, so it may not be any more useful to you.
:Whistle
RicardoTM
Posts: 272
Joined: December 28th, 2022, 9:30 pm
Location: México

Re: colors.lua

Post by RicardoTM »

eclectic-tech wrote: March 6th, 2024, 4:50 am can create 'complimentary' colors,
Those colors must say some nice things eh :D
eclectic-tech wrote: March 6th, 2024, 4:50 am but it also uses the HSLib that JSMorley posted, so it may not be any more useful to you.
:Whistle
Unfortunately no :(
User avatar
Yincognito
Rainmeter Sage
Posts: 7209
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: colors.lua

Post by Yincognito »

RicardoTM wrote: March 6th, 2024, 6:37 am Those colors must say some nice things eh :D



Unfortunately no :(
I'd say write your own functions, based on the Wiki data and the way the script you mentioned approaches things. I know, it's more convenient to not reinvent the wheel and use an already made script, but if it's syntax is incompatible with the types of function arguments used in Rainmeter's Lua, then there aren't many choices than to do it yourself. :confused:

After all, it's just math, how hard can it be? Plus, it would be a nice way to learn Lua (I did the same, learned it while making my ETS2 save game editor). ;-)
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
RicardoTM
Posts: 272
Joined: December 28th, 2022, 9:30 pm
Location: México

Re: colors.lua

Post by RicardoTM »

Yincognito wrote: March 6th, 2024, 3:01 pm I'd say write your own functions, based on the Wiki data and the way the script you mentioned approaches things. I know, it's more convenient to not reinvent the wheel and use an already made script, but if it's syntax is incompatible with the types of function arguments used in Rainmeter's Lua, then there aren't many choices than to do it yourself. :confused:

After all, it's just math, how hard can it be? Plus, it would be a nice way to learn Lua (I did the same, learned it while making my ETS2 save game editor). ;-)
Yup, I'm in the process actually. I will never stand with my arms crossed waiting for a miracle to happen :)
User avatar
Yincognito
Rainmeter Sage
Posts: 7209
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: colors.lua

Post by Yincognito »

RicardoTM wrote: March 6th, 2024, 3:25 pm Yup, I'm in the process actually. I will never stand with my arms crossed waiting for a miracle to happen :)
That's the spirit! :thumbup: :great:
One day when I'm in the mood, I might as well rewrite my own RGB-HSL-HSV-CMYK-LAB-YPbPr-XYZ-xyY Color.lua script to make it more flexible and correct the TODO-s in it.
One day... :???:
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
RicardoTM
Posts: 272
Joined: December 28th, 2022, 9:30 pm
Location: México

Re: colors.lua

Post by RicardoTM »

So... I've been writing it and testing it on the lua console and everything is working perfectly, the problem is that now when I try it on Rainmeter I get this error

"Script: colors.lua:12: attempt to perform arithmetic on local 'r' (a string value)"

I'm using the correct syntax [&MeasureScript:FunctionName('255,50,197')]

I have more code, but it says the same even if I only have the rgb2hsl function on the file, i had a bunch of local variables but I already delete all the local stuff and keeps saying the same.

Code: Select all

function rgb2hsl(r, g, b)
    r, g, b = r / 255, g / 255, b / 255
     max, min = math.max(r, g, b), math.min(r, g, b)
    l = (max + min) / 2
    if max == min then
        h, s = 0, 0
    else
         d = max - min
        s = l > 0.5 and d / (2 - max - min) or d / (max + min)
        if max == r then
            h = (g - b) / d + (g < b and 6 or 0)
        elseif max == g then
            h = (b - r) / d + 2
        else
            h = (r - g) / d + 4
        end
        h = h / 6
    end
    return h, s, l
end
this is how I had it originally:

Code: Select all

function rgb2hsl(r, g, b)
    r, g, b = r / 255, g / 255, b / 255
    local max, min = math.max(r, g, b), math.min(r, g, b)
    l = (max + min) / 2
    if max == min then
        h, s = 0, 0
    else
        local d = max - min
        s = l > 0.5 and d / (2 - max - min) or d / (max + min)
        if max == r then
            h = (g - b) / d + (g < b and 6 or 0)
        elseif max == g then
            h = (b - r) / d + 2
        else
            h = (r - g) / d + 4
        end
        h = h / 6
    end
    return h, s, l
end
any idea why?

edit.

Found the problem, it is reading '255,50,197' as a string, it has to be entered as '255','50','197'...

fixed it changing

Code: Select all

function rgb2hsl(color)
	color = string.gsub(color, ' ', '')
	r, g, b = string.match(color, '(%d+),(%d+),(%d+)')
and also adding

Code: Select all

return string.format('%d,%d,%d', r, g, b)
to the other functions.