В мир информатики # 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 Автор
использует язык программирования Паскаль.
Подробные комментарии к программе позволят вам
подготовить соответствующую программу и на
другом языке. — Ред. |