Консультация № 177613
02.04.2010, 23:26
45.00 руб.
0 18 1
Здравствуйте. У меня семестр. работа. Нужно написать программу: вращение куба в пространстве. Главное, чтобы программа была простая для понимание (без всяких лишних операций, вращение куба с постоянной небольшой скоростью) и обязательно должны быть комментарии к каждому действию. Использовать TASM.

Обсуждение

Неизвестный
03.04.2010, 00:05
общий
zaharchenko-r:
Операционная система?
Если ДОС, то какой видеорежим?
Если Windows, то использовать DirectX, OpenGL или GDI?
Это существенная информация!
Неизвестный
03.04.2010, 07:36
общий
И, кстати, обязательная в этой рассылке (см. правила)
Неизвестный
03.04.2010, 10:44
общий
нужно использовать функции DOS (int 10h, int 21h и т.д.), видео режим - на ваше усмотрение.
Есть метод. указания: http://files.mail.ru/MWM9PJ
Неизвестный
03.04.2010, 15:53
общий
На мой взгляд эти "методические" никакого отношения к данному заданию не имеют.
Следовательно, все придется самому обсчитывать (положение на экране и видимость каждой грани, ....). Тогда уж определите, где находится ось вращения. Чтобы не получилось так, что потом скажите: "А мне не так нужно было..."
Рисовать с помощью функций DOS - это такая фигня получится, в смысле скорости отрисовки. Отсюда еще вопрос: размер кубика, цвет, ....
Неизвестный
03.04.2010, 20:46
общий
Я думаю, что ось вращения сама может циклично изменять свое положения или хотя бы располагалась так чтобы при вращении куба было видно большинство граней, что касается размера и цвета - это не столь важно, главное чтобы сам куб при вращении не сливался в одно монотонное пятно. DOS - потому что мы его только изучаем, не думаю что от нас требуют знание каких-то новых графических библиотек.
Неизвестный
04.04.2010, 00:09
общий
М-да. Вы представляете себе, что эта задача на 97% математическая - нужно рассчитывать положение на экране каждой точки поверхности куба. Задача решенная уже давно, на специальностях, связанных с программированием, есть специальный курс, где рассматриваются такие вопросы. От Вас могут потребовать объяснить эту всю математику - Вы действительно хотите пойти по этому пути.
С другой стороны, в том, что Вы назвали "новыми графическими библиотеками", нужно знать только что и как вызвать, но и прощу будет программировать под Windows.
Неизвестный
04.04.2010, 00:22
общий
Ну можно программировать и под Windows, главное чтобы было просто и понятно, ну и с комментариями для того чтобы можно было отчетать программу в универе. А что использовать - DirectX, OpenGL или GDI это я уже не знаю, буду надеяться на вас!
давно
Посетитель
7438
7205
06.04.2010, 09:28
общий
Продлил вопрос на 5 суток
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
06.04.2010, 14:38
общий
zaharchenko-r:
Судя из постановки вопроса, требуется таки под ДОС...
И если сильно хочется, то можно сделать
Ваше окончательное решение: как делаем?
Только вот насчет того, что будет "просто для понимания" не надейтесь...
Придется изрядно потрудиться, чтобы понять. Не зря же ее дали, как семестровую работу.
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
06.04.2010, 22:58
общий
Математики много, но в ней хоть возможно разобраться, а для DOS я сам делал несколько программ, так что будет легче.
Все-таки DOS.
Неизвестный
07.04.2010, 07:50
общий
Да, и еще: нужна рамка куба или закрашивать грани? Учитывать ли освещенность?
давно
Посетитель
7438
7205
07.04.2010, 09:18
общий
Boriss:
Думаю, более чем достаточно каркаса. Работа чисто на вращение, поэтому объект может отображаться попроще.
Сделаете?
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
07.04.2010, 12:46
общий
Опять тот же ... вкус или ответ... Смогу, скорее всего, завтра взяться за эту задачу (сегодня Совет, заседание кафедры и еще пара дел и одна программа на Паскале - не успею)
Неизвестный
07.04.2010, 16:42
общий
Согласен, каркаса будет достаточно!
давно
Посетитель
7438
7205
13.04.2010, 03:38
общий
это ответ
Здравствуйте, zaharchenko-r.
Вот Вам искомая программа.
Можете поэксперементировать, вращать вокруг любой оси...
Довольно подробно прокомментировано, разберетесь и с ассемблером, и с математикой.
Удачи!

Приложение:
;Используемые формулы для вращения
; Вокруг оси 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

