Инженерам программистам

Yakushev Alexsandr

Здравствуйте уважаемые читатели! Вашему вниманию предлагается урок, в котором будет рассмотрено множество интересных возможностей maxscript’а. Они, на мой взгляд, будут полезны не только 3д-художникам, желающим разнообразить свои познания, но и инженерам — программистам.

Основу урока составляет описание практического применения maxscript’а для расчета аэродинамических характеристик летательных аппаратов на гиперзвуковом участке полета. Реализованная программа была многократно испытана и ее результаты были подвергнуты тщательному анализу. Как выяснилось, относительная погрешность, при сравнении с экспериментальными данными, составляет всего 5-6%!

Заранее приношу свои извинения за возможные ошибки и упущения. Сам я не являюсь профессиональным программистом, но все же постараюсь описать элементы программы в максимально доступной форме.

Итак, начнем:

Как и во всех современных языках программирования в макроязыке применяется цветовая идентификация:

Зеленым — обозначаются, сопровождающие программу, пояснения

Синим — обозначаются различные функции Maxscript’a, коих бесчисленное множество!:)

Коричневым — обозначаются различные тексты, которые будут отображаться в интерфейсе программы

Черным — все остальное.

Так как наша программа будет заниматься не только расчетами, но и построением графиков, то нам пригодиться стандартная графическая функция, которую я выдернул из прилагающихся туториалов по maxscript’у:

— создание графической функции для построения прямых линий

function BLine bmp x1 y1 x2 y2 c =

(

local ary = #(c)

— assign originals

local Xb = x1 as integer

local Yb = y1 as integer

— build the line deltas

local dX = x2-x1 as float

local dY = y2-y1 as float

— straight horiz

if dy == 0.0f do

(

local xsign = 1

if xb > x2 then xsign = 1 as integer

if x2 < xb then xsign = -1 as integer

setPixels bmp [xb,yb] ary

while xb != x2 do

(

xb += xsign

setPixels bmp [xb,yb] ary

)

return true

)

— straight vertical

if dx == 0.0f do

(

local ysign = 1

if yb > y2 then ysign = 1 as integer

if y2 < yb then ysign = -1 as integer

setPixels bmp [xb,yb] ary

while yb != y2 do

(

yb += ysign

setPixels bmp [xb,yb] ary

)

return true

)

— no straights, go for bresenham line slide

— set up the movements

local xsign = 1

if xb > x2 then xsign = 1 as integer

if x2 < xb then xsign = -1 as integer

local ysign = 1

if yb > y2 then ysign = 1 as integer

if y2 < yb then ysign = -1 as integer

dx = abs(dx)

dy = abs(dy)

setPixels bmp [xb,yb] ary

— line more vertical than horizontal

if dx < dy then

(

p = 2 * dx — dy

const1 = 2 * dx

const2 = 2 * (dx — dy)

while yb != y2 do

(

yb += ysign

if p < 0 then ( p = p + const1 )

else

(

p += const2

xb += xsign

)

setPixels bmp [xb,yb] ary

)

)

— line more horizontal than vertical

else

(

p = 2 * dy — dx

const2 = 2 * (dy — dx)

const1 = 2 * dy

while xb != x2 do

(

xb = xb + xsign

if p < 0 then ( p = p + const1 )

else

(

p = p + const2

yb = yb + ysign

)

setPixels bmp [xb,yb] ary

)

)

)

При желании можно обойтись и без нее, но все-таки с этой функцией интерфейс программы легче воспринимается человеческим глазом.

Теперь приступим к созданию плавающего окна и его интерфейса:

Присвоим переменным параметры ширины и высоты плавающего окна и встроенной в него пиксельной карты, которая затем пригодится нам для построения графиков функций:

— объявление переменных

M_width = 860 — ширина плавающего окна

M_hight = 490 — высота плавающего окна

R_width = 800 — ширина пиксельной карты

R_hight = 400 — высота пиксельной карты

Создадим плавающее окно, которое я назвал :

— создание нового плавающего окна

Main = newRolloutFloater «Построитель аэродинамических функций» M_width M_hight

Далее:

— объявление внутри плавающего окна локального свитка

