简体   繁体   中英

How to draw a rainbow line using Graphic class c#

So i'm trying to create infinite rainbow line. Here ColorUtils class How do I get a rainbow color gradient in C#? (I'm using Framework 4.7.3

    int rainbowStage = 0;
    int rainbowNext = 0;

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        BackgroundWorker worker = new BackgroundWorker();

        PointF point1 = new PointF(100.0F, 100.0F);
        PointF point2 = new PointF(235.0F, 100.0F);

        worker.DoWork += async delegate (object s, DoWorkEventArgs args)
        {
            do
            {
                Console.WriteLine(await getRainbow());
                rainbowNext++;
                Pen pen = new Pen(await getRainbow(), 3);
                e.Graphics.DrawLine(pen, point1, point2);
                e.Graphics.Clear(Color.FromArgb(26, 26, 26));
            } while (rainbowStage == rainbowNext);
        };

        worker.RunWorkerCompleted += delegate (object s, RunWorkerCompletedEventArgs args)
        {
            rainbowStage++;
        };
    }

    async Task<ColorUtils.ColorRGB> getRainbow()
    {
        for (double i = 0; i < 1; i += 0.01)
        {
            ColorUtils.ColorRGB c = ColorUtils.HSL2RGB(i, 0.5, 0.5);
            return c;
        }
        ColorUtils.ColorRGB c1 = ColorUtils.HSL2RGB(0, 0.5, 0.5);
        return c1;
    }

So, you have found how to generate a sequence of rainbow colors. But your code is not actually correct, it should be something like:

IEnumerable<Color> GetRainbow()
{
    for (double i = 0; i < 1; i += 0.1)
    {
        Color c = ColorUtils.HSL2RGB(i, 0.5, 0.5);
        yield return c;
    }
}

That will give you a sequence of ten colors, using the actual color type used by winforms.

To draw the rainbow we need to draw each color as a separate line, slightly offset from each other. To get the offset we need to do some vector math:

var dir = point1 - point2;
var offset= new PointF(dir.Y, -dir.X).Normalize();
...
public static PointF Normalize(this PointF A)
{
    float distance = Math.Sqrt(A.X * A.X + A.Y * A.Y);
    return new PointF(A.X / distance, A.Y / distance);
}
public static PointF Mul(this PointF a, float b)
{
    return new PointF(a.X * b, a.Y * b);
}

We can then begin drawing our line:

var colors = GetRainbow().ToList();
var width = 10f / colors.Count;
for(int i = 0; i < colors.Count; i++){
    using var pen = new Pen(colors[i], width);
    var o = offset.Mul( width * i)
     e.Graphics.DrawLine(pen, point1 + o, point2 + o);
}

That should give you a rainbow line of with 10 with one pixel per color, adjust the width literal to get a wider line.

An alternative approach would be to create something like a texture brush displaying the rainbow and use that for drawing.

Note that all drawing code need to be run on the UI thread, and any other calculations are really fast, they just involve a handful of simple math operations. So there is nothing at all to be gained from trying to run anything on any background thread.

Here's a copy, paste, run, version of what you're trying to do:

public class Form1 :  Form
{
    public Form1()
    {
        this.Paint += Form1_Paint;
    }
    
    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        foreach (var rgb in GetRainbow().Select((colour, indexer) => (colour, indexer)))
        {
            Console.WriteLine(rgb.colour);
            using (Pen pen = new Pen(rgb.colour, 3))
            {
                e.Graphics.DrawLine(pen, new PointF(100.0F, 100.0F + rgb.indexer), new PointF(235.0F, 100.0F + rgb.indexer));
            }
        }
    }

    IEnumerable<ColorRGB> GetRainbow()
    {
        for (double i = 0; i < 1; i += 0.01)
        {
            yield return HSL2RGB(i, 0.5, 0.5);
        }
    }

    public struct ColorRGB
    {
        public byte R;
        public byte G;
        public byte B;

        public ColorRGB(Color value)
        {
            this.R = value.R;
            this.G = value.G;
            this.B = value.B;
        }

        public static implicit operator Color(ColorRGB rgb)
        {
            Color c = Color.FromArgb(rgb.R, rgb.G, rgb.B);
            return c;
        }

        public static explicit operator ColorRGB(Color c)
        {
            return new ColorRGB(c);
        }
    }

    public static ColorRGB HSL2RGB(double h, double sl, double l)
    {
        double v;
        double r, g, b;

        r = l;   // default to gray
        g = l;
        b = l;
        v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl);
        if (v > 0)
        {
            double m;
            double sv;
            int sextant;
            double fract, vsf, mid1, mid2;

            m = l + l - v;
            sv = (v - m) / v;
            h *= 6.0;
            sextant = (int)h;
            fract = h - sextant;
            vsf = v * sv * fract;
            mid1 = m + vsf;
            mid2 = v - vsf;
            switch (sextant)
            {
                case 0:
                    r = v;
                    g = mid1;
                    b = m;
                    break;
                case 1:
                    r = mid2;
                    g = v;
                    b = m;
                    break;
                case 2:
                    r = m;
                    g = v;
                    b = mid1;
                    break;
                case 3:
                    r = m;
                    g = mid2;
                    b = v;
                    break;
                case 4:
                    r = mid1;
                    g = m;
                    b = v;
                    break;
                case 5:
                    r = v;
                    g = m;
                    b = mid2;
                    break;
            }
        }
        ColorRGB rgb;
        rgb.R = Convert.ToByte(r * 255.0f);
        rgb.G = Convert.ToByte(g * 255.0f);
        rgb.B = Convert.ToByte(b * 255.0f);
        return rgb;
    }
}

When run, I get this:

表格1

There were too many things going wrong with your original code, but don't use async / await , don't forget to dispose disposables, don't forget to compute the actual line you're trying to draw, use yield return to get your rainbow colours, etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM