Вопрос № 178453: Уважаемые эксперты! Нужно написать программу - вращение пирамиды в 3-х мерном пространстве. Пирамида состоит из 5-ти вершин(т.е. квадрат в основании). ОС - DOS, использовать TASM. ...
Вопрос № 178453:
Уважаемые эксперты! Нужно написать программу - вращение пирамиды в 3-х мерном пространстве. Пирамида состоит из 5-ти вершин(т.е. квадрат в основании). ОС - DOS, использовать TASM.
Отвечает Лысков Игорь Витальевич, Модератор :
Здравствуйте, rmka. Легко Вот Вам вращающаяся пирамида. Все необходимые формулы и комментарии найдете в тексте программы
Код:
;Используемые формулы для вращения ; Вокруг оси x ; ; YT = Y * COS(xang) - Z * SIN(xang) ; ; ZT
= Y * SIN(xang) + Z * COS(xang) ; ; Y = YT ; ; Z = ZT ; ; ; ; Вокруг оси y ; ; XT = X * COS(yang) - Z * SIN(yang) ; ; ZT = X * SIN(yang) + Z * COS(yang) ; ; X = XT ; ; Z = ZT ; ; ; ; Вокруг оси x ; ; XT = X * COS(zang) - Y * SIN(zang) ; ; YT = X * SIN(zang) + Y * COS(zang) ; ; X = XT ; ; Y = YT ;
;Структура для хранения
одной вершины пирамиды POINT3D struc xx dw ? yy dw ? zz dw ? POINT3D ends
;структура для хранения координат вершины пирамиды на проскости POINT2D struc xx dw ? yy dw ? POINT2D ends
;расчет новых 3D координат для выбранного угла ;вращаем вокруг какой-либо оси, меняются 2 оставшиеся координаты ;(смотри формулы в начале и дальше) ;параметрами являются адреса координат и угол ;новые координаты записываются на то же место CalcRotation PROC uses si di, coord1:word,
coord2:word, angle:word ;преобразуем градусы в радианы fild angle ;угол fldpi ;пи fmulp ;угол*пи fdiv c180 ;угол*пи/180 ;найдем sin и cos fsincos ;st=sin, st(1)=cos
;адреса координат mov di, coord1 mov si, coord2
fild word ptr [di] ;первая координата (пусть, для определенности, x) fmul st, st(1) ;x * cos(a) fild word ptr [si] ;вторая координата (y) fmul st, st(3) ;y * sin(a) fsubp ;x * cos(
a) - y * sin(a)
fild word ptr [di] ;x fmul st, st(3) ;x * sin(a) fild word ptr [si] ;y fmul st, st(3) ;y * cos(a)
faddp ;x * sin(a) + y * cos(a)
fistp word ptr [si] ;y = x * sin(a) + y * cos(a) fstp st(1) ;уберем из стека sin fstp st(1) ; и cos fistp word ptr [di] ;x = x * cos(a) - y * sin(a) ret CalcRotation endp
;вращаем точку вокруг выбранных осей ;si указывает на структуру POINT3D RotatePoint PROC ;читаем 3D координаты во временные переменные mov ax, [POINT3D ptr si].xx mov [X], ax mov ax, [POINT3D ptr si].yy mov [Y], ax mov ax, [POINT3D ptr si].zz mov [Z],
ax
;вращаем вокруг оси x ; YT = Y * COS(xang) - Z * SIN(xang) ; ZT = Y * SIN(xang) + Z * COS(xang) ; Y = YT ; Z = ZT ; call CalcRotation, offset [Y], offset [Z], [XAngle]
;вращаем вокруг оси y ; XT = X * COS(yang) - Z * SIN(yang) ; ZT = X * SIN(yang) + Z * COS(yang) ; X = XT ; Z = ZT call CalcRotation, offset [X], offset [Z], [YAngle]
;вращаем вокруг оси z ; XT = X * COS(zang) - Y * SIN(zang) ; YT = X *
SIN(zang) + Y * COS(zang) ; X = XT ; Y = YT call CalcRotation, offset [X], offset [Y], [ZAngle]
ret RotatePoint endp
;преобразуем 3D координаты вершин пирамиды в 2D координаты на экране ;используем переменные [X], [Y], [Z] ;результат пишем по адресу [di], в элемент POINT2D Conv3Dto2D PROC mov ax, [Xoff] ; Xoff*X / Z+Zoff = экранное X mov bx, [X] imul bx mov bx, [Z] add bx, [Zoff] ; расстояние от глаз idiv bx add ax, [Mx] ; центр экрана mov [POINT2D
ptr di].xx, ax ; сохраним экранное X
mov ax, [Yoff] ; Yoff*Y / Z+Zoff = экранное Y mov bx, [Y] imul bx mov bx, [Z] add bx, [Zoff] ; расстояние от глаз idiv bx add ax, [My] ; центр экрана mov [POINT2D ptr di].yy, ax ; сохраним экранное Y ret Conv3Dto2D ENDP
;основная подпрограмма расчета и рисования MainProgram PROC call UpdateAngles ; расчитываем новые углы
lea di, Cube2D ; адрес 2D коорди
нат на экране lea si, Cube ; адрес 3D координат вершин пирамиды mov cx, MaxPoints ; число вершин ConvLoop: call RotatePoint ; вращаем вершины call Conv3Dto2D ; 3D в 2D add si, size POINT3D ; на следующую 3D вершину add di, size POINT2D ; на следующую 2D вершину loop ConvLoop
;рисуем lea si, Cube2D ;адрес координат вершин на экране ;по всем ребрам ;задаем номера вершин и цвет ;(при желании можно раскрасить все ребра в разые цвета) PRLINE 0, 1, 11 PRLINE 1,
2, 11 PRLINE 2, 3, 11 PRLINE 3, 0, 11 PRLINE 0, 4, 11 PRLINE 1, 4, 11 PRLINE 2, 4, 11 PRLINE 3, 4, 11
;рисуем линию (x1,y1)-(x2,y2) цветом color Line proc uses di bx, x1:wor
d, 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, ax ;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 st ;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
;пауза ~20мс. Под
Windows весьма условно, скорее всего будет больше time equ 2 ;число интервалов по 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
.data c180 dd 180. ;180 градусов - для преобразоваия в радианы
* Стоимость одного СМС-сообщения от 7.15 руб. и зависит от оператора сотовой связи.
(полный список тарифов)
** При ошибочном вводе номера ответа или текста #thank услуга считается оказанной, денежные средства не возвращаются.
*** Сумма выплаты эксперту-автору ответа расчитывается из суммы перечислений на портал от биллинговой компании.