Sign in to follow this  
Diosces

Nested for loops

Recommended Posts

I'm trying to display a form that toggles thru 256 shades in each primary color through nested for loops. I believe it was working right when I had one nested statement but when I added another I just toggle thru a yellow spectrum. I would build in an echo function to see whats wrong but I'm wiped after work, etc.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            while (Visible)
            {
                for (int c = 0; c < 254 && Visible; c++)
                {
                    Application.DoEvents();
                    System.Threading.Thread.Sleep(3);
                    this.BackColor = Color.FromArgb(254 - c, c, c);
                    for (int a = 0; a < 254 && Visible; a++)
                    {
                        Application.DoEvents();
                        System.Threading.Thread.Sleep(3);
                        this.BackColor = Color.FromArgb(254 - c, 254 - a, c);
                        for (int b = 0; b < 254 && Visible; b++)
                        {
                            Application.DoEvents();
                            System.Threading.Thread.Sleep(3);
                            this.BackColor = Color.FromArgb(254 - c, 254 - a, 254-b);
                        }
                    }
                }


            }
        }
    }
}



Hints or answers to what went wrong on adding the second nested statement is greatly appreciated.

Share this post


Link to post
Share on other sites
this.BackColor = Color.FromArgb(254 - c, c, c);
Try deleting this line, and the one inside the second loop, and only putting it inside the third loop. Also, why are you using c c c as the values? surely it should be a,b,c all the way? I might have misunderstood that part, however.

Also, the blue element is changing pretty rapidly, the green element not so fast, and the red element slowest. If the sleep(3) means 3 milliseconds, then the algorythm is biased towards the red end of the spectrum (since the red values are more static than the other two).
This is because the speed of the inner two loops means you only see the AVERAGE values of red and blue, due to screen response time and the fact that 3ms is too fast for the human eye to detect.

If you want to go through the entire spectrum, you need to use a different system altogether, with some for loops working in series:

START: R=255 G=0 B=0
LOOP 1: green value increases until R=255 G=255 B=0
LOOP 2: red value decreases until R=0 G=255 B=0
LOOP 3: blue value increases until R=0 G=255 B=255
LOOP 4: green value decreases until R=0 G=0 B=255
LOOP 5: red value increases until R=255 G=0 B=255
LOOP 6: blue value decreases until R=255 G=0 B=0

This way you will go through the entire spectrum in the order order red>orange>yellow>greenishyellow>green>cyan>blue>purple>magenta. You can modify the overall saturation of colour by reducing the lower number. (here it is 0)

It may be an idea to pre-calculate these values.

Share this post


Link to post
Share on other sites
Quote:
Original post by Diosces
I'm trying to display a form that toggles thru 256 shades

Funny, your code says 254 instead of 256.

Anyway, here is a solution with only one for loop (untested, but you get the idea):


for (int i = 0xffffff; i >= 0 && Visible; --i)
{
int r = i & 0xff;
int g = (i >> 8) & 0xff;
int b = i >> 16;

Application.DoEvents();
System.Threading.Thread.Sleep(3);
this.BackColor = Color.FromArgb(r, g, b);
}



Let me know if this works for you.

Share this post


Link to post
Share on other sites
Guys thanks for the replies.

Species:
I hear what you're saying about the unequal delay.
As long as each color change is incremented every 3 ms I feel I might have gotten close to what I initially was shooting for. As it's written do you think I get 1) blue completely cycled through every 254x254 (65,025) times for every increment of red 2) green cycled through every 255 times for every increment of red?

Devfred:
You soultion is simple elegance. Unfortunately as a beginner I can't grasp the logic. It's early morning so maybe with a few cups of coffee+a triple esspresso maybe start to understand.

Thanks again

Share this post


Link to post
Share on other sites
Could you test if the following, simpler code also works?


for (int i = 0xffffff; i >= 0 && Visible; --i)
{
Application.DoEvents();
System.Threading.Thread.Sleep(3);
this.BackColor = Color.FromArgb(i);
}


Share this post