;макро вызова п/п рисования линии по номерам вершин куба
PRLINE macro num1, num2, colour
mov bx, num2
shl bx, 2
push colour
push [POINT2D ptr bx+si].yy
push [POINT2D ptr bx+si].xx
mov bx, num1
shl bx, 2
push [POINT2D ptr bx+si].yy
push [POINT2D ptr bx+si].xx
call Line
add sp, 10
endm

.model tiny, C ; модель памяти и порядок вызова параметров
.386 ; нужно для команды fsincos
.code ; сегмент кода
.startup ; точка входа

mov ax, 0013h ; vga 320x200x256
int 10h

mov ax, @DATA
mov ds, ax ; инициируем сегмент данных
mov ax, 0a000h
mov es, ax ; es - сегмент видео

;установим глобальные переменные
mov [DeltaX], 1 ; скорость вращения (приращение угла)
mov [DeltaY], 1 ;
mov [DeltaZ], 1 ;

;переменные, используемые для расчета координат на эране
mov [Xoff], 256 ; пропорции куба
mov [Yoff], 256
mov [Zoff], 300 ; расстояние от взгляда

MainLoop:
call MainProgram ; рисуем куб один раз

mov ah, 1 ; проверяем на код клавиши
int 16h
jz MainLoop
mov ah, 0
int 16h
cmp ah, 1 ; по Esc выходим
jne MainLoop

;выход в DOS
mov ax, 0003h ; назад в текстовый режим
int 10h

mov ax, 4c00h ; bye-bye
int 21h

;подпрограммы

;расчет новых углов для вращения
;делаем простой инкремент углов на Delta_
;при переходе через 360 обнуляем
UpdateAngles PROC
mov ax, [XAngle]
add ax, [DeltaX]
cmp ax, 360 ; проверяем 0..359
jb set_XAngle
sub ax, 360
set_XAngle:
mov [XAngle], ax

mov ax, [YAngle]
add ax, [DeltaY]
cmp ax, 360 ; проверяем 0..359
jb set_YAngle
sub ax, 360
set_YAngle:
mov [YAngle], ax

mov ax, [ZAngle]
add ax, [DeltaZ]
cmp ax, 360 ; проверяем 0..359
jb set_ZAngle
sub ax, 360
set_ZAngle:
mov [ZAngle], ax
ret
UpdateAngles ENDP

;расчет новых 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, 5, 11
PRLINE 2, 6, 11
PRLINE 3, 7, 11
PRLINE 4, 5, 11
PRLINE 5, 6, 11
PRLINE 6, 7, 11
PRLINE 7, 4, 11

call DELAY ; пауза

;вытираем все ребра цветом 0(черным)!
PRLINE 0, 1, 0
PRLINE 1, 2, 0
PRLINE 2, 3, 0
PRLINE 3, 0, 0
PRLINE 0, 4, 0
PRLINE 1, 5, 0
PRLINE 2, 6, 0
PRLINE 3, 7, 0
PRLINE 4, 5, 0
PRLINE 5, 6, 0
PRLINE 6, 7, 0
PRLINE 7, 4, 0

ret
MainProgram 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, 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

;пауза ~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

.data
c180 dd 180. ;180 градусов - для преобразоваия в радианы

;вершины нашего куба, центр - точка (0, 0, 0)
Cube POINT3D <-40, -40, -40>
POINT3D <-40, 40, -40>
POINT3D < 40, 40, -40>
POINT3D < 40, -40, -40>
POINT3D <-40, -40, 40>
POINT3D <-40, 40, 40>
POINT3D < 40, 40, 40>
POINT3D < 40, -40, 40>

;здесь будут экранные координаты вершин куба
Cube2D POINT2D 8 dup (<>)

X DW ? ;временные переменные для вычисления координат при вращении
Y DW ?
Z DW ?

XAngle DW 0 ;углы поворота относительно осей
YAngle DW 0
ZAngle DW 0

DeltaX DW ? ;инкремент по осям
DeltaY DW ?
DeltaZ DW ?

Xoff DW ? ;пропорции по X
Yoff DW ? ; и Y
Zoff DW ? ;расстояние от глаз

Mx DW 160 ;середина экрана
My DW 100

MaxPoints EQU 8 ;число вершин

END
5
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
13.04.2010, 03:42
общий
Boriss:
Не получилось? Ну ничего, я сам сделал...
Я не мог пройти мимо задачки с ассемблером и математикой "в одном флаконе".
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
13.04.2010, 08:46
общий
Да нет, тоже получается на бумаге - не успеваю отладить. Щас посмотрю... А мне нужно посчитать систему охранных колец для диода Шоттки. На нашем заводе собираются их выпускать, хотелось бы смоделировать поточнее - но это система простая, а математически задача сложная - большие перепады концентрации электронов
Неизвестный
13.04.2010, 10:05
общий
Огромное спасибо!!!! Программа работает - СУПЕР.
Форма ответа