rollout Aero «Окно просмотра»

Любой локальный свиток должен иметь начало и конец, которые обозначаются соответственно , :

(— начало свитка

Теперь, когда мы объявили начало локального свитка, можно начинать вставлять различные элементы интерфейса:

— создание пиксельной карты

bitmap gr width:R_width height:R_hight color: white

— создание цифровых меток для маркировки оси ординат (левый край)

label lbl21 «-2.0» pos:[0,395] width:20 height:18

label lbl22 «-1.5» pos:[0,350] width:20 height:18

label lbl23 «-1.0» pos:[0,300] width:20 height:18

label lbl24 «-0.5» pos:[0,250] width:20 height:18

label lbl25 «0.0» pos:[0,200] width:20 height:18

label lbl26 «0.5» pos:[0,150] width:20 height:18

label lbl27 «1.0» pos:[0,100] width:20 height:18

label lbl28 «1.5» pos:[0,50] width:20 height:18

label lbl29 «2.0» pos:[0,0] width:20 height:18

— создание цифровых меток для маркировки оси ординат (правый край)

label lbl51 «-2.0» pos:[825,395] width:20 height:18

label lbl52 «-1.5» pos:[825,350] width:20 height:18

label lbl53 «-1.0» pos:[825,300] width:20 height:18

label lbl54 «-0.5» pos:[825,250] width:20 height:18

label lbl55 «0.0» pos:[825,200] width:20 height:18

label lbl56 «0.5» pos:[825,150] width:20 height:18

label lbl57 «1.0» pos:[825,100] width:20 height:18

label lbl58 «1.5» pos:[825,50] width:20 height:18

label lbl59 «2.0» pos:[825,0] width:20 height:18

— создание цифровых меток для маркировки оси абсцисс (горизонтальная ось)

label lbl30 «0» pos:[22,407] width:20 height:18

label lbl31 «10» pos:[60,407] width:20 height:18

label lbl32 «20» pos:[104,407] width:20 height:18

label lbl33 «30» pos:[148,407] width:20 height:18

label lbl34 «40» pos:[192,407] width:20 height:18

label lbl35 «50» pos:[240,407] width:20 height:18

label lbl36 «60» pos:[284,407] width:20 height:18

label lbl37 «70» pos:[329,407] width:20 height:18

label lbl38 «80» pos:[373,407] width:20 height:18

label lbl39 «90» pos:[418,407] width:20 height:18

label lbl40 «100» pos:[458,407] width:20 height:18

label lbl41 «110» pos:[500,407] width:20 height:18

label lbl42 «120» pos:[545,407] width:20 height:18

label lbl43 «130» pos:[590,407] width:20 height:18

label lbl44 «140» pos:[635,407] width:20 height:18

label lbl45 «150» pos:[679,407] width:20 height:18

label lbl46 «160» pos:[724,407] width:20 height:18

label lbl47 «170» pos:[768,407] width:20 height:18

label lbl48 «180» pos:[812,407] width:20 height:18

Цифровые метки, в данном случае, необходимы только для повышения удобства чтения полученных данных. Надеюсь, что пока понятно, что обозначают , :, если, все таки возникли вопросы, то просто поэкпериментируйте с параметрами и, думаю, все встанет на свои места.

Далее:

— создание внутри локального свитка 4-х групп:

— первая

groupBox grp1 «Построение аэродинамических коэффициентов в связанной и поточной системе координат» pos:[5,420] width:835 height:47

—(

radiobuttons copy_type

labels:#(«Cx «, «Cy «, «Cz «, «Cxa «, «Cya «, «K = (Cya/Cxa) «) pos: [260,440]

default:1

pickbutton bn_Master «Выделить объект» pos:[10,435] width:130 height:27

button knopka «Построить» pos:[705,435] width:130 height:27

—)

— вторая

group «Переменные величины»

(

spinner x1 «Аэродинамический коэффициент K :» range:[0,3,2] type:#float pos:[170,490] width:100

spinner x2 «Шаг:» range:[0.1,90,5] type:#float pos:[640,490] width:50

—spinner x3 «Number of points» range:[36,800,36] type:#integer pos:[580,490] width:80

label lbl1 «Площадь миделя:» pos:[340,490] width:100 height:18

editText edt1 text:»3.14159″ pos:[430,490] width:100 height:18

)

— третья

group «Быстрое построение простых объектов (площадь миделя: 3.14159; позиция центра: [0,0,0])»

(

button Constr_sphere «Создать сферу» pos:[10,535] width:100 height:27

spinner CS_rad «радиус:» range:[0,100,1] type:#float pos:[150,545] width:55

spinner CS_s «число сегментов:» range:[0,100,18] type:#integer pos:[285,545] width:75

button Constr_cone «Создать конус (высота h=1)» pos:[685,535] width:150 height:27

spinner C_rad «радиус:» range:[0,100,1] type:#float pos:[480,545] width:55

spinner C_s «число сторон:» range:[0,100,24] type:#integer pos:[600,545] width:65

)

— четвертая

group «Вывод данных»

(

button Otp «Записать данные в файл» pos:[10,590] width:140 height:27

label lbl8 «по адресу:» pos:[155,597] width:70 height:18

editText edt2 text:»D:\\out1.txt» pos:[210,597] width:70 height:18

button disp «Отобразить и сохранить» pos:[685,590] width:150 height:27

button Vert «Записать в файл координаты вершин (по фэйсам)» pos:[290,590] width:260 height:27

label lbl9 «по адресу:» pos:[555,597] width:70 height:18

editText edt3 text:»D:\\out2.txt» pos:[610,597] width:70 height:18

)

Группы созданы двумя разными способами, но дают почти один и тот же результат. Какая же между ними разница? создает группу размеры и положение которой задаются параметрами , а создает группу, которая автоматически подстраивается под размеры плавающего окна и размеры встроенных в нее элементов интерфейса.

С помощью переключателей в первой группе будет производиться выбор характеристик, которые необходимо построить на пиксельной карте. Значение означает, что по умолчанию будет выбрана первая характеристика . Хотелось бы еще пояснить, значение команд . A — задает нижний диапазон, B — верхний, C — значение, отображаемое при запуске программы, — дословно переводится, как , а вообще означает дробный тип отображаемых чисел, — соответственно, целый тип. Значение всех остальных параметров легко понять, если их поварьировать и посмотреть на результат, нажав , что означает выполнить написанный скрипт.

Но здесь может возникнуть небольшая трудность с отображением заданного интерфейса, чтобы ее избежать, просто добавьте ниже следующие строки:

)— конец свитка