Link to post
Share on other sites
Quote:
Original post by DevFred
Could you test if the following, simpler code also works?

*** Source Snippet Removed ***


I used DevFred's method for colouring some boxes in a physics simulation, and it works nicely. My method is designed to transform smoothly between the colours, and this DevFred's first method works by turning the channels on and off.

DevFred, I think your simplified method will look the same as the previous one. Your first method works nicely, however.


Share this post


Link to post
Share on other sites
First, note the repetitiveness of the original code. The repetition comes from treating one iteration of each outer loop as somehow "special". Also, we can fix the bounds of the loops. All we want is for each value to range from 0 to 255, inclusive. That means we logically should start counting from 0, while < 256 (notice, one more than the last value, because of the strict inequality). Then, the colour values will be 255 - whatever: 255 - 0 = 255, and 255 - 255 = 0, so this will produce the whole valid range of values - from 255 to 0, inclusive.


private void button1_Click(object sender, EventArgs e)
{
while (Visible)
{
for (int c = 0; c < 255 && Visible; c++)
{
for (int a = 0; a < 255 && Visible; a++)
{
for (int b = 0; b < 255 && Visible; b++)
{
Application.DoEvents();
System.Threading.Thread.Sleep(3);
this.BackColor = Color.FromArgb(255 - c, 255 - a, 255 - b);
}
}
}
}
}


Next, we note that a simple 'return' can eliminate the repeated checks for 'Visible'ity:



private void button1_Click(object sender, EventArgs e)
{
while (true)
{
for (int c = 0; c < 255; c++)
{
for (int a = 0; a < 255; a++)
{
for (int b = 0; b < 255; b++)
{
Application.DoEvents();
System.Threading.Thread.Sleep(3);
this.BackColor = Color.FromArgb(255 - c, 255 - a, 255 - b);
if (!Visible) return;
}
}
}
}
}


Now, we note that Color.FromArgb() is also overloaded to take one int argument, a single "packed" colour value. We are in effect "counting down" through all the possible colours: in the ARGB representation, the blue byte is least significant. The largest colour value we want to use is the one corresponding to a = 0, r = 255, g = 255, b = 255. If you look at that as a base -256 number, you can see what it equals: the same as FFFFFF in hex (since two hex digits correspond exactly to one base-256 digit). The smallest is zero, of course.


private void button1_Click(object sender, EventArgs e)
{
while (true)
{
for (int colour = 0xffffff; colour >= 0; --colour)
{
Application.DoEvents();
System.Threading.Thread.Sleep(3);
this.BackColor = Color.FromArgb(colour);
if (!Visible) return;
}
}
}


Finally, we can set up the colour to loop around indefinitely, thus making the while loop redundant. We can do that by not having a loop-exit condition in the for loop, and using some modulus arithmetic in the per-loop change. However, now we can simply move our explicit loop exit (the Visible check) back into the for-initializer, since there is only one place we want to check. That's awkward, though: the loop exit has nothing to do with our "counter" variable, so it's a highly nonstandard way to use the for loop construct. Instead, we just use a while loop, initializing the "counter" outside.


private void button1_Click(object sender, EventArgs e)
{
int colour = 0xffffff;
while (Visible)
{
Application.DoEvents();
System.Threading.Thread.Sleep(3);
this.BackColor = Color.FromArgb(colour);
colour = (colour + 0xffffff) % 0x1000000); // or & 0xffffff, if you prefer
}
}

Share this post


Link to post
Share on other sites
Man, You guys rock! Thanks!

I'm trying to make the leap from simple statements to the more simple, more elegant(efficient) manner of doing something.

Devfred your stuff works

I definitely get most of it~70% and will study the feedback till I'm satisfied I can apply the solution methods to other, similar problems.

I especially dig Zahlman's stepped way of teaching how to 'distill'(removing the repetitiveness) to a final solution.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman
First, note the repetitiveness of the original code. The repetition comes from treating one iteration of each outer loop as somehow "special". Also, we can fix the bounds of the loops. All we want is for each value to range from 0 to 255, inclusive. That means we logically should start counting from 0, while < 256 (notice, one more than the last value, because of the strict inequality). Then, the colour values will be 255 - whatever: 255 - 0 = 255, and 255 - 255 = 0, so this will produce the whole valid range of values - from 255 to 0, inclusive.


private void button1_Click(object sender, EventArgs e)
{
while (Visible)
{
for (int c = 0; c < 255 && Visible; c++)
{
for (int a = 0; a < 255 && Visible; a++)
{
for (int b = 0; b < 255 && Visible; b++)
{
Application.DoEvents();
System.Threading.Thread.Sleep(3);
this.BackColor = Color.FromArgb(255 - c, 255 - a, 255 - b);
}
}
}
}
}


Next, we note that a simple 'return' can eliminate the repeated checks for 'Visible'ity:



private void button1_Click(object sender, EventArgs e)
{
while (true)
{
for (int c = 0; c < 255; c++)
{
for (int a = 0; a < 255; a++)
{
for (int b = 0; b < 255; b++)
{
Application.DoEvents();
System.Threading.Thread.Sleep(3);
this.BackColor = Color.FromArgb(255 - c, 255 - a, 255 - b);
if (!Visible) return;
}
}
}
}
}


Now, we note that Color.FromArgb() is also overloaded to take one int argument, a single "packed" colour value. We are in effect "counting down" through all the possible colours: in the ARGB representation, the blue byte is least significant. The largest colour value we want to use is the one corresponding to a = 0, r = 255, g = 255, b = 255. If you look at that as a base -256 number, you can see what it equals: the same as FFFFFF in hex (since two hex digits correspond exactly to one base-256 digit). The smallest is zero, of course.


private void button1_Click(object sender, EventArgs e)
{
while (true)
{
for (int colour = 0xffffff; colour >= 0; --colour)
{
Application.DoEvents();
System.Threading.Thread.Sleep(3);
this.BackColor = Color.FromArgb(colour);
if (!Visible) return;
}
}
}


Finally, we can set up the colour to loop around indefinitely, thus making the while loop redundant. We can do that by not having a loop-exit condition in the for loop, and using some modulus arithmetic in the per-loop change. However, now we can simply move our explicit loop exit (the Visible check) back into the for-initializer, since there is only one place we want to check. That's awkward, though: the loop exit has nothing to do with our "counter" variable, so it's a highly nonstandard way to use the for loop construct. Instead, we just use a while loop, initializing the "counter" outside.


private void button1_Click(object sender, EventArgs e)
{
int colour = 0xffffff;
while (Visible)
{
Application.DoEvents();
System.Threading.Thread.Sleep(3);
this.BackColor = Color.FromArgb(colour);
colour = (colour + 0xffffff) % 0x1000000); // or & 0xffffff, if you prefer
}
}


Wow, I certainly learned something from this. Its always good when somebody takes the time to share knowledge. Thanks Zahlman.

Share this post


Link to post
Share on other sites
Zalman

I think I got all of it can you just explain the math in this part?
I know 0x specifies take the hex string and convert to an int,
how it's incremneting looks like white value is added to white value ffffff is being added to ffffff in each loop..



colour = (colour + 0xffffff) % 0x1000000); // or & 0xffffff, if you prefer

Share this post


Link to post
Share on other sites
Modulo arithmetic. (x - 1) is equivalent to -1, mod x. I used this instead of -1 directly because of a habit of avoiding negative numbers in these calculations: if I just subtracted each time, when the value looped around to -1, it wouldn't necessarily get converted properly, since IIRC the C++ % operator doesn't necessarily work as described on the Wikipedia page for negative numbers.

Also, y % x is equivalent to y & (x - 1), if and only if x is a (nonnegative, integral) power of two.

Share this post


Link to post
Share on other sites
Ug, that divide by 0x1000000 is bugging me. I'm not near a computer that has a C# compiler so I can't test the code. Im assuming the first increment is 16777215/16777216 ? a modulo would return ?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this