Условие: песочные часы с высыпающимся песком
Описание алгоритма работы: Для начала рисуются две пересекающиеся прямые, обозначающие собой проекции
конусов песочных часов. Находятся уравнения этих прямых L1 и L2 как уравне-
ния прямой, проходящей через две точки. В данном случае уравнения прямых
имеют вид: L1 x = (2*y-550)/(-3), L2 x = (2*y+50)/3. Уравнения выражены
через Y - это сделает программирование более удобным, т.к. y-овая координа-
та нам известна. Тогда в цикле заполяется песком простанство от A(L1(Y), Y)
до B(L2(Y), Y). Значения Y берутся в зависимости от заполняемого конуса:
верхний конус заполняется "черным песком", т.о. мы стираем изображение и
соответственно добавляется такое же кол-во песка в нижний конус. В большом
цикле при заполении верхнего конуса Y координата увеличивается, а при
заполнении нижнего уменьшается (т.к. песок насыпается слоями и лезет вверх).
Решая совместно уравнения прямых, можно найти координату точки их пересече-
ния нам нужна только Y координата, и она равняется Y=125
Исходный текст:
uses Graph, CRT;
const
PauseTime = 1000; {длительность паузы при прорисовке изображения}
Count = 50; {количество песка в часах, 60 < Count < 125}
var
grDriver: Integer; {переменные для инициализации графического режима}
grMode: Integer; {номер графического режима}
ErrCode: Integer; {код ошибки инициализации графического режима}
i, j, k : integer; {переменные циклов}
Key : word; {код нажатой клавиши}
BEGIN
grDriver := Detect;
InitGraph(grDriver, grMode, 'c:\bp\bgi'); {инициируем графику}
ErrCode := GraphResult;
if ErrCode <> grOk then Halt(1); {Выход из программы}
{продолжаем если графика инициализирована удачно}
Line(50, 50, 150, 200);
Line(150, 50, 50, 200);
Line(50, 50, 150, 50);
Line(50, 200, 150, 200);
SetFillStyle(SolidFill, DarkGray); { новый стиль заполнения }
Bar(45, 45, 155, 49);
Bar(45, 201, 155, 206); {уравнения линий ограничивающих песок: }
repeat
{цикл рисования часов, каждый новый круг часы "переворачиваются"}
{J = Count - верхняя граница линии песка, 125 - нижняя}
for j := Count to 125 do { l1: x=(2y+50)/3; l2: x=(2y-550)/(-3) - выведены}
begin { с применением аналитической геометрии}
for i := Round((2*j+50)/3)+1 to Round((2*j-550)/(-3))-1 do
PutPixel(i, j, Yellow); {рисуем начальное состояние песка}
end;
for k := 199 downto 199-Count do
for i := Round((2*k-550)/(-3))+1 to Round((2*k+50)/3)-1 do
PutPixel(i, k, Black); {стираем песок в нижнем конусе}
{ждем реакции пользователя, разрешающей начало течения песка}
k := ord(ReadKey); {получаем код нажатой клавиши}
if k = 0 then k := ord(ReadKey); {если нажата клавиша с расширеным кодом}
if k = 27 then Break; {выходим из цикла, если нажата Esc}
{y = 125 - ордината точки пересечения двух линий l1 и l2}
j := 199; {т.к. нижний конус кончается при y=200, чтобы не затереть его}
k := Count; {уменьшаем начальное значение J}
Randomize; {инициализируем генератор случайных чисел}
while (j > 199-(125-Count)) and (k < 125) do
begin
{рисуем струю песка}
for i := j downto 125 do
if Random(10) < 6 then {имитируем песчинки в струе песка}
PutPixel(100, i, Black) else PutPixel(100, i, Yellow);
{рисуем полоску песка в нижнем конусе}
for i := Round((2*j-550)/(-3))+1 to Round((2*j+50)/3)-1 do
PutPixel(i, j, Yellow);
{стираем полоску песка в верхнем конусе (1)}
for i := Round((2*k+50)/3)+1 to Round((2*k-550)/(-3))-1 do
PutPixel(i, k, Black);
{стираем полоску песка в верхнем конусе (2)}
for i := Round((2*(k+1)+50)/3)+1 to Round((2*(k+1)-550)/(-3))-1 do
PutPixel(i, k+1, Black);
Delay(PauseTime); {пауза и изменение переменных цикла}
{уровень песка в верхнем конусе уменьшается быстрее, чем повышается}
{его количество в нижнем}
dec(j);
inc(k, 2);
end; {while}
{завершение работы}
for i := j downto 125 do {cтираем струю песка}
PutPixel(100, i, Black);
k := ord(ReadKey); {получаем код нажатой клавиши}
if k = 0 then k := ord(ReadKey); {если нажата клавиша с расширеным кодом}
until k = 27;
CloseGraph; {закрываем графику}
END. {конец программы}
|