Addrollout Aero main — добавление свитка к плавающему окну

Если все сделано правильно, то Вы должны получить следующий результат:

Изучив все параметры удалите строки:

)— конец свитка

Addrollout Aero main — добавление свитка к плавающему окну

Потому что они понадобятся нам позже.

Итак, создав необходимый интерфейс, можно приступить к описанию событий (т.е. что у нас произойдет при нажатии на ту или иную кнопку):

— при нажатии на кнопку производится запись координат вершин каждого треугольника выделенного объекта в текстовый файл

on Vert pressed do

(

local o=convertToMesh $ — локальной переменной присваивается, преобразованный в Mesh(сетку), выделенный объект

local s=edt3.text as string — локальной переменной присваивается путь, по которому будет сохранен текстовый файл

local d=createFile s — по указанному адресу создается файл (пока пустой)

for f in o.faces do — объявление цикла по фэйсам (треугольникам, из которых состоит объект)

(

Vx=(meshop.getVertsUsingFace o f.index) as array — получение массива фэйсов

for i=1 to 3 do — цикл по трем вершинам рассматриваемого фэйса

(

local b=getVert o Vx[i] — переменной «b» присваивается значение i-ой вершины фэйса

format «% % %» b.x b.y b.z to:d — запись координат [x,y,z] i-ой вершины

format «\n» to:d — запись перехода на следующую строку

)

)

close d — закрывает созданный файл

Запись координат вершин(по фэйсам) необходима для того, чтобы появилась возможность обрабатывать данные не только в 3Dstudio Max’е, но и в других программах, таких как Маткад или Матлаб.

— при нажатии на кнопку будет создана сфера с указанными в обработчике события параметрами

on Constr_sphere pressed do

(

local s = sphere radius: CS_rad.value segs: CS_s.value smooth:off

max tool zoomextents all — показать сферу в области просмотра

)

Значения и считываются с показаний соответствующих счетчиков(spinner) и .

— при нажатии на кнопку будет создан конус с указанными в обработчике события параметрами

on Constr_cone pressed do

(

local c = cone radius1: C_rad.value sides: C_s.value height: 1 heightsegs: 1 capsegs: 1 smooth:off

rotate c 90 [0,1,0] — повернуть конус на 90 градусов относительно оси Y

move c [-0.3, 0, 0] — передвинуть конус по оси X на -0.3 единицы

max tool zoomextents all — показать конус в области просмотра

)

— при нажатии на кнопку и после выделения объекта в сцене произойдет следующее событие

on bn_Master picked obj do

(

obj.name = «Выделен» — кнопку переименовать в

bn_Master.text=obj.name — присвоить объекту имя

select $Выделен — выделить объект в сцене с именем

)

— при открытии плавающего окна произойдет следующее событие

on Aero open do

(

— оформление дизайна пиксельной карты

local bm = bitmap R_width R_hight color: white — создание пиксельной карты

local col = color 220 220 220 — создание локальной переменной со значением цвета в виде массива

for x=0 to R_width by 44.444444 do( — создание вертикальных линий

BLine bm x 0 x R_hight col )

for y=0 to R_hight by 50 do( — создание горизонтальных линий

BLine bm 0 y R_width y col )

BLine bm 0 ((R_hight)/2) R_width ((R_hight)/2) red — создание оси абсцисс красного цвета

gr.bitmap = bm — записать полученное изображение в пиксельную карту

)

Проще говоря, данное событие описывает то, что Вы увидите на месте пиксельной карты при первом запуске скрипта (Ctrl+e). Как же все таки создаются вертикальные и горизонтальные линии?

Эти строки

for x=0 to R_width by 44.444444 do( — создание вертикальных линий

BLine bm x 0 x R_hight col )

означают примерно следующее:

Для x от 0 до «R_width» (значение было задано в начале) с интервалом 44.4444 выполнить функцию , при этом первые два параметра — это начало линии, а последних два параметра «x R_hight» — это конец линии, «col» — переменная со значением цвета.

Далее:

— при нажатии на кнопку в группе произойдет следующее событие

on disp pressed do

(

display gr.bitmap — отображение полученной пиксельной карты

)

Осталось описать события к двум кнопкам — это кнопки и . Так как описания событий к этим кнопкам довольно похожи и, при этом, довольно объемны, то ограничусь описанием события только для кнопки , а описание для кнопки можно, при желании, посмотреть в оригинале программы.

Событие для кнопки интересно тем, что в нем производится расчет площади, как отдельных фэйсов, так и всего объекта, так же вычисляются нормали и вектора из центра глобальной системы координат к центрам фэйсов. Все эти операции можно выполнять с помощью стандартных функций макроязыка, а можно вычислять самим с помощью методов аналитической геометрии. В событии описываются, как тот, так и другой способ. Способы, которые дают быстрейший результат — активированы, а те, что замедляют расчеты приведены в виде подсказок:

— при нажатии на кнопку произойдет следующее событие

on knopka pressed do

( — начало события

clearlistener() — отчистка «Слушателя» (вызывается клавишей F11 при активном главном окне 3dstudio MAX!)

— оформление дизайна пиксельной карты

local bm = bitmap R_width R_hight color: white — создание пиксельной карты

local col = color 220 220 220 — создание локальной переменной со значением цвета линий в виде массива

for x=0 to R_width by 44.444444 do( — создание вертикальных линий

BLine bm x 0 x R_hight col )

for y=0 to R_hight by 50 do( — создание горизонтальных линий

BLine bm 0 y R_width y col )

BLine bm 0 ((R_hight)/2) R_width ((R_hight)/2) red — создание оси абсцисс красного цвета

ary = #(blue) — создание переменной со значением цвета построительных точек (цвет графика функции)

— объявление переменных необходимых для расчета

o = converttoMesh $ — преобразование выделенного объекта в доступную для редактирования сетку

Ka = x1.value — переменной присваивается значение счетчика

Smid = edt1.text as float — переменной присваивается значение в текстовом поле

— начало циклического изменения значений угла от 0 до 180 градусов с шагом, указанным счетчиком

for i = 0 to 180 by x2.value do

( — начало цикла по

P1 = [0.0, 0.0, 0.0] — объявление переменной для обнуления суммы в цикле по фэйсам при первой итерации

potok = [cos i, 0, sin i] — объявление переменной потока (обеспечивается вращение единичного вектора против часовой стрелки на 180 градусов)

— начало цикла обращения к фэйсам объекта

for f in o.faces do

(— начало цикла по

— получение массива фэйсов

index = (meshop.getVertsUsingFace o f.index) as array

— получение координат каждой вершины рассматриваемого фэйса в виде векторов

a = getVert o index[1] — координаты первой вершины

b = getVert o index[2] — координаты второй вершины

c = getVert o index[3] — координаты третьей вершины

— расчет площади рассматриваемого фэйса (где функция — вычисление длины вектора, а функция — векторное произведение двух векторов)

— вычисление площади фэйса с помощью стандартной функции макроязыка

—local Square = meshop.getFaceArea o f.index

— вычисление площади фэйса методом аналитической геометрии

vec1 = b — a

vec2 = c — a

Square = (length(cross vec1 vec2))/2

— получение вектора нормали к данному фэйсу

local N = getFaceNormal o f.index

—an=(b.y-a.y)*(c.z-a.z)-(b.z-a.z)*(c.y-a.y)

—bn=(b.z-a.z)*(c.x-a.x)-(b.x-a.x)*(c.z-a.z)

—cn=(b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x)

—N = [an, bn, cn]

Nn = normalize N — нормализация(преобразование к величине от 0 до 1) нормали фэйса

— вычисление вектора, направленного из центра глобальной системы координат к центру данного фэйса

local Cf = meshop.getFaceCenter o f.index

—amc = (a.x + b.x + c.x)/3

—bmc = (a.y + b.y + c.y)/3

—cmc = (a.z + b.z + c.z)/3

—Cf = [amc, bmc, cmc]

Cf_n = normalize Cf — его нормализация

— направляем все нормали в одну сторону — наружу объекта (где функция — это функция скалярного перемножения двух векторов)

N_edit = if (dot Nn Cf_n)>0 then Nn else -Nn

local Uv = (dot N_edit potok)^2

— расчет значения вектора потока в данном фэйсе

P = if (dot N_edit potok)

— суммирование значений векторов потока по всем фэйсам

P1 = P1+P

)— конец цикла по

— привидение к безразмерному виду суммарного значения вектора потока делением на площадь миделя (напомню, что Pd.x=Cx, Pd.y=Cy, Pd.z=Cz), знак минуса здесь необходим для зеркального отображения графика относительно оси X

Pd = P1/Smid

— если убрать перед словами знаки тире, то в «Слушатель» будут выведены значения и , это было удобно при отработке программы

—print «Угол»

—print i

—print «Значение потока»

— вычисление значений аэродинамических коэффициентов и в поточной системе координат

Cxa = (Pd.x)*cos(i)+(Pd.z)*sin(i)

Cya = (Pd.z)*cos(i)-(Pd.x)*sin(i)

Kachestvo = (-Cya)/(Cxa) — вычисление аэродинамического качества

— в зависимости от выбранного положения переменной присваивается нужное значение

P_end = (if copy_type.state == 1 then Pd.x else

if copy_type.state == 2 then Pd.y else

if copy_type.state == 3 then Pd.z else

if copy_type.state == 4 then Cxa else

if copy_type.state == 5 then Cya else Kachestvo)

—print P_end

setpixels bm [4.4444*i, 100*P_end + 200] ary — запись значений в переменную

gr.bitmap = bm — запись значений переменной в пиксельную карту

) — конец цикла по

) — конец события

Хотелось бы обратить Ваше внимание на то, каким образом производится выбор с помощью и на значения параметров в сроке , коэффициенты 4.4444, 100, 200 необходимы лишь для масштабирования выводимого графика под пиксельную карту, а параметр — задан выше и обозначает цвет графика.

Ну вот, почти и все, осталось только закрыть свиток , в котором мы описывали интерфейс и производили расчеты и создать, как во всех нормальных программах :), свитки и :

)— конец свитка

