Skip to content

Chimera’s Color Picker – A Free Script

Chimera’s Color Picker

A Gift to the Second Life Community

Introduction to the Color Picker

I was quite surprised when after searching for the better part of month that I could not find a Color Picker freely available to the Second Life community.   The best solution, of course, would be for Second Life to make one available as a part of the tools available in their scripting language, but none is currently available.

There are a couple of proprietary color pickers being sold, but you can’t really plug them into your own projects since the scripts aren’t modifiable.  Additionally, there are some very good examples showing coordinate-to-color conversions in the public domain, but as a workable color picker they are far from useable.

Putting together a practical and useable color picker offered me a way in which I could make a contribution to the Second Life community.  So over the course of a couple of months, I spent lots of hours working on a solution.  It is now finished, and I am making it freely available for anyone’s use in their own projects.

I tried to create a color picker that looks and acts similar to the internal dialog found in Second Life.  I wanted something that’s familiar and comfortable to users.  Of course, because of code limitations, it’s not possible duplicate all of the functions in Second Life’s internal dialog.  The best solution would be for Second Life to provide a plug-in module for scripting purposes, but until that time, I hope that the solution I’m providing here will be helpful to you.

Here’s what it looks like . . .ColorPickerDescribedDescription

The color picker allows a user to select a color from a palette and adjust the luminosity (brightness) of the color on a luminosity slider. Selection is done by clicking the mouse on the palette or the luminosity slider.  The basic overall structure and lay-out resembles Second Life’s internal Color Picker dialog box.  The user’s selected color and associated brightness appears  in a “Current Color” swatch – which is also referred to as the “Preview” swatch.

A “Plus Marker” moves to the touched location on the color palette so that the user has a visual point of reference. There’s also a pointer on the luminosity slider providing a similar point of reference for the brightness scale.

The user may save up to six (6) colors (although, it is not too difficult to add or subtract to the number of saved colors for your own projects). The user may also select from 17 swatches at the bottom of the Color Picker which consist of the most common and basic web colors.  Once the selection is made, and if desired, the user can further adjust the color to their liking.  The web colors can be changed depending on your project, but it does require making the appropriate change in the image that underlies the Color Picker.  Once again, that’s not too difficult since the image has full permissions and all the basic scripting elements are in place.

All of the user’s “Saved Colors” remain and are available next time the Color Picker used. The colors and the reference markers on the palette and luminosity slider are saved even when the Color Picker is detached and/or placed back in the user’s inventory.

Credits

Almost all scripts in Second Life are built on the work of others and this is certainly no exception.  It would not have been possible if a number of scripters had not worked around the edges coming up with innovative ideas and approaches.  The first individuals – of whom I’m aware – and who made code publicly available to us all include Aaron Edelweiss, Fox Stirling, Bjorn Nordlicht (and forgive me if I’ve left out someone). They worked out methods by which a color could be selected (by clicking on it) from a palette and converting the resulting touch coordinates into HSL and RGB color values.

I also used the RGB to HSL color conversation function found in the Second Life scripting library. The function was coded by Clematide Oyen (Laura Aastha Bondi) who in turn was inspired by a function written by Alec Thilenius.

One key problem in putting the Color Picker together was trying to come up with a method of marking where the user clicked on the color palette and luminosity slider.  When one is trying to get just the right color, it’s very helpful – almost critical – to have those two reference points.

Thankfully, Nova Convair came to the rescue by responding to my post on the scripting forum.  His solution was unique and elegant. He suggested – and provided some sample code – to use a transparent prim with a marker on it which overlays the color palette.  The real genius in his approach was this: moving the marker to the last clicked point was not done by using positioning coordinates (the usual approach), but rather by using texture off-sets. You’ll see that in the code, below, where llSetLinkPrimitiveParams moves the “Plus Marker” prim by off-sets.  The same  solution worked for the luminosity slider.

I would be remiss not to mention Rolig Loon who is always generous with his time to help others on the scripting forum. Finally I would like to thank all of the Second Life scripters who freely make their knowledge available by public postings of code samples and completed projects which benefit us all.

 

How to Use this Script

It is possible to build your own color picker with a few prims and use this script.  But to save you many hours of time, I have pre-built it for you.  All prims, images and code are all full permissions.  It’s all linked together up with the script loaded and ready to run.  I’ve even included a demonstration object so you can see how the colors change. The pre-built Color Picker gives you a starting point, and then, if necessary for your own project, you can modify it by re-arranging prims or altering the underlying image.

I have put everything together in a package which includes the 12-prim color picker, the color picker script, and a demonstration object that allows you to see how everything works.  The free package is available at my store –  more information below on how to obtain it.

The pre-built Color Picker can be attached as a HUD, or it can be rezzed.  It works in either configuration.  I tried to size the Color Picker so that in HUD form, it would approximate the size of Second Life’s internal color picker.  Of course, I couldn’t standardize it since the size of HUD’s vary with screen resolution.  I worked with several different sizes and settled upon one that worked reasonable well for a variety of different resolutions.

There are two scripts:

  • Color Picker Script.  This script goes in the parent prim of the Color Picker which is the largest prim, the prim that forms the base of the Color Picker.  No other scripts are necessary in the Color Picker.  If you re-design the color picker, you won’t have to worry about placing scripts in other linked prims.
  • Colorizing Script.  The second script (the “Colorizing” script) goes in the object that you will be coloring.  The object might be a clothing, shoes, hair, or a build of some type.  In the package, I have included a “Colorizing” script which receives the color values from the Color Picker.

Just a bit more about the Colorizing script.  As noted above, the Colorizing script receives the color values from the Color Picker.  The way this is done is that Color Picker sends the values by the use of the “llSay” function.  The Colorizing script, then, uses the “llListen” function to receive the color values.  Once the values are received, the Colorizing script makes the appropriate color change to the object.

