© Данная статья была опубликована в № 10/2007 журнала "Информатика" издательского дома "Первое сентября". Все права принадлежат автору и издателю и охраняются.      Главная страница "Первого сентября"
     Главная страница журнала "Информатика"
     Содержание № 10/2007
 В мир информатики # 90 (16-30 апреля). Пиксельный “сканер”


В мир информатики # 92 (16–31 мая).
Школа программирования

Пиксельный “сканер”

О.В. Трушин,
г. Уфа

В предыдущей статье [1] описывался принцип отображения на экране монитора математических зависимостей с помощью генератора пикселей с их фильтрацией. Попробуем применить другой подход — “сканирование” всего массива пикселей экрана и отображение требуемых зависимостей.

Для решения задачи нам понадобится функция, устанавливающая цвет пикселя с координатами x, y1:

Function Color(x, y: word): byte;

Begin

Color := …

End;

— где на месте многоточия будет записана та или иная зависимость.

Определим также процедуру Scan, выводящую пиксели, цвет которых задает функция Color, на экране размером xm ґ ym:

Procedure Scan;

Begin

{Перебор координат экрана}

For x := 0 to xm do

For y := 0 to ym do

PutPixel(x, y, Color(x, y))

End;

В основной программе осталось лишь задать параметры и вызвать процедуру Scan:

Uses Crt, Graph;

Var xm, ym, x, y: word; Gd, Gm: integer;

… {Описание процедур}

BEGIN

{Инициализируем графический режим}

Gd := 0; InitGraph(Gd, Gm, '');

{Определяем максимальные размеры экрана}

xm := GetMaxX; ym := GetMaxY;

{Вызываем процедуру сканирования}

Scan;

ReadKey

END.

Оформив функцию Color соответствующим образом, можно отобразить некоторые классические зависимости, например:

1) прямую (ax + by = c):

Color := (2 * x + 3 * y) div 100;

2) эллипс ((x/a)2 + (y/b)2 = 1):

Color := Round(Sqr((x — xc)/100) + Sqr((y — yc)/70));

3) гиперболу (x Ч y = c):

Color := Abs((x — xc) * (y — yc)) div 4000;

4) так называемую “астроиду” (xa + ya = ra,
0 < a < 1):

Color := Round(Sqrt(Abs(x — xc)) + Sqrt(Abs(y — yc)));

5) прямоугольники размером 30 х 40:

If (x mod 60 < 30) and (y mod 80 < 40)

Then Color := 10 Else Color := 0;

В этих формулах xc, yc — начало координат для функций (например, центр экрана), а величина r и постоянные коэффициенты определяют параметры функциональных зависимостей и частоту смены цветов.

Попытаемся сканированием построить график конкретной функции, например, окружности. Для этого введем функцию F:

Function F(x, y: word): real;

Begin

F := Sqr(x — xc) + Sqr(y — yc)

End;

— и процедуру Found отображения окружности (что соответствует условию F = z) цветом Color:

Procedure Found(z: real; color: byte);

Begin

For x := 0 To xm Do {Перебор столбцов}

{Перебор пикселей столбца}

For y := 0 To ym Do

If z = F(x, y)

Then PutPixel(x, y, Color)

End;

При этом вместо процедуры Scan в основной программе вызовем процедуру Found(Sqr(200.0), 14) для построения окружности радиусом 200 пикселей. Увы, при условии точного равенства z = F(x, y) мы получим очень мало точек. Усовершенствуем условие нахождения точек — на паре соседних точек будем определять момент, когда отклонение от точного значения меняет знак. Для этого заменим условие z = F(x, y) на следующее:

If (z <= F(x, y)) And (z >= F(x, y + 1))

Or (z >= F(x, y)) And (z <= F(x, y + 1))

График получился, но не совсем удачный — на вертикальных отрезках видны разрывы. Это происходит потому, что сканирование экрана идет по столбцам (внутренний цикл по y), и некоторые строки остаются пустыми. Придется прогнать наш “сканер” повторно, но уже по строкам. Добавляем в процедуру Found операторы:

