Отвечает Лысков Игорь Витальевич, Модератор :
Здравствуйте, Доктор Ватсон. Вот Вам морфинг квадрата в окружность. Для квадрата достаточно хранить информацию только о половине стороны. Все остальное рисуется аналогично.
Код:
.model tiny, C .386
R equ 80 ;радиус окружности = половине стороны квадрата N equ 20 ;число
итераций
POINT struc ;структура для хранения информации о меняющейся точке X0 dd ? ;расстояние от центра точки на стороне квадрата Xd dq ? ;приращение по Х для следующей итерации Xi dq ? ;Х позиции точки при очередной итерации POINT ends
.code .startup call FormPoints ;формируем информацию о точках половины стороны квадрата
mov ax, 0013h ; vga 320x200x256 int 10h
mov ax, 0a000h mov es, ax ; es - с
егмент видео
mov cx, N ;число итераций call PrintFigure,0bh ;рисуем нулевую итерацию - исходную фигуру - квадрат mov ah,0 int 16h ;для начала морфинга ждем нажатия на клавишу FigureOff: call PrintFigure,0 ;вытираем старую итерацию call PrintFigure,0bh ;рисуем новую итерацию
call DELAY ;пауза
loop FigureOff ;рисуем N раз
WaitEscape: mov ah, 0 int 16h ;выходим по Esc cmp ah, 1 jne WaitEscape
;выход в DOS mov ax, 0003h ; назад
в текстовый режим int 10h
mov ax, 4c00h ; bye-bye int 21h
;Формируем массив с инфой обо всех точках ;для квадрата достаточно иметь инфу о половине стороны FormPoints proc local num:word, radius:word, count:word lea di, pPoints ;будем хранить инфу сразу за программой mov cx, R+2 ;число точек = радиусу+2, чтобы прорисовать углы mov num, 0 ;начинаем от центра, num = расстояние по Х от центра mov radius, R ;радиус mov coun
t, N ;число отрезков по Х FormLoop: fild num ;x fst [POINT ptr di].X0 ;сохраним, как (float)x0 fst [POINT ptr di].Xi ;и как начальный x fld st ;st(1) = st = x fimul radius ;x*R fild num ;x fimul num ;x^2 fild radius ;R fimul radius ;R^2 faddp ;x^2+R^2 fsqrt ;sqrt(x^2+R^2) fdivp ;x*R/sqrt(x^2+R^2) fsubp ;x-x*R/sqrt(x^2+R^2) fidiv count ;deltaX = (x-x*R/sqrt(x^2+R^2))/count fstp [POINT ptr di].Xd ;сохраним deltaX add di, size
POINT ;на следующую точку inc num ;x=x+1 loop FormLoop ;по всем точкам ret FormPoints endp
;Рисуем очередную итерацию цветом colour PrintFigure PROC colour:word uses cx local X1Num:word,Y1num:word,X2Num:word,Y2num:word,RR:word mov RR, R ;радиус для вычислений mov cx, R+1 ;число точек lea di, pPoints ;адрес массива точек mov bx, size POINT ;длина одного элемента
;получаем координаты первой точки fld [POINT ptr di]
.Xi ;Хi ;считаем Y=Xi*R/X0 cmp [POINT ptr di].X0, 0 ;для самой первой jne PFDiv fistp X1Num ;X1=Xi mov Y1Num, R ;Y1=R jmp PrintLoop PFDiv: fist X1Num ;X1=Xi fimul RR ;Xi*R fdiv [POINT ptr di].X0 ;Xi*R/X0 fistp Y1Num ;Y1=Xi*R/X0 PrintLoop: push cx ;соханим счетчик ;получаем координаты второй точки fld [POINT ptr di+bx].Xi ;Xi fist X2Num ;X2=Xi fimul RR ;Xi*R fdiv [POINT ptr di+bx].X0 ;Xi*R/X0 fistp Y2Num ;Y2=Xi*R/X0
;выводим
отрезок в правой половине верхней стороны квадрата push colour mov ax, 100 sub ax, Y2Num ;Y2 выше центра push ax mov ax, 160 add ax, X2Num ;X2 справа центра push ax mov ax, 100 sub ax, Y1Num ;Y1 выше центра push ax mov ax, 160 add ax, X1Num ;X1 справа центра push ax call line add sp, 5*2
;выводим отрезок в левой половине верхней стороны квадрата push colour mov ax, 100 sub ax, Y2
Num ;Y2 выше центра push ax mov ax, 160 sub ax, X2Num ;X2 слева центра push ax mov ax, 100 sub ax, Y1Num ;Y1 вы
ше центра push ax mov ax, 160 sub ax, X1Num ;X1 слева центра push ax call line add sp, 5*2
;выводим отрезок в правой половине нижней стороны квадрата push colour mov ax, 100 add ax, Y2Num ;Y2 ниже центра push ax mov ax, 160 add ax, X2Num ;X2 справа центра push ax mov ax, 100 add ax, Y1Num ;Y1 ниже центра push ax mov ax, 160 add ax, X1Num ;X1 справа центра push ax call line add sp, 5*2
;выводим отрезок в левой
половине нижней стороны квадрата push colour mov ax, 100 add ax, Y2Num ;Y2 ниже центра push ax mov ax, 160 sub ax, X2Num ;X2 слева центра push ax mov ax, 100 add ax, Y1Num ;Y1 ниже центра push ax mov ax, 160 sub ax, X1Num ;X1 слева центра push ax call line add sp, 5*2
;выводим отрезок в нижней половине левой стороны квадрата ;X и Y меняются местами push colour mov ax, 100 add ax, X2Num
;Y2 ниже центра push ax mov ax, 160 sub ax, Y2Num ;X2 слева центра push ax mov ax, 100 add ax, X1Num ;Y1 ниже центра push ax mov ax, 160 sub ax, Y1Num ;X1 слева центра push ax call line add sp, 5*2
;выводим отрезок в верхней половине левой стороны квадрата ;X и Y меняются местами push colour mov ax, 100 sub ax, X2Num ;Y2 выше центра push ax mov ax, 160 sub ax, Y2Num ;X2 слева центра push ax mov ax, 100 sub ax,
X1Num ;Y1 выше центра push ax mov ax, 160 sub ax, Y1Num ;X1 слева центра push ax call line add sp, 5*2
;выводим отрезок в нижней половине правой стороны квадрата ;X и Y меняются местами push colour mov ax, 100 add ax, X2Num ;Y2 ниже центра push ax mov ax, 160 add ax, Y2Num ;X2 справа центра push ax mov ax, 100 add ax, X1Num ;Y1 ниже центра push ax mov ax, 160 add ax, Y1Num ;X1 справа
центра push ax call line add sp, 5*2
;выводим отрезок в верхней половине правой стороны квадрата ;X и Y меняются местами push colour mov ax, 100 sub ax, X2Num ;Y2 выше центра push ax mov ax, 160 add ax, Y2Num ;X2 справа центра push ax mov ax, 100 sub ax, X1Num ;Y1 выше центра push ax mov ax, 160 add ax, Y1Num ;X1 справа центра push ax call line add sp, 5*2
;копируем правую точку (x2,y2) на место первой (x1,y1) для
следующего отрезка mov ax, X2Num mov X1Num, ax mov ax, Y2Num mov Y1Num, ax
cmp colour, 0 ;после вытирания цветом 0 сдвигаем Xi на DeltaX jne PFNext fld [POINT ptr di].Xi fsub [POINT ptr di].Xd fstp [POINT ptr di].Xi ;Xi=Xi-Xd PFNext: add di, size POINT ;на следующую точку pop cx ;счетчик циклов dec cx ;loop не работает - слишком далеко jcxz PFRet jmp PrintLoop PFRet: ;для последней точки подправим
Xi cmp colour, 0 jne PFRet1 fld [POINT ptr di].Xi fsub [POINT ptr di].Xd fstp [POINT ptr di].Xi PFRet1: ret PrintFigure ENDP
;рисуем линию (x1,y1)-(x2,y2) цветом color Line proc uses di bx, x1:word, y1:word, x2:word, y2:word, color:byte local i:word, \ ;для работы со сопроцессором delta_x:word, \ ;длина проекции на ось абсцисс delta_y:word, \ ;длина проекции на ось ординат incx:word, \ ;приращение по X incy:word ;приращение по Y
;определим
длину проекции на ось абсцисс и шаг по оси X mov ax, x2 sub ax, x1 ;ax=x2-x1;
;определим шаг по X (+1 если вперед, -1 если назад, 0 если не меняется) mov incx, 0 ;пусть incx=0 test ax, ax ;ax=delta_x jz set_delta_x ;не меняется jg set_x_1 ;вперед? dec incx ;назад, значит incx=-1 neg ax ;найдем ax=abs(delta_x) jmp set_delta_x ;на сохранение set_x_1: inc incx ;вперед, значит incx=1; set_delta_x: mov delta_x, a
x ;delta_x = abs(x2-x1)
;определим длину проекции на ось ординат и шаг по оси Y mov ax, y2 sub ax, y1 ;ax=y2-y1;
;о
пределим шаг по Y (+1 если вперед, -1 если назад, 0 если не меняется) mov incy, 0 ;пусть incy=0 test ax, ax ;ax=delta_y jz set_delta_y ;не меняется jg set_y_1 ;вперед? dec incy ;назад, значит incy=-1 neg ax ;найдем ax==abs(delta_y) jmp set_delta_y ;на сохранение set_y_1: inc incy ;вперед, значит incy=1; set_delta_y: mov delta_y, ax ;delta_y=abs(y2-y1)
;определим большее из проекций как основное напрвление cmp ax, delta_x ;ax=delta_y jge from_y ;y
будет основным cmp delta_x, 0 ;проверим, чтобы не было delta_x=0 (для точки), jz Line_ret ; иначе будет деление на 0 ;delta_x>delta_y && delta_x!=0 ;основное направление - по оси X fild delta_y fidiv delta_x ;st=k=(float)(delta_y/delta_x)
;for (int i=0;i<delta_x;i++) xor cx, cx ;cx=i jmp cmp_i_x ;на проверку i<delta_x x_loop: ;тело цикла mov i, cx ;запишем переменную цикла в память (для сопроцессора) fld s
t ;st=st(1)=k fimul i ;st=k*i fimul incy ;st=incy*k*i call floor ;округлим до целого в большую сторону fistp i ;сохраним в переменной mov ax, i ;относительный номер строки на экране add ax, y1 ;добавим до ординаты начальной точки mov dx, 320 ;получим индекс начала строки экрана в сегменте экрана imul dx ; для этого умножим на длину в байтах одной стоки mov bx, ax ;сохраним bx=y=(y1+floor(incy*k*i))*320 ;посчитаем X mov ax, incx ;X меняется ровно на шаг
приращения, imul cx ; умноженному на индекс точки add ax, x1 ;добавим абциссу начальной точки ax=x=x1+incx*i
add ax, bx ;сложим с индексом начала строки mov di, ax ;будем адресовать через di
mov al, color ;цвет точки mov es:[di], al ;рисуем!
inc cx ;на следующую точку cmp_i_x: cmp cx, delta_x ;дошли до конца? jl x_loop jmp Line_ret ;на выход
from_y: ;вдоль оси Y fild delta_x fidiv delta_y ;st
=k=(float)(delta_x/delta_y)
;for (int i=0;i<delta_y;i++) xor cx, cx ;cx=i jmp cmp_i_y ;на проверку i<delta_y y_loop: ;тело цикла mov ax, incy ;Y меняется ровно на шаг приращения, imul cx ; умноженному на индекс точки add ax, y1 ;добавим абциссу начальной точки ax=y=y1+incy*i mov dx, 320 ;получим индекс начала строки экрана в сегменте экрана imul dx ; для этого умножим на длину в байтах одной стоки mov bx, ax ;сохраним bx=y=(y1+incy*i)*320 ;посчитаем
X mov i, cx ;запишем переменную цикла в память (для сопроцессора) fld st ;st=st(1)=k fimul i ;st=k*i fimul incx ;st=incx*k*i call floor ;округлим до целого в большую сторону fistp i ;сохраним в переменной mov ax, i ;относительный номер строки на экране add ax, x1 ;ax=x=x1+floor(incx*k*i)
add ax, bx ;сложим с индексом начала строки mov di, ax ;будем адресовать через di
mov al, color ;цвет точки mov
es:[di], al ;рисуем!
inc cx ;на следующую точку cmp_i_y: cmp cx, delta_y ;дошли до конца? jl y_loop Line_ret: fistp i ;удалим из сопроцессора k ret Line endp
;округление до целого в большую сторону ;округление по умолчанию, до ближайщего, не устраивает floor proc local CtrlWordOld:word, CtrlWordNew:word fstcw CtrlWordOld ;сохраним управляющее слово fclex ;сбросим исключения mov CtrlWordNew,0763h ;установим необходимое значение управляющего слова fldcw CtrlWordNew ;загружаем
управляющее слово frndint ;округляем st до целого fclex ;сбросим исключения fldcw CtrlWordOld ;восстановим старое управляющее слово ret floor endp
;пауза ~40мс. Под Windows весьма условно, скорее всего будет больше time equ 4 ;число интервалов по 10мс DELAY: pusha ;сохраним все регистры mov ah,2dh ;сбросим "локальное" системное время (под nt+ все равно не поменяет) xor cx,cx xor dx,dx int 21h
dl2: mov ah,2ch int 21h ;читаем время ;считаем сотни мс mov al,100 mul dh ;секунды умножае
м на 100 -> ax = количество интервалов по 10мс xor dh,dh ;dx - число сотых xchg ax,dx ;поменяем местами mov cl,10 div cl ;ax = количество интервалов по 10мс из сотых add ax,dx ;складываем с количеством из секунд cmp ax,time ;сравним с ожидаемым интервалом jl dl2 ;ждем, если меньше popa ;восстановим все регистры ret
pPoints label DWORD ;с этого адреса будем хранить данные о точках
end
----- Удачи!
Ответ отправил: Лысков Игорь Витальевич, Модератор
Ответ отправлен: 22.05.2010, 08:31
Номер ответа: 261563 Украина, Кировоград Тел.: +380957525051 ICQ # 234137952 Mail.ru-агент: igorlyskov@mail.ru Абонент Skype: igorlyskov
Оценка ответа: 5 Комментарий к оценке: Cпасибо!
Вам помог ответ? Пожалуйста, поблагодарите эксперта за это! Как сказать этому эксперту "спасибо"?
Отправить SMS#thank 261563
на номер 1151 (Россия) |
Еще номера »
Оценить выпуск »
Нам очень важно Ваше мнение об этом выпуске рассылки!
* Стоимость одного СМС-сообщения от 7.15 руб. и зависит от оператора сотовой связи.
(полный список тарифов)
** При ошибочном вводе номера ответа или текста #thank услуга считается оказанной, денежные средства не возвращаются.
*** Сумма выплаты эксперту-автору ответа расчитывается из суммы перечислений на портал от биллинговой компании.