Like the Color Picker script, the Colorizing script can be modified depending upon your needs. You may need to color only certain sides – or selected prims.  You have complete flexibility to incorporate whatever changes necessary.

Usual Cautions.  While I have spent many hours debugging the script, I can’t promise it will work in all applications. You should thoroughly test it for your project.  Should you make modifications, I have left in various (but commented out) “llOwnerSay” statements which can aid in debugging.

 

Comments in the Script

You’ll see that I’m very much a believer in well documented scripts.  I know.  I know.  That’s contrary to Second Life scripting convention.  One of the scripting style guides for Second Life says this:

“Avoid excessive comments. Well written code explains itself. For more complicated functions, put general comments concerning what you are trying to do at the top of the function. Leave comments out of the body of the function.”

It’s my personal belief that sparsely commented scripts have done the Second Life world a big disfavor.  No, make that a huge disfavor.  Here’s why.

It’s clear that the majority of Second Life scripters, myself included, are in the novice to intermediate category.   Yes, well written code explains itself – but it explains itself to the experts, to individuals working with Second Life scripts day in and day out.   To the rest of us, the great majority,  well, we’re left in the dark.

Comments really help to understand the logic and flow of the script – and in many cases help to remind where the differences lie between Second Life scripting language and other programming languages.   Just take a look at what the no “excessive comments” rule has wrought:   most SL scripts are spectacularly devoid of comments.  A good many are not even commented at all.  It’s a real struggle to understand them.  I have taken hours to decipher some of them.  With others, I’ve simply given up.

Those of you who find the script, below, “excessively” commented can easily delete the comments.  The rest of us, I believe, will be happy to leave them in.

 

Color Picker Package

As mentioned above, the following script is available in a complete package.

The free package is available at my store.  I only ask for one small favor.  While you are at my store, I would be grateful if you added the store to your “Picks.”  (If you’ve never added a store to your picks, here how to do it in two illustrations:  Adding a Pick )

Stop by my store (Chimera’s Simple Elegance) for the free package that has all the parts and is ready to be used.  If you’d like to put things together yourself, I’ve also reproduced both scripts for your use below.

 

First Script: Color Picker Script
(This script goes in the contents of the Color Picker)

//Chimera's Color Picker
//
//Compiled and coded by Chimera Firecaster 
// (With deep appreciation to the individuals mentioned in the "credits" below)
//
//Version 1.0
//April, 2014
//
//WHEN RECOMPILING, USE THE MONO OPTION
//
//PURPOSE: A plug-in module resembling the Color Picker dialog box in Second Life which can be 
//  used for your own projects in which you or an end-user needs to select a color.
//
//CREDITS: This color picker is built upon the work of others.  It would not have been possible if a 
//  number of scripters had not worked around the edges coming up with innovative ideas and approaches.
//  The first individuals - of whom I'm aware - and who made code publicly available to us all include 
//  Aaron Edelweiss, Fox Stirling, Bjorn Nordlicht (and forgive me if I’ve left out someone).  They 
//  worked out methods by which a color could be selected (by clicking on it) from a palette.  I 
//  also used the RGB to HSL color conversation function found in the Second Life scripting library.  
//  The function was coded by Clematide Oyen (Laura Aastha Bondi) who in turn was inspired by a function 
//  written by Alec Thilenius.
//
//One key problem in putting the Color Picker together was trying to come up with a method of 
//  marking where the user clicked in the color palette and luminosity slider.  When one is trying to 
//  get just the right color, it’s very helpful - almost critical - to have those two reference points.
//  Thankfully, Nova Convair came to the rescue by responding to my post on the scripting forum.  His
//  solution was unique and elegant.  He suggested - and provided some sample code - using
//  a transparent prim with a marker on it to overlay the color palette.  Here's the real genius in
//  his approach: moving the marker to the last clicked point was done not by using positioning 
//  coordinates (the usual approach), but rather by using texture off-sets.  You'll see that in the 
//  code, below, where llSetLinkPrimitiveParams moves the "Plus Marker" prim by off-sets.  The same 
//  solution worked for the luminosity slider.
//
//I would be remiss not to mention Rolig Loon who is always generous with his time to help others on
//  the scripting forum.  Finally I would like to thank all of the Second Life scripters who freely
//  make their knowledge available by public postings of code samples and projects which benefit us all.
//
//
//HOW TO USE THIS SCRIPT: It possible to build the Color Picker with a few prims and some images, but
//  to save you many hours of time, I have pre-built it for you.  All prims, images and code are all
//  full permissions.  It's all linked together up with the code in place.  I've even included a 
//  demonstration cube so you can see how the colors change.  The pre-built Color Picker gives you a 
//  starting point, and then, if necessary for your own project, you can modify it by re-arranging 
//  prims or altering the underlying image.  You can obtain the free, full permissions color picker 
//  with modifiable code at my store, at the following: http://slurl.com/secondlife/Duggan/102/230/68/
//
//CAUTION: While I have spent many hours debugging the script, I can't promise it will work in
//  all applications.  You should thoroughly test it for your project.  
//
//NOTE: this is a FREE script.  It is given generously to the Second Life community without the 
//  expectation of anything in return. You are most welcome to incorporate it into your own projects
//  whether they are in the public domain or not.  But please do not create and charge for a "Color 
//  Picker" or something very similar based on the code below.  That's bad form and will 
//  most certainly bring bad karma if you dishonor the kindness of others.
//
//
//-----Do Not Remove Above Information
//
//
//Values you may wish to adjust . . .

//Channel to pass color values to a script in the object which is being colored
integer gChannel = -2471717;

//The object being colored will pick up each selected color.  However, if DON't want the object
//to be colored until the user clicks on OK, then set the following to FALSE
integer gKeepUpDating = TRUE;

//Global Initializations . . .
integer gLPointerNo; //Link # of Luminsity Pointer
integer gLSliderNo;  //Link # of Luminisity Slider
vector gLumSlider; //touch coordinate from Lum Slider
integer gLPlusMarker; //Link # of Marker on Palette
integer gLPreview;  //Link # of Current Color (Preview) Swatch
//Variables and associated processes connected with RGB and HSL colors 
//can get confusing, Sothroughout the scripts, I've specifically indicate 
//which is which by including RGB & HSL in the variable names
vector gPreviewColorRGB; //RGB Value of Current Color (Preview) Swatch 
vector gPreviewColorHSL; //HSL Value of Current Color (Preview) Swatch 
integer gSaveColor1; //Link #'s of SaveColors
integer gSaveColor2; 
integer gSaveColor3; 
integer gSaveColor4; 
integer gSaveColor5; 
integer gSaveColor6; 
vector gSaveColorRGB1; //SaveColors in RGB
vector gSaveColorRGB2;
vector gSaveColorRGB3;
vector gSaveColorRGB4; 
vector gSaveColorRGB5;
vector gSaveColorRGB6;



//------------------------------------------------------------------------------
// User Defined Functions
//------------------------------------------------------------------------------



//Many thanks to Fox Stirling & Aaron Edelweiss who generously provided ideas and 
//code samples for the following four functions.  Their important contributions
//to SL scripting involved converting between coordinates on a color palette (and
//luminosity slider to HSL and RGB color values.

//This is used to tint the luminosity slider, coordinating it with 
//the "Current" (preview) color.  A fixed LUM of 0.70 is used for the tint.
TintLumSlider(float hue, float sat) 
{
    float R;
    float G;
    float B;
    float var_1;
    float var_2;
    float lum = 0.70;
    
    if ( sat == 0 )    //HSL from 0 to 1
    {
        R = lum * 255;  //RGB results from 0 to 255
        G = lum * 255;
        B = lum * 255;
    }
    else
    {
        if ( lum < 0.5 )
        {
            var_2 = lum * (1.0 + sat);
        }
        else if(lum >= 0.5)
        {
            var_2 = (lum + sat) - (lum * sat);
        }
        var_1 = 2 * lum - var_2;        
        
        R = Hue_2_RGB( var_1, var_2, (hue + (1.0/3.0)));
        G = Hue_2_RGB( var_1, var_2, hue);
        B = Hue_2_RGB( var_1, var_2, (hue - (1.0/3.0)));
    }
    //llOwnerSay("Color: "+(string)R+" "+(string)G+" "+(string)B);    
    llSetLinkColor(gLSliderNo, <R,G,B>, ALL_SIDES);
}



//Calculates HSL (Hue & Sat) from the touch coordinates of the Palette
vector CalculateHueSat(vector Palette)  
{
    integer hue;
    integer sat; 
    //llOwnerSay("Calculate Hue & Sat Function - Start Palette.x: " +(string)Palette.x);
    //Note that we are dealing with touch coordinates - not offsets               
    float hPoint = Palette.x * 360;  //touch coordinates (0 to 1)from the Palette 
    float sPoint = Palette.y * 100;  //examples  .5 * 100 = 50    
    //calculate standard HSL hue scale 0 - 360
    if(hPoint > 359.5)
    {
        hue = llCeil(hPoint);  //359.5+ becomes 360
    }
    else if(hPoint < 0.5)    //.5 - becomes 0
    {
        hue = llFloor(hPoint);
    }
    else
    {
        hue = (integer)hPoint; //51.3 becomes 51
    }
    if(hue == -360)
    {
        hue = 0;
    }    
    //calculate standard HSL saturation scale 0 - 100
    if(sPoint > 99.5)
    {
        sat = llCeil(sPoint);  //99.5 becomes 100
    }
    else if(sPoint < 0.5)  // 0
    {
        sat = llFloor(sPoint);
    }
    else
    {
        sat = (integer)sPoint;
    }    
    if(sat == -100)
    {
        sat = 0;
    }
    //llOwnerSay("Hue: "+(string)hue + " Sat: " + (string)sat);     
    vector HSL = <hue,sat,0>;    
    HSL.x = (HSL.x * 1.0) / 360; //convert from standard HSL to SL's HSL
    HSL.y = (HSL.y * 1.0) / 100; //convert from standard HSL to SL's HSL 
    HSL.z = gPreviewColorHSL.z;  //Pick up Lum from preview
    return HSL;    
}   



//Sets the color in the CURRENT (preview) SWATCH
SetPreviewColor(vector HSL)
{    
    gPreviewColorHSL = HSL; //Save preview color in HSL 
    float R;
    float G;
    float B;
    float var_1;
    float var_2;
    if ( HSL.y == 0 )     //Saturation = 0
    {
        R = HSL.z * 255;  //RGB results from 0 to 255
        G = HSL.z * 255;
        B = HSL.z * 255;
    }
    else
    {
        if ( HSL.z < 0.5 )
        {
            var_2 = HSL.z * (1.0 + HSL.y);
        }
        else if(HSL.z >= 0.5)
        {
            var_2 = (HSL.z + HSL.y) - (HSL.z * HSL.y);
        }
            var_1 = 2 * HSL.z - var_2;       
        
        R = Hue_2_RGB( var_1, var_2, (HSL.x + (1.0/3.0)));
        G = Hue_2_RGB( var_1, var_2, HSL.x);
        B = Hue_2_RGB( var_1, var_2, (HSL.x - (1.0/3.0)));
    }
    llSetLinkColor(gLPreview,<R,G,B>,ALL_SIDES);
    //llOwnerSay("Color in RGB: "+ (string)<R,G,B>);
    gPreviewColorRGB = <R,G,B>;    
    TintLumSlider(HSL.x,HSL.y);  //tints the luminosity slider   
    if (gKeepUpDating)
    {
        SendColorInfo("CONTINUE"); //Send the color values to a script in the object to be colored
    }   
}


    
//Used to convert hue to RGB
float Hue_2_RGB( float v1, float v2, float vH ) 
{
    if ( vH < 0.0 )
    { 
        vH += 1.0;
    }
    if ( vH > 1.0 )
    { 
        vH -= 1.0;
    }
    if ( ( 6.0 * vH ) < 1.0 )
    {        
        return ( v1 + ( v2 - v1 ) * 6.0 * vH );
    }
    if ( ( 2.0 * vH ) < 1.0 )
    {         
        return ( v2 );
    }
    if ( ( 3.0 * vH ) < 2.0 ) 
    {        
        return ( v1 + ( v2 - v1 ) * ( ( 2.0 / 3.0 ) - vH ) * 6.0 );
    }
    else
    {        
        return ( v1 );
    }
}



