Color Table Algorithm

前端 未结 2 1994
傲寒
傲寒 2020-12-21 16:03

I\'ve been searching for just over a week now on how to create something like this.

I have the code and for loops to create each Panel by X, and Y coords a

相关标签:
2条回答
  • 2020-12-21 16:28

    For best control I suggest using a color calculation function.

    There are many out there; here is one I use:

    Color HsvToRgb(double h, double S, double V)
    {
        /// Convert HSV to RGB
        /// h is from 0d - 360d
        /// s,v values are 0d - 1d
        /// r,g,b values are 0 - 255
    
        int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
        double f = hue / 60 - Math.Floor(hue / 60);
    
        value = value * 255;
        int v = Convert.ToInt32(value);
        int p = Convert.ToInt32(value * (1 - saturation));
        int q = Convert.ToInt32(value * (1 - f * saturation));
        int t = Convert.ToInt32(value * (1 - (1 - f) * saturation));
    
        if      (hi == 0)  return Color.FromArgb(255, v, t, p);
        else if (hi == 1)  return Color.FromArgb(255, q, v, p);
        else if (hi == 2)  return Color.FromArgb(255, p, v, t);
        else if (hi == 3)  return Color.FromArgb(255, p, q, v);
        else if (hi == 4)  return Color.FromArgb(255, t, p, v);
        else               return Color.FromArgb(255, v, p, q);
    }
    

    Do note the input ranges!!

    Now it is easy to setup a Color array at class level:

    int width = 10;
    int height = 9;
    Color[,] colors;
    

    And fill it:

    void loadColors()
    {
        colors = new Color[width, height];
    
        // load greys
        for (int i = 0; i < width; i++ ) colors[i, 0] = HsvToRgb(0f, 0f, 1f * i / width);
        // load bright stripe:
        for (int i = 0; i < width; i++) colors[i, 1] = HsvToRgb(i* 360f / width, 0.33f, 1f);
        // load matrix:
        for (int j = 2; j < height; j++)
            for (int i = 0; i < width; i++) 
                 colors[i, j] = HsvToRgb(i * 360f / width, 1f, 1f * (height - j + 2) / height);
    }
    

    From this is is a snap to set the BackColors of your Panels.

    Here is a Form.Paint function, I used to create the above screenshot:

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        int w = ClientSize.Width / width;
        int h = ClientSize.Height / height;
    
        for (int j = 0; j < height; j++) 
            for (int i = 0; i < width; i++)
            {
                using (SolidBrush brush = new SolidBrush(colors[i,j]))
                e.Graphics.FillRectangle(brush, i * w, j * h, w, h);
            }
    }
    

    Of course it is as simple as changing two numbers to make a finer grid, here 20x20:

    Also note how the even spacing of hues doesn't really work well, as neither the human eye nor our common display systems are equally sensitive to changes in hues across the spectrum..

    The eye is actually rather sensitive to greenish hues

    the just-noticeable difference in wavelength varies from about 1 nm in the blue-green and yellow wavelengths, to 10 nm and more in the longer red and shorter blue wavelengths

    but our monitors do a pretty bad job at creating different green hues..

    Using an adapted list of perceptionally evenly spaced hues might help, depending on what you want..

    Using this one-liner:

    private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
    {
        int hue = (int) ((Bitmap)pictureBox1.Image).GetPixel(e.X, e.Y).GetHue();
    }
    

    on the image above gives us one such list of hues:

    20 32 41 50 58 72 133 163 170 177 183 190 197 206 269 288 307 324 334 346 
    

    I have modified it a little, maybe to make it work better with my monitor:

    List<int> hues = new List<int> 
    { 20, 32, 41, 50, 58, 72, 133, 162,  180, 188, 195,  205, 215, 223, 246, 267, 288, 300, 320, 346 };
    

    And changing the above code (keeping width = 20) to

    HsvToRgb(hues[i],..
    

    results in this:

    Update: I have replaced the HsvToRgb function by a greatly simplified one.

    0 讨论(0)
  • 2020-12-21 16:36

    With this answer you'll get the possibility to use the HSB representation of a color (maybe take also a look here).

    By using this you can for the first row use a random hue, a zero saturation and a brightness going from 1.0 to 0.0 in the amount of needed steps. For the other rows you have to take 1.0 for the saturation and increase the hue from 0 to 360 in the same amount of steps and also increase the saturation from 0.0 to 1.0 per row.

    I just put an example together:

    private void OnResize(object sender, EventArgs e)
    {
        Invalidate();
    }
    
    protected override void OnPaint(PaintEventArgs e)
    {
        UpdateImage(e.Graphics);
    }
    
    private void UpdateImage(Graphics graphics)
    {
        var columns = 10;
        var rows = 8;
    
        var hueSteps = 360 / columns;
        var columnSteps = 1.0F / columns;
    
        var width = Width / columns;
        var height = Height / (rows + 1);
    
        for (int i = 0; i < columns; i++)
        {
            var gray = ColorExtensions.FromAhsb(255, 0, 0, columnSteps * i);
    
            using (var brush = new SolidBrush(gray))
            {
                graphics.FillRectangle(brush, width * i, 0, width, height);
            }
        }
    
        for (int i = 0; i < columns; i++)
        {
            for (int j = 1; j <= rows; j++)
            {
                var color = ColorExtensions.FromAhsb(255, hueSteps * i, 1, columnSteps * j);
    
                using (var brush = new SolidBrush(color))
                {
                    graphics.FillRectangle(brush, width * i, height * j, width, height);
                }
            }
        }
    }
    

    The result is not exact the same, but that would be just a matter of re-arrange the loops:

    0 讨论(0)
提交回复
热议问题