For y := 0 To ym Do

For x := 0 To xm Do {Перебор пикселей строки}

If (z <= F(x, y)) And (z >= F(x + 1, y))

Or (z >= F(x, y)) And (z <= F(x + 1, y))

Then PutPixel(x, y, Color);

Описанная методика хороша для отображения общих функциональных зависимостей, но плохо подходит для вывода конкретных графиков. Сканер работает медленно, всего проводится xm ? ym = 640 ? 480 ~ 3 · 105 проверок в два прохода. Но для сложных задач это зачастую единственный метод. Например, нужно построить эквипотенциальные поверхности электростатического поля от нескольких заряженных шаров:

Поле создается набором заряженных шаров q[i] с радиусами rq[i] и центрами в точках xq[i], yq[i]. Добавляем описания переменных:

Var xq, yq, rq, q: Array[1..5] of real;

i, k, n: integer; {Счетчики}

Emax, m: real; {Максимум поля и масштаб}

Расчет величины поля Е в точке x, y будем проводить суммированием его составляющих Ex[i], Ey[i] по осям координат от k зарядов. Величина поля, создающегося зарядом q на расстоянии r, определяется как q/r2, и для получения составляющих по осям x, y надо ввести множители dx/r, dy/r. Причем внутри заряженных шаров величина поля равна нулю. Опишем расчет потенциала поля функцией E:

Function E(x, y: real): real;

Var Ex, Ey, dx, dy, r: real;

i: integer; space: boolean;

Begin

{Признак нахождения точки с координатами x, y вне шара}

space := true;

Ex := 0; Ey := 0; {Начальные значения}

For i := 1 To k Do {Перебор зарядов}

Begin

{Разности координат точки x, y и центра шара}

dx := x — xq[i];

dy := y — yq[i];

{Расстояние от точки до центра шара}

r := Sqrt(Sqr(dx) + Sqr(dy));

{Если точка расположена внутри одного из заряженных шаров, то поле равно нулю, и цикл с перебором зарядов прекращаем}

If r < rq[i] Then

Begin space := false; Break End;

{Суммирование полей зарядов}

Ex := Ex + q[i] * dx/(r * r * r);

Ey := Ey + q[i] * dy/(r * r * r)

End;

{Значение функции}

If space Then E := Sqrt(Sqr(Ex) + Sqr(Ey))

Else E := 0

End;

При этом функциональную зависимость для значений поля (Function F) определяем с учетом масштаба m для перевода метров в пиксели:

F := E(x/m, y/m)

В основной программе назначаем число зарядов k, их характеристики и сканируем n значений поля:

…

k := 2; {Число зарядов}

xq[1] := 0.10; yq[1] := 0.12;

rq[1] := 0.01; q[1] := -20;

xq[2] := 0.23; yq[2] := 0.12;

rq[2] := 0.02; q[2] := 40;

{Масштаб для ширины монитора 0,3 метра}

m := xm/0.3;

For i := 1 To k Do

{Рисуем заряженные шары}

Circle(Round(xq[i] * m), Round(yq[i] * m),

Round(rq[i] * m));

{Определяем максимум отображаемого значения поля, взяв значение вблизи первого заряда}

Emax := E(xq[1] — 1.01 * rq[1],

yq[1] — 1.01 * rq[1]);

n := 8; {Число отображаемых значений поля}

For i := 1 To n Do

Found(Sqr(i/n * Emax, 16 — i);

ReadKey

END.

Справедливости ради заметим, что из-за огромного числа вычислений построение линий поля проходит достаточно медленно. КПД нашего сканера функций очень низкий, а как его увеличить — это очередная классическая задача, но уже из области профессионального программирования.

Литература

1. Трушин О.В. Генератор массива пикселей. / “В мир информатики” № 90 (“Информатика” № 8/2007).


1 Автор использует язык программирования Паскаль. Подробные комментарии к программе позволят вам подготовить соответствующую программу и на другом языке. — Ред.