// RGB to HSL conversion function. By Clematide Oyen (Laura Aastha Bondi). Inspired by 
// a function written by Alec Thilenius.  Available on the SL Script Library
vector RgbToHsl(vector rgb)
{
    float r = rgb.x;
    float g = rgb.y;
    float b = rgb.z;
    float h;
    float s;
    float l;
    float max;
    float min; 
    // Looking for the max value among r, g and b
    if (r > g && r > b) max= r;
    else if (g > b) max = g;
    else max = b; 
    // Looking for the min value among r, g and b
    if (r < g && r < b) min = r;
    else if (g < b) min = g;
    else min = b;
 
    l = (max + min) / 2.0;
 
    if (max == min)
    {
        h = 0.0;
        s = 0.0;
    }
    else
    {
        float d = max - min;
 
        if (l > 0.5) s = d / (2.0 - max - min);
        else s = d / (max + min);
 
        if (max == r) {
            if (g < b) h = (g - b) / d + 6.0;
            else h = (g - b) / d;
        }
        else if (max == g)
            h = (b - r) / d + 2.0;
        else
            h = (r - g) / d + 4.0;
        h /= 6.0;
    } 
    return <h, s, l>;
}



//This positions the Lum Pointer based on the color in the current (preview) swatch
MoveLumPointer(vector ColorHSL)
{    
     list ParamList = llGetLinkPrimitiveParams(gLPointerNo,[PRIM_TEXTURE,4]);
     //ParamList is a list consisting of texture name, repeats, off-sets, etc.
     string TextName = llList2String(ParamList,0);        
     string OldParams= llList2String(ParamList,1); //repeats
     vector Repeats = (vector)OldParams;
     string OldParams2 = llList2String(ParamList,2); //offsets
     vector OffSets = (vector)OldParams2;
     ColorHSL.x = OffSets.x;
     //Next is important: Luminosity is contained in ColorHSL.z (that's "z" not "y")
     ColorHSL.y = ColorHSL.z;
     ColorHSL.z = 0;     
     //Make sure the coordinates don't exceed the boundaries
     if (ColorHSL.y < .023913) 
     {                        
         ColorHSL.y = .023913;
     }   
     if (ColorHSL.y > .973882) 
     {                        
         ColorHSL.y = .973882;
     }  
     //Need the inverse
     if (ColorHSL.y == 0)
     {
         ColorHSL.y = 1;
     }
     else if (ColorHSL.y == 1)
     {
         ColorHSL.y = 0;   
     }
     else
     {
         ColorHSL.y = llFabs(1 - ColorHSL.y);
     }           
     ColorHSL.y = ColorHSL.y-.47;     
     //llOwnerSay("Moving Lum Pointer - Actual Offset: "+(string)ColorHSL.y);
     llSetLinkPrimitiveParamsFast(gLPointerNo, [PRIM_TEXTURE, 4, TextName, Repeats, ColorHSL, PI ]);
}



//This positions the Plus Marker based on the color in the current (preview) swatch
//Face #4 is hard coded.  If you change prims, you may need to change the face #
MovePlusMarker(vector ColorHSL)  
{   
    list ParamList = llGetLinkPrimitiveParams(gLPlusMarker,[PRIM_TEXTURE,4]);
    //ParamList is a list consisting of texture name, repeats, off-sets, etc.    
    string TextureName = llList2String(ParamList,0);       
    string OldParams= llList2String(ParamList,1); //repeats
    vector Repeats = (vector)OldParams;
    string OldParams2 = llList2String(ParamList,2); //offsets
    vector OffSets = (vector)OldParams2;    
    ColorHSL.x = ColorHSL.x - (ColorHSL.x * .05); //adjust for increased size of plus marker     
    ColorHSL.y = ColorHSL.y - (ColorHSL.y * .05); //adjust for increased size of plus marker     
    //llOwnerSay("HSL Color prior to conversion to offsets: "+(string)ColorHSL.x);
    //Slope-intercept formula correction - see note in "if (nLinkNo == gLPlusMarker)" below
    ColorHSL.x = ColorHSL.x + ((ColorHSL.x - 1.26163) / -23.48) ;   
    //Make sure we don't go beyond the palette boundaries
    if (ColorHSL.x > .95179) 
    {
         ColorHSL.x = .95179;  
    }
    if (ColorHSL.x < .04707 )
    {
         ColorHSL.x = .04707;
    }
    if (ColorHSL.y > .95060)
    {
         ColorHSL.y = .95060;
    }
    if (ColorHSL.y < .03623)
    {
         ColorHSL.y = .03623;
    }
    ColorHSL.x = ColorHSL.x - .5;  //.47 better for yellow and cyan
    ColorHSL.y = ColorHSL.y - .5;    
    //llOwnerSay("HSL converted to offsets: "+(string)ColorHSL);    
    ColorHSL.z = 0;
    //llOwnerSay("PLUS POINTER - Offsets to Move: "+(string) ColorHSL);
    //this positions the Plus Pointer based on color
    llSetLinkPrimitiveParamsFast(gLPlusMarker, [PRIM_TEXTURE, 4, 
                       TextureName, Repeats, ColorHSL, PI ]);
}


//Second routine which sets the color in "Current Color" (Preview)
//Swatch - It ALSO returns the color in HSL values   
vector SetPreviewColor2(vector ColorRGB)
{  
    llSetLinkColor(gLPreview,ColorRGB,ALL_SIDES);                
    gPreviewColorRGB = ColorRGB;
    vector HSL2 = RgbToHsl(ColorRGB);    
    //llOwnerSay("HSL2: "+(string)HSL2);    
    return HSL2;
}


//This is used by each of the Save Colors & the Small Swatches of Basic Web Colors
//It sets the "Current" preview color, tints the Lum Slider and moves the markers
//Although I could have included the lines below for each swatch, this saves
//quite a few lines of code.  Also note that RGB colors for the web colors need
//conversion to SL's system which is easily done by dividing by 255
ColorizeMoveMarkers(vector ColorRGB)
{
    if (ColorRGB.x > 1 || ColorRGB.y > 1 || ColorRGB.z > 1)
    {
        ColorRGB = ColorRGB / 255;  
    }
    gPreviewColorHSL = SetPreviewColor2(ColorRGB); //set the preview color & return HSL 
    TintLumSlider(gPreviewColorHSL.x, gPreviewColorHSL.y);  //tint the Lum Slider                   
    MovePlusMarker(gPreviewColorHSL);  //move the Plus Marker based on HSL color values
    MoveLumPointer(gPreviewColorHSL);  //move the Lum Pointer based on HSL color values
    if (gKeepUpDating)
    {
        SendColorInfo("CONTINUE"); //Send the color values to a script in the object to be colored
    }   
    
} 


//This sends the SL RGB color values to the object which will be colored
//WhichButton means which button has been pressed:  OK, Cancel or Continue (which means keep going)
SendColorInfo(string WhichButton)
{
    string Msg = "["+"<" + (string)gPreviewColorRGB.x + "," + (string)gPreviewColorRGB.y + 
                  "," + (string)gPreviewColorRGB.z +">"+"]"+"["+WhichButton+"]"+"]";
    llSay(gChannel, Msg);
    
}    
                                                                                                          
//---------------------------------------------------------------------------  
// End of User Defined Functions                       
//---------------------------------------------------------------------------