Addrollout Aero main — добавление свитка к плавающему окну

— объявление свитка

rollout Aero2 «Помощь»

(

label lbl1a «Данная программа производит расчет и построение, по уточненной теории Ньютона, сверхзвуковых стационарных аэродинамических характеристик элементов

летательных аппаратов. К таковым относятся: аэродинамические коэффициенты в связанной системе координат Cx, Cy, Cz; аэродинамические коэффициенты в

скоростной (поточной) системе координат Cxa, Cya; аэродинамическое качество K = Cya/Cxa.

Плюсы и недостатки программы:

Плюсами данной программы, несомненно, являются скорость получения результата с возможностью его последующего сохранения или в виде

изображения, в любом из распространенных сейчас форматов, или записи результатов расчета в текстовый файл в виде матрицы столбца.

К недостаткам относятся — а) направление осей связанной системы координат в 3DstudioMAX’е немного отличается от общепринятой в аэро-

динамике (ось Z в 3DMAX’е соответствует оси Y в аэродинамике), б)чтобы скопировать в буфер графические построения с цифровыми метками по осям —

— необходимо воспользоваться сочетанием клавиш , при использовании же кнопки будет сохранено

только изображение построенной функции без цифровых меток!

Порядок проведения расчетов и пояснения к интерфейсу программы:

1. Необходимо создать объект и правильно расположить его в области просмотра. Для создания объектов сложной формы необходимо воспо-

льзоваться стандартными средствами построения объектов в 3DstudioMAX’е, а для демонстрационных расчетов можно воспользоваться кнопками

и в группе . Эти кнопки

создают, соответственно, сферу и конус в центре ГСК. Все параметры, в данном случае, заданные по умолчанию(радиус, площадь миделя)-

— заданы правильно.

2. Необходимо указать(выделить) объект кнопкой . После указания объекта кнопка изменит свое название на .

3. В группе задаются такие параметры, как аэродинамический коэффициент, площадь миделя и шаг, с которым будут про-

изводиться расчеты.

4. В группе , для построения графика, необходимо

выбрать нужную функцию и нажать на кнопку .

5. После проведенных исследований может понадобится сохранение результатов расчета или в виде текстового файла, или в виде изображения.

Для этого в группе есть, соответственно, две кнопки и , а если же понадобится

сохранить координаты вершин (по фэйсам) для последующих расчетах в других программах, то необходимо воспользоваться кнопкой

» pos:[10,10] width:860 height:390

)

Addrollout Aero2 main rolledUp:true

— объявление свитка

rollout Aero3 «О программе»

(

label lbl1b «Автор программы: Александр Якушев

Принимали участие в создании программы: Дмитрий Агафонов

Дмитрий (DG) http://dgsd.nm.ru/

Фёдор Мосалов

Руководитель: Миненко Виктор Елисеевич» pos:[10,10] width:790 height:70

)

Addrollout Aero3 main rolledUp:true

Наиболее важным, в плане познания макроязыка, является строка в которой команда означает, что при запуске скрипта свитки будут в свернутом состоянии.

Написать окончательно программу по этому уроку невозможно, так как здесь нет описания события к кнопке , но думаю, что это не проблема, потому что основной целью данного урока была попытка описать те или иные возможности макроязыка для последующего применения в других областях.

Оригинал же скрипта можно скачать здесь.

Хочу выразить огромную благодарность за неоценимую помощь и поддержку Дмитрию (DJ), моим, уже бывшим :), одногрупникам Дмитрию Агафонову и Фёдору Мосалову, а также руководителю дипломного проекта в МГТУ им. Н.Э. Баумана Миненко Виктору Елисеевичу.

Если у Вас возникли вопросы, то пишите, постараюсь помочь.

Удачи!

Понравилась статья? Поделиться с друзьями: