|
|
Back in the early 1990s, when I was in college and new to computer graphics, I came up
with an interesting pattern-drawing algorithm. The algorithm is quite simple (its logic fits in just 5 lines
of C code), and you can see the result as the background image of this page.
The idea to draw such a pattern came from a
magazine I had in my hands one day. One page in it had a simple, but delicate background: on a light-gray
surface there were thin, snake-shaped, shaded short lines, generally going up and right.
Of course, a seasoned computer graphics programmer would try to 'model' this: sprinkle a set of points that
would 'start' snakes and draw lines going in a general 'up-right' direction, shading them.
This would produce the desired image.
I, on the other hand, ended up with an entirely different method that is far simpler and produces
exactly what I had in mind. I decided to make it public, because I have not seen anything like it
in computer graphics literature, or online. Here is the original code:
|
#define LIGHT_GRAY 7
#define DARK_GRAY 8
#define WHITE 15
void draw_surface (int iLeft,int iTop, int iRight, int iBottom)
{
register int x, y;
int iWidth = iRight - iLeft;
int iHeight = iBottom - iTop;
// fill area with light-gray color
setfillstyle (SOLID_FILL, LIGHT_GRAY);
// loop untill interrupted
while (!kbhit())
{
x = rand() % iWidth + iLeft;
y = rand() % iHeight + iTop;
if (LIGHT_GRAY == getpixel(x, y))
{
if (!(DARK_GRAY == getpixel(x, y-1) || DARK_GRAY == getpixel(x+1, y-1) ||
DARK_GRAY == getpixel(x-1, y-1) || DARK_GRAY == getpixel(x-1, y)))
{
if (WHITE != getpixel(x+1, y+1))
{
putpixel (x, y, WHITE);
putpixel (x+1, y+1, DARK_GRAY);
}
}
}
}
}
|
Of course, the three if statements
can be combined into just one long one.
I separated them, to make the code look simpler.
Here is the logic.
After filling the surface with a base light-gray color, the program goes into a loop,
producing random (x,y) pairs and checking if the (x,y) point is still light-gray.
If yes, it checks four neighboring points, three above
(x,y): (x-1,y-1),(x,y-1),(x+1,y-1)
and one to the left (x-1,y). If none of them is shaded (painted with dark-gray), it
performs yet another check, of the point (x+1,y+1) for being not white. If this condition is met,
it paints (x,y) with white and
(x+1,y+1) with the shade (dark-gray) color.
This code can be modified a little to produce patterns that are a bit different.
Just adding another (fifth) point to the second if statement that checks
(x-1,y+1) slightly changes the general direction of the pattern snakes.
Alternatively, removing the (x+1,y-1) point from the same
if statement produces similar result, but
pattern snakes fill the surface somewhat more densely.
|
|
|
|
|
|
4-point check |
|
5-point check |
|
3-point check |
|
As the loop progresses, the picture gets closer to a
certain 'final' state,
when not a single (x,y) point will meet all three conditions.
This fact can be used to make the decision when to end the loop, rather than wait for a keystroke
as in the code above.
|
void draw_surface (int iLeft,int iTop, int iRight, int iBottom)
{
register int x, y;
unsigned int iCnt = 0;
int iWidth = iRight - iLeft;
int iHeight = iBottom - iTop;
// iGraceCount is max number of unsuccessful tries
// before loop is ended. It should depend on the area:
unsigned int iGraceCount = (iWidth * iHeight)/2;
// fill area with light-gray color
setfillstyle (SOLID_FILL, LIGHT_GRAY);
// main loop
while (iGraceCount > iCnt)
{
x = rand() % iWidth + iLeft;
y = rand() % iHeight + iTop;
iCnt++;
if (LIGHT_GRAY == getpixel(x, y))
{
if (!(DARK_GRAY == getpixel(x, y-1) || DARK_GRAY == getpixel(x+1, y-1) ||
DARK_GRAY == getpixel(x-1, y-1) || DARK_GRAY == getpixel(x-1, y)))
{
if (WHITE != getpixel(x+1, y+1))
{
putpixel (x, y, WHITE);
putpixel (x+1, y+1, DARK_GRAY);
iCnt = 0;
}
}
}
}
}
|
Statistically, the total number of changes depends (linearly) on the area, and slightly on the
rand() function used
in the algorithm.
Tests show that in the case of a 4-point check (inside the second if
statement) the total number of 'hits'
(when all 3 conditions are met) is slightly above 30% of the total number of points in the area,
while in the case of a 5-point check the number is around 28%, and with a 3-point check this number
is around 32%. This difference is logical: the more conditions you set for a point,
the fewer will meet all of them.
Finally, in order to remove the border seams, the code has to
treat pixels to the right from the right edge of the area as if they were pixels on the left
side, and pixels below the bottom, as if they were pixels on the top. The following code paints
an area 200x200 pixels, and returns the number pixels painted white:
|
#define LEFT_CORNER 100
#define TOP_CORNER 100
#define AREA_WIDTH 200
#define AREA_HEIGHT 200
int VirtGetPixel (int x, int y)
{
if (x < LEFT_CORNER) x += AREA_WIDTH;
if (y < TOP_CORNER) y += AREA_HEIGHT;
if (x >= LEFT_CORNER + AREA_WIDTH) x -= AREA_WIDTH;
if (y >= TOP_CORNER + AREA_HEIGHT) y -= AREA_HEIGHT;
return getpixel (x, y);
}
void VirtSetPixel (int x, int y, int color)
{
if (x < LEFT_CORNER) x += AREA_WIDTH;
if (y < TOP_CORNER) y += AREA_HEIGHT;
if (x >= LEFT_CORNER + AREA_WIDTH) x -= AREA_WIDTH;
if (y >= TOP_CORNER + AREA_HEIGHT) y -= AREA_HEIGHT;
putpixel (x, y, color);
}
unsigned int draw_surface (void)
{
register int x, y;
unsigned int iCnt = 0;
unsigned int iGraceCount = 0;
unsigned int iWhiteCnt = 0;
// iGraceCount is max number of unsuccessful tries
// before loop is ended. It should depend on the area:
iGraceCount = (AREA_WIDTH * AREA_HEIGHT) / 2;
// fill area with light-gray color
setfillstyle (SOLID_FILL, LIGHT_GRAY);
// main loop
while (iGraceCount > iCnt)
{
x = rand() % AREA_WIDTH + LEFT_CORNER;
y = rand() % AREA_HEIGHT + TOP_CORNER;
iCnt++;
if (LIGHT_GRAY == VirtGetPixel(x, y))
{
if (!(DARK_GRAY == VirtGetPixel (x, y-1) ||
DARK_GRAY == VirtGetPixel (x+1, y-1) ||
DARK_GRAY == VirtGetPixel (x-1, y-1) ||
DARK_GRAY == VirtGetPixel (x-1, y)))
{
if (WHITE != VirtGetPixel(x+1, y+1))
{
VirtPutPixel (x, y, WHITE);
VirtPutPixel (x+1, y+1, DARK_GRAY);
iCnt = 0;
iWhiteCnt++;
}
}
}
}
return iWhiteCnt ;
}
|
Yuri Yakimenko, |
|