default
{
    state_entry()
    {
        integer i = llGetNumberOfPrims();
        while (i)  //Start by getting the link numbers of each prim
        {
            if (llGetLinkName(i) == "LumPointer") // This is the Lumosity pointer link #
            {
                gLPointerNo = i;                
            }
            else if (llGetLinkName(i) == "LumSlider")  //This is Lumosity slider link #
            {
                gLSliderNo = i;
            }
            else if (llGetLinkName(i) == "PlusMarker")  //plus marker overlaying the palette link #
            {
                gLPlusMarker = i;
            }
            else if (llGetLinkName(i) == "PreviewBox")  //This is the preview link #
            {
                gLPreview = i;
            }    
            else if (llGetLinkName(i) == "SaveColor1")  //Save Color link #
            {
                gSaveColor1 = i;
            }        
            else if (llGetLinkName(i) == "SaveColor2")  //Save Color link #
            {
                gSaveColor2 = i;
            }        
            else if (llGetLinkName(i) == "SaveColor3")  //Save Color link #
            {
                gSaveColor3 = i;
            }                
            else if (llGetLinkName(i) == "SaveColor4")  //Save Color link #
            {
                gSaveColor4 = i;
            }                
            else if (llGetLinkName(i) == "SaveColor5")  //Save Color link #
            {
                gSaveColor5 = i;
            }                
            else if (llGetLinkName(i) == "SaveColor6")  //Save Color link #
            {
                gSaveColor6 = i;
            }                
          --i;
        } 
        list ParamList  = llGetLinkPrimitiveParams(gLPreview,[PRIM_COLOR,ALL_SIDES]);
        string OldParams = llList2String(ParamList,0);   
        gPreviewColorRGB = (vector)OldParams;  //set the global preview swatch variable in RGB
        //just in case, something went wrong during last use, set preview at blue color
        if (gPreviewColorRGB.x > 1 || gPreviewColorRGB.y > 1 || gPreviewColorRGB.y > 1)
        {
            gPreviewColorRGB.x = .36640;  //light blue color - places plus marker in
            gPreviewColorRGB.y = .74032;  //upper 1/3 of palette and luminosity pointer
            gPreviewColorRGB.z = .91360;  //in upper half of luminosity slider
            gPreviewColorHSL = SetPreviewColor2(gPreviewColorRGB); //sets preview color
            TintLumSlider(gPreviewColorHSL.x, gPreviewColorHSL.y);  //tint the Lum Slider                   
            MovePlusMarker(gPreviewColorHSL);  //move the Plus Marker based on HSL color values
            MoveLumPointer(gPreviewColorHSL);  //move Lum Pointer based on HSL color values                
        }        
        gPreviewColorHSL = RgbToHsl(gPreviewColorRGB); //set global preview swatch in HSL
        
        ParamList  = llGetLinkPrimitiveParams(gSaveColor1,[PRIM_COLOR,ALL_SIDES]);
        OldParams = llList2String(ParamList,0);   
        gSaveColorRGB1 = (vector)OldParams;  //set the SaveColor1
        
        ParamList  = llGetLinkPrimitiveParams(gSaveColor2,[PRIM_COLOR,ALL_SIDES]);
        OldParams = llList2String(ParamList,0);   
        gSaveColorRGB2 = (vector)OldParams;  //set the SaveColor2
        
        ParamList  = llGetLinkPrimitiveParams(gSaveColor3,[PRIM_COLOR,ALL_SIDES]);
        OldParams = llList2String(ParamList,0);   
        gSaveColorRGB3 = (vector)OldParams;  //set the SaveColor3
        
        ParamList  = llGetLinkPrimitiveParams(gSaveColor4,[PRIM_COLOR,ALL_SIDES]);
        OldParams = llList2String(ParamList,0);   
        gSaveColorRGB4 = (vector)OldParams;  //set the SaveColor4
        
        ParamList  = llGetLinkPrimitiveParams(gSaveColor5,[PRIM_COLOR,ALL_SIDES]);
        OldParams = llList2String(ParamList,0);   
        gSaveColorRGB5 = (vector)OldParams;  //set the SaveColor5
        
        ParamList  = llGetLinkPrimitiveParams(gSaveColor6,[PRIM_COLOR,ALL_SIDES]);
        OldParams = llList2String(ParamList,0);   
        gSaveColorRGB6 = (vector)OldParams;  //set the SaveColor6        
    }
    

    touch_start(integer total_number)
    {
        integer nLinkNo = llDetectedLinkNumber(0); 
        //User has touched the COLOR PALETTE (actually, it's PLUS MARKER prim that they touch)       
        if (nLinkNo == gLPlusMarker)
        {
              //llOwnerSay( "\nTouched PlusMarker.");
              list ParamList = llGetLinkPrimitiveParams(gLPlusMarker,[PRIM_TEXTURE,llDetectedTouchFace(0)]);
              //ParamList is a list consisting of texture name, repeats, off-sets, etc.
              //First element: texture - Element 0 (not 1)
              string TextureName = llList2String(ParamList,0);                 
              string OldParams= llList2String(ParamList,1); //repeats
              vector Repeats = (vector)OldParams;
              string OldParams2 = llList2String(ParamList,2); //offsets
              vector OffSets = (vector)OldParams2;        
              vector Palette = llDetectedTouchST(0); //Palette is the touch coordinates - not offsets         
              vector v = Palette; //we'll use v for the offsets         
              //llOwnerSay("Plus Marker Touched At: "+(string)v);           
              //PlusMarker is 10% larger than Palette - need to increase coordinates by 5% 
              //It's purposely made larger so that the plus marker doesn't disappear on sides       
              Palette = (Palette * .05) + Palette;   
              //Also a slight correction is required so that the color touched on the pallet is the same
              //color that visually appears in the "Current" preview swatch. It's a very slight difference
              //resulting from the conversion from touch coordinates to color values.  It is not actually
              //even apparent until clicking the narrow yellow and cyan color bands at the top of the palette.  
              //The difference is slightly more on the left side of the palette than the right.  Few people
              //would notice it, but, nonetheless, to assure visual integrity, the slope-intercept formula 
              //is employed to make this adjustment. The slope (m) calculates at -23.48.  The intercept (b) 
              //calculates at 1.26163, resulting in the following: 
              Palette.x = Palette.x - ((Palette.x - 1.26163) / -23.48) ;           
              //just in case the user has gone over the boundaries, the following
              //will keep the plus marker from going out the palette boundaries
              //and it keeps Palette at 1 or under.  Note that v.x & v.y are hard coded
              //if you change the size of the palette, you may need to re-calculate
              if (Palette.x > 1) 
              {
                  Palette.x = 1;
                  v.x = .95179;  
              }
              if (v.x < .04707 )
              {
                  Palette.x = 0;
                  v.x = .04707;
              }
              if (Palette.y > 1)
              {
                  Palette.y = 1;
                  v.y = .95060;
              }
              if (v.y < .03623)
              {
                  v.y = .03623;
              }
              //llOwnerSay("Actual palette coordinates for color: "+(string)Palette);
              v.x -= .5;
              v.y -= .5;          
              //llOwnerSay("Plus Marker Offsets: "+(string)v);              
              //this moves the plus marker to the touched location - note use of PI for rotation
              llSetLinkPrimitiveParamsFast(gLPlusMarker, [PRIM_TEXTURE, llDetectedTouchFace(0), 
                                           TextureName, Repeats, v, PI ]);
              //Using the coordinates, the following calculate hue & saturation & places it in HSL             
              vector HSL = CalculateHueSat(Palette);  //lum is picked up from gPreviewColorHSL
              //llOwnerSay("HSL from CalculateHueSat: "+ (string)HSL);                             
              SetPreviewColor(HSL); //Set the preview color by converting from HSL to RGB   
        }  
               
                                                                 
        //User has touched the LUMINOSITY SLIDER (Actually they touch the LUM POINTER prim)
        else if (nLinkNo == gLPointerNo)  //Pointer for Luminosity Slider Touched
        {
             //llOwnerSay("Lum Slider Touched.");
             integer lum;  //lum is our variable for luminosity
             list ParamList = llGetLinkPrimitiveParams(gLPointerNo,[PRIM_TEXTURE,llDetectedTouchFace(0)]);
             //ParamList is a list consisting of texture name, repeats, off-sets, etc.             
             string TextName = llList2String(ParamList,0);   
             string OldParams= llList2String(ParamList,1); //repeats
             vector Repeats = (vector)OldParams;
             string OldParams2 = llList2String(ParamList,2); //offsets
             vector OffSets = (vector)OldParams2;             
            
             vector gLumSlider = llDetectedTouchST(0); //local coordinates 
             vector v = gLumSlider; //We'll use v for the offsets             
             //llOwnerSay("Lum slider touched - Y = "+(string)v.y); 
             v.x = OffSets.x;  //x (horizontal stays the same - we are only interested in Y             
             //LumPointer is made larger so that the pointer doesn't disappear on top & bottom,   
             //so make sure the pointer arrow doesn't exceed the top or bottom of the lum slider
             if (gLumSlider.y < .023913) 
             {                        
                 v.y=.023913;
             }   
             if (gLumSlider.y > .973882) 
             {                        
                 v.y=.973882;
             }   
             v.y -= .47;  //.47 instead of .5 provide more accurate placement                   
             //llOwnerSay("Lum pointer MOVED to OFFSET = " + (string)v.y);
             //this moves the lum pointer to the touched location             
             llSetLinkPrimitiveParamsFast(gLPointerNo, [PRIM_TEXTURE, llDetectedTouchFace(0), 
                                       TextName, Repeats, v, PI ]);                        
             //clicking high on the luminosity slider gives us smaller numbers
             //BUT for luminosity, low numbers are darker - so we need the inverse
             if (gLumSlider.y == 0)
                {
                    gLumSlider.y=1;
                }
             else if (gLumSlider.y==1)
                {
                    gLumSlider.y=0;   
                }
             else
                {
                    gLumSlider.y = llFabs(1 - gLumSlider.y);
                }              
             if (gLumSlider.y > 1)    
             {                      
                 gLumSlider.y = 1;  
             }                                                   
             //calculate the luminosity value based on the Y coordinate
             float YPoint = gLumSlider.y * 100;
             if(YPoint > 99.5)
             {
                 lum = llCeil(YPoint);
             }
             else if(YPoint < 0.5)
             {
                 lum = llFloor(YPoint);
             }
             else
             {
                 lum = (integer)YPoint;
             }            
             if(lum == -100)
             {
                 lum = 0;
             }            
             gPreviewColorHSL.z = (lum * 1.0) / 100; //change lum
             //llOwnerSay("LUM IS "+(string)gPreviewColorHSL.z);             
             SetPreviewColor(gPreviewColorHSL);  //change current (preview) color with new lum
        }  
        
        
        else //If the user didn't touch the palette or lum slider, 
        {    //check for other touch locations below . . .
            vector Touched = llDetectedTouchST(0);
            //llOwnerSay("Touched Elsewhere: "+ (string)Touched);
            integer Linkno = llDetectedLinkNumber(0);
            
            //OK button touched
            if (Touched.x > .54476 && Touched.x < .71228 && Touched.y > .03489 && Touched.y < .08287)
            {
                //llOwnerSay("OK Button Touched");
                SendColorInfo("OK"); //Send the color values to a script in the object to be colored 
             }
                
            //CANCEL button touched
            if (Touched.x > .75556 && Touched.x < .91860 && Touched.y > .03489 && Touched.y < .08287)    
            {
               //llOwnerSay("Cancel Button Touched");
               SendColorInfo("CANCEL"); //Send the color values to a script in the object to be colored 
            }
            //SAVE COLOR BUTTON Pressed 
            //If you make design alterations, you may need to change the following the coordinates
            if (Touched.x > .06335 && Touched.x < .21840 && Touched.y > .61755 && Touched.y < .65810)
            {
                //llOwnerSay("Save Color BUTTON Pressed");
                if (gPreviewColorRGB != gSaveColorRGB1)
                {   //Saves the selected color to the top of "Saved Colors" area & existing colors down
                    llSetLinkColor(gSaveColor6,gSaveColorRGB5,ALL_SIDES);
                    gSaveColorRGB6 = gSaveColorRGB5;
                    llSetLinkColor(gSaveColor5,gSaveColorRGB4,ALL_SIDES);
                    gSaveColorRGB5 = gSaveColorRGB4;
                    llSetLinkColor(gSaveColor4,gSaveColorRGB3,ALL_SIDES);
                    gSaveColorRGB4 = gSaveColorRGB3;
                    llSetLinkColor(gSaveColor3,gSaveColorRGB2,ALL_SIDES);
                    gSaveColorRGB3 = gSaveColorRGB2;
                    llSetLinkColor(gSaveColor2,gSaveColorRGB1,ALL_SIDES);
                    gSaveColorRGB2 = gSaveColorRGB1;
                    llSetLinkColor(gSaveColor1,gPreviewColorRGB,ALL_SIDES);                
                    gSaveColorRGB1 = gPreviewColorRGB;
                }
            }
            
            //Next 6 "if's": User has clicked on one of the SAVED COLOR SWATCHES
            //The same function "ColorizeMoveMarkers" is used for all ofthe Save Colors 
            //and the small basic web color swatches
            
            if (Linkno == gSaveColor1) //Save Color #1
            {
                //The following sets the "Current" preview color, tints Lum Slider & moves the markers
                ColorizeMoveMarkers(gSaveColorRGB1);                
             }
             if (Linkno == gSaveColor2)
             {                
                ColorizeMoveMarkers(gSaveColorRGB2);                
             }
             if (Linkno == gSaveColor3)
             {                
                ColorizeMoveMarkers(gSaveColorRGB3);                
             }
             if (Linkno == gSaveColor4)
             {                
                ColorizeMoveMarkers(gSaveColorRGB4);                
             }
             if (Linkno == gSaveColor5)
             {                
                ColorizeMoveMarkers(gSaveColorRGB5);                
             }
             if (Linkno == gSaveColor6)
             {                
                ColorizeMoveMarkers(gSaveColorRGB6);                
             }
             
            //Next 17 "if's": User has clicked on one of the small BASIC WEB COLOR SWATCHES
             
            //User has clicked on the BLACK swatch
            if (Touched.x > .29355 && Touched.x < .31636 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<0,0,0>);                 
            }
            //User has clicked on the GRAY swatch
            if (Touched.x > .32513 && Touched.x < .34754 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<128,128,128>);                 
                
            }//User has clicked on the SILVER swatch
            if (Touched.x > .35673 && Touched.x < .37916 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<192,192,192>);                 
                
            }//User has clicked on the WHITE swatch
            if (Touched.x > .38971 && Touched.x < .41220 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<255,255,255>);                 
                
            }//User has clicked on the RED swatch
            if (Touched.x > .42276 && Touched.x < .44386 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<255,0,0>);                 
                
            }//User has clicked on the MAROON swatch
            if (Touched.x > .45446 && Touched.x < .47562 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<128,0,0>);                 
                
                
            }//User has clicked on the YELLOW swatch
            if (Touched.x > .48756 && Touched.x < .50740 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<255,255,0>);                 
                
            }//User has clicked on the LIME swatch
            if (Touched.x > .52071 && Touched.x < .53926 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<0,255,0>);                 
                
            }//User has clicked on the OLIVE swatch
            if (Touched.x > .55252 && Touched.x < .57507 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<128,128,0>);                 
                
            }//User has clicked on the GREEN swatch
            if (Touched.x > .58569 && Touched.x < .60696 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<0,128,0>);                 
                
            }//User has clicked on the TEAL swatch
            if (Touched.x > .61893 && Touched.x < .64022 && Touched.y > .11679 && Touched.y < .15163)
            {
                llOwnerSay("Teal touched");
                ColorizeMoveMarkers(<0,128,128>);                 
                
            }//User has clicked on the CYAN swatch
            if (Touched.x > .64955 && Touched.x < .67086 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<0,255,255>);                 
                
            }//User has clicked on the BLUE swatch
            if (Touched.x > .68284 && Touched.x < .70150 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<0,0,255>);                 
                
            }//User has clicked on the NAVY swatch
            if (Touched.x > .71484 && Touched.x < .737546 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<0,0,128>);                 
                
            }//User has clicked on the VIOLET swatch
            if (Touched.x > .74557 && Touched.x < .76427 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<148,0,211>);                 
                
            }//User has clicked on the PURPLE swatch
            if (Touched.x > .77898 && Touched.x < .79905 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<128,0,128>);                 
                
            }//User has clicked on the MAGENTA swatch
            if (Touched.x > .80975 && Touched.x < .83117 && Touched.y > .11679 && Touched.y < .15163)
            {
                ColorizeMoveMarkers(<255,0,255>);                 
            }
        }
        
    }
}


Second Script: Colorizer Script – below . . .
(This script goes in the contents of the object that you will be coloring)

//Colorizer Script
//
//Chimera Firecaster
//
//Version 1.0
//April, 2014
//
//A subsidary script to Chimera's Color Picker
//
//Place this script in an object.  It could be clothing, hair, shoes or any object in which
//  you need to change the color.  The main Color Picker script will send this script the
//  RGB color values of the user's chosen color - which will change the color of the object.
//
//If you have left the variable gKeepUpdating (in main Color Picker) set at TRUE.  The color
//  of the object in which this script is placed will keep changing as the user changes colors
//  in the Color Picker.  If, however, you have set gKeepUpdating to FALSE, the color will only 
//  change until the user presses <OK> in the Color Picker. 
//
//This script retains the original color of the object.  If the user has tried several colors
//  and clicks on the "Cancel" button in the Color Picker, this script will return the object's
//  color to the original.  Once the user clicks on the "OK" button in the Color Picker, then
//  that current color becomes the original color.
//
//Note that the gChannel variable, below, must match gChannel in the main Color Picker script/
//
//This script is currently set to change the color on all sides, but it can modified depending
//  upon your needs.  For example, you may wish to color only a certain side or you may wish 
//  to color selected prims in a linked set.
//
//
//
//Global Variables
integer gChannel = -2471717;
vector gOriginalColor;
vector gSaveColor;

default
{
    state_entry()
    {
        gOriginalColor = llGetColor(ALL_SIDES);
        llListen(gChannel, "", "", "");
    }
    
    listen(integer Channel, string Name, key ID, string Msg)
    {
        //Msg has the color and "which-button-pressed" information delimited with brackets:
        //So, it looks like: [<.25,.34,.60>][OK].  The first function places the color
        //(which is element 0) and the button info (element 1) in a list.
        list MsgList = llParseString2List(Msg, [ "[","]" ], []);
        vector vColor = (vector)llList2String(MsgList,0); //extract color from the list
        string WhichButton = llList2String(MsgList,1);   //extract button info from list
        
        //llOwnerSay("vColor: "+(string)vColor);
        
        //Not necessary to change color if it's already the desired color
        if (WhichButton == "CONTINUE") //User hasn't pressed any buttons
        {
           if (vColor != gSaveColor)
           {
                llSetColor(vColor, ALL_SIDES); //Color the object
                gSaveColor = vColor;           //Save the color
           }            
        }         
        
        if (WhichButton == "OK")  //User has pressed the OK button
        {
           if (vColor != gSaveColor)  
           {
                llSetColor(vColor, ALL_SIDES);                
           }
           gOriginalColor = vColor;  //Re-set original color to the new color          
           gSaveColor = vColor;            
        }         
                 
        if (WhichButton == "CANCEL") //User has pressed the Cancel buttojn
        {           
             llSetColor(gOriginalColor, ALL_SIDES); //Return the color to the original         
             gSaveColor = gOriginalColor;
        }                  
                
    }
}

_______

Key Words:  Second Life Scripts, Changing Colors, Replacing Colors, Color Selection, Color Chooser, Color Palette

 

%d bloggers like this: