Анимация в Mathematica
Юдинцев В. В.
Самарский университет
Кафедра теоретической механики

Стили по умолчанию...

SetOptions[Graphics,BaseStyle->{14,FontFamily->"Helvetica"}];

Геометрические объекты в Mathematica

Окружность

Graphics[Circle[{0,0},2]]

Graphics

Функция Graphics отображает графические примитивы, передаваемые ей в виде списка. Графические примитивы могут чередоваться описаниями их атрибутов (цвет и толщина линий, тип линий, ...)

Graphics[{Red,Circle[{0,0},1],Blue,Circle[{0,0},2]}]

Graphics[{Red,Disk[{0,0},1]}]

Несколько объектов

Несколько объектов {(Subscript[g, 1]), (Subscript[g, 2]), (Subscript[g, 3]), ..., (Subscript[g, n])}

Graphics[{
Circle[{0, 0}, 2],
Rectangle[{0.5, 0.5},{1.5,1}],
Rectangle[{-1.5, 0.5},{-0.5,1.3}],
Disk[{0, -1},0.3]
},
Frame->True]

Контур и заливка

Graphics[{
Yellow, (* Желтый *)
Disk[{0, 0}, 2],(* Диск *)
White,(* Белый *)
EdgeForm[{Thick,Red,Dashed}], (* Толстая красная рамка пунктиром *)
Rectangle[{0, 0},{1,1}] (* Прямоугольник *)
},
Frame->True]

Сектор

Graphics[
{
Lighter[Blue,0.7],(* Светлее на 70 %. 100 % -- белый *)
EdgeForm[{Thick,Blue}],(* толстая голубая линия *)
Disk[{0,0},1,{-30°,120°}]
},
Frame->True(* Показать рамку рисунка с надписями осей *),
ImageSize->500
]

Угол отсчитывается от горизонтальной оси против часовой стрелки

Тело 1 механизма

Graphics[{
Lighter[Blue,0.7],
EdgeForm[{Thick,Blue}],
Disk[{0,-1},1,{-180°,0°}]},
Frame->True,ImageSize->1000]

Опора

Функция Line[{{x1,y1},{x2,y2},{x3,y3}}]

Graphics[
{
Blue,
Thickness[0.02],
Line[{{0,0},{0,-1}}] (* Line[{{(Subscript[x, 1]),(Subscript[y, 1])},{(Subscript[x, 2]),(Subscript[y, 2])},{(Subscript[x, 3]),(Subscript[y, 3])},...}] *)
},
Frame->True,ImageSize->1000]

Стойка, стержень и диск

Graphics[{
(* Стержень *)
Blue,
Thickness[0.01],
Line[{{0,0},{0,-1}}],
(* Стойка *)
White,
EdgeForm[{Thick,Black}],
Disk[{0,0},0.03],
(* Диск *)
Lighter[Blue,0.7],
EdgeForm[{Thick,Blue}],
Disk[{0,-1},1,{-180°,0°}]
},
Frame->True]

Стойка, стержень и диск

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

body1={
Blue,Thickness[0.01],Line[{{0,0},{0,-1}}],

Lighter[Blue,0.7],EdgeForm[{Thick,Blue}],
Disk[{0,-1},1,{-180°,0°}],

White,EdgeForm[{Thick,Black}],
Disk[{0,0},0.03]
}

Чтобы показать эти объекты необходимо использовать функцию Graphics[]

Перемещение и поворот

Графический комплекс, состоящий из стойки и пластины

Graphics[body1,Frame->True,ImageSize->500]

Перемещение и поворот

Перенесём объект на 1 единицу вдоль осей x и y. Используем функцию GeometricTransformation. 
Первый аргумент функции - геометрический объект, второй - список, состоящий из двух элементов: матрица поворота и вектор смещения. Таким образом, функция GeometricTransformation определяет поворот и смещение объекта.

Graphics[
GeometricTransformation[body1,{RotationMatrix[0],{1,1}}],
Frame->True,ImageSize->500]

GeometricTransformation[ОБЪЕКТ, {МатрицаПовотора, {Смещение}}]

Матрица поворота

Матрица поворота в плоскости (вокруг оси z, перпендикулярной плоскости рисунка)

(({{x'}, {y'}}))=(({{cos φ, -sin φ}, {cos φ, cos φ}}))·(({{x}, {y}}))

Точка с координатами (({{x}, {y}})) поворачивается на угол φ вокруг начала координат

RotationMatrix[45.0°]//MatrixForm

Поворачиваем вектор {1, 0} на 20 градусов против часовой стрелки

RotationMatrix[20.0 °].{1,0}

Поворот вектора

Исходный вектор

r1={1.0,0.0};
Graphics[{
Blue,
Arrowheads[0.1],(* Размер стрелки *)
Arrow[{{0,0},r1}]
},
PlotRange->{{-1.1,1.1},{-1.1,1.1}},Frame->True,ImageSize->500]

Поворот вектора

Повернутый вектор

Graphics[{
Blue,Arrow[{{0, 0},r1}],
Red,Arrow[{{0, 0},RotationMatrix[30°]. r1}]
  },
PlotRange->{{-1.1,1.1},{-1.1,1.1}},Frame->True,ImageSize->500]

Это “активная” точка зрения на поворот - поворачивается объект.

GeometricTransformation

Использование функции GeometricTransformation для поворота и перемещения

Graphics[
GeometricTransformation[body1,{RotationMatrix[45°],{1,1}}],
Frame->True,ImageSize->800]

Держимся в рамках

Опция PlotRange позволяет определить границы области рисунка, чтобы при перемещении объекта Mathematica не выполняла автоматический выбор границ по осям координат (для построения анимации это не нужно).

Graphics[
GeometricTransformation[body1,{RotationMatrix[45°],{1,1}}],
Frame->True,PlotRange->{{-2,3},{-2,2}},ImageSize->1000]

Объект как функция положения

Создадим функцию, для формирования геометрического объекта в заданном положении. Удалим все определения, связанные с именем body1:

ClearAll[body1];

Определяем функцию от угла поворота с именем body

body1[φ_]:=Module[{b},
b={
(* Стержень *)
Blue,Thickness[0.01],Line[{{0,0},{0,-1}}],
(* Сектор, пластина *)
Lighter[Blue,0.7],EdgeForm[Directive[Thick,Blue]],
Disk[{0,-1},1,{-180°,0°}],
(* Стойка *)
White,EdgeForm[Directive[Thick,Black]],
Disk[{0,0},0.03]
};
(* Результат работы функции - повернутый и смещенный объект *)
GeometricTransformation[b,{RotationMatrix[φ],{0,0}}]
];

Функция Module позволяет создавать многострочные функции с локальными переменными, как в традиционных языках программирования.

Поворот

Рисуем, вызывая новую функцию

Graphics[body1[45°],Frame->True,PlotRange->{{-2,2},{-2,0.1}},ImageSize->600]

Добавляем канал для шарика

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

body1[φ_]:=Module[{b},
b={
(* Стержень *)
Blue,Thickness[0.01],Line[{{0,0},{0,-1}}],
(* Сектор, пластина *)
Lighter[Blue,0.7],EdgeForm[Directive[Thick,Blue]],
Disk[{0,-1},1,{-180°,0°}],
(* Стойка *)
White,EdgeForm[Directive[Thick,Black]],
Disk[{0,0},0.03],
(* Канал *)
 Rectangle[{-1, -1.6}, {1, -1.5}]
};
GeometricTransformation[b,{RotationMatrix[φ],{0,0}}]
];

Graphics[body1[0°],Frame->True]

Канал для шарика

Маскируем (убираем границу у всех объектов), чтобы казалось, что пластина с каналом это один объект.

body1[φ_] := Module[{b},
   b = {
     Lighter[Blue, 0.6],Thickness[0.01],Line[{{0, 0}, {0, -1}}],
     
     Lighter[Blue, 0.7],
     Disk[{0, -1}, 1, {-180 °, 0 °}],
     
     White, EdgeForm[Directive[Thick, Black]],
     Disk[{0, 0}, 0.03],

     EdgeForm[Directive[Thin, White]],
     Rectangle[{-1, -1.6}, {1, -1.5}]
     };
   GeometricTransformation[b, {RotationMatrix[φ], {1, 1}}]
   ];
Graphics[body1[0 °], Frame -> True]

Пружина

Функция Table генерирует списки
Table[выражение, {i, начальное значение, конечное значение, шаг}]

Table[(i^2),{i,0,5}]

Пружина в виде ломаной линии

Spring[L_,d_,n_]:=Table[{i *L/n,(((-1))^i)d/2},{i,0,n}];
Spring[0.5,0.01,5]

Чтобы нарисовать пружину, используем 
функцию Line[{p1, p2, p3,..., pn}]

Graphics[
Line[Spring[0.5, 0.01, 20]]
]

Больше “витков”, больше диаметр

Graphics[
 Line[Spring[0.5, 0.03, 50]]
 ]

Перемещение и поворот пружины

Линию, нарисованную по точкам, тоже можно поворачивать и перемещать, используя функцию GeometricTransformation

Поворачиваем на 20 градусов и перемещаем на 1 и 1 по x и y соответственно

Graphics[
GeometricTransformation[Line[Spring[0.5, 0.03, 50]],{RotationMatrix[20°], {1, 1}}],
Frame->True
  ]

Graphics[
 GeometricTransformation[Line[Spring[0.2, 0.03, 50]],{RotationMatrix[20 °], {0, 0}}],
Frame -> True
]

Стержень, пластина и пружина

body1[φ_] := Module[{b},
b=
{
Lighter[Blue, 0.6],Thickness[0.01],Line[{{0, 0}, {0, -1}}],

Lighter[Blue, 0.7],Disk[{0, -1}, 1, {-180 °, 0 °}],

White, EdgeForm[{Thick, Black}],
Disk[{0, 0}, 0.03],

EdgeForm[{Thin, White}],
Rectangle[{-1, -1.6}, {1, -1.5}],

Red,Thin,Translate[Line[Spring[0.5, 0.08, 30]],{-0.85,-1.55}]}
;
   GeometricTransformation[b, {RotationMatrix[φ], {0, 0}}]
   ];
Graphics[body1[0 °],Frame -> True]

Шарик

Добавляем в систему шарик (функция Disk)

body1[φ_,x_] := Module[{b},
b=
{
(* СТЕРЖЕНЬ *)
Lighter[Blue, 0.6],Thickness[0.01],Line[{{0, 0}, {0, -0.05}}],
(* ПЛАСТИНА *)
Lighter[Blue, 0.7],Disk[{0, -0.05},0.1, {-180 °, 0 °}],
(* ОПОРА *)
White, EdgeForm[{Thick, Black}],Disk[{0, 0}, 0.003],
(* КАНАЛ *)
EdgeForm[{Thin, White}],Rectangle[{-1, -0.09}, {1, -0.11}],
(* ПРУЖИНА *)
Red,Thin,Translate[Line[Spring[x, 0.02, 30]],{-0.09,-0.1}],
(* ШАРИК *)
Blue,Disk[{-0.09+x,-0.1}, 0.01]
};
   GeometricTransformation[b, {RotationMatrix[φ], {0, 0}}]
   ];

Graphics[body1[0,0.05], Frame -> True,PlotRange->{{-0.2,0.2},{-0.18,0.04}},ImageSize->500]

Переходим в 3D

Graphics3D[{Cylinder[{{0,0,0},{0,0,1}},0.1]}]
Graphics3D[{Sphere[{0,0,0},1]},Axes->True]

Освещение

Graphics3D[{Sphere[{0, 0, 0}, 1]},Axes->True,Lighting->"Neutral"]

Освещение

lp=2{1,1,1};
lightColor=Red;
Graphics3D[{
{White,Sphere[{0,0,0}, 1]},
{Specularity[0],lightColor,Sphere[lp,0.05]}
},Axes->True,Lighting->{{"Point",Green,lp},{"Ambient",GrayLevel[0.3]}}]

Освещение

lightColor=Red;
Animate[
Graphics3D[{
{White,Sphere[{0,0,0}, 1]},
{lightColor,Sphere[2{Cos[φ],Sin[φ],0},0.05]}
},Axes->True,Lighting->{{"Point",Green,2{Cos[φ],Sin[φ],0}},{"Ambient",GrayLevel[0.3]}},PlotRange->{{-2,2},{-2,2},{-2,2}}]
,{φ,0,2π}]

Импорт 3D объекта

Импорт 3D объекта из файла (.obj)

SetDirectory[NotebookDirectory[]];
object=Import["11804_Airplane_v2_l2.obj",{"GraphicsComplex"}];

Graphics3D[{object},Axes->True]

Импорт 3D объекта

Связанная с объектом система координат (начало координат определяется в файле с моделью)

SK={Red,Arrow[{{0,0,0},{20,0,0}}],Green,Arrow[{{0,0,0},{0,20,0}}],Blue,Arrow[{{0,0,0},{0,0,20}}]};

Масштабирование и перемещение (функции Translate и Scale)

Graphics3D[{SK,EdgeForm[None],Translate[Scale[object,0.02],{-145,0,-128}]},Axes->True]

Импорт 3D объекта

Поворот

Graphics3D[{EdgeForm[None],SK,
Rotate[
Translate[Scale[object,0.02],{-145,0,-128}],
180°,{0,0,1}
]
},Axes->True]

plane={EdgeForm[None],SK,Rotate[Translate[Scale[object,0.02], {-145,0,-128}],180°,{0,0,1}]};

Поворот

Поворот вокруг оси z на угол φ (рыскание) - RotationMatrix[φ,{0,0,1}]

Manipulate[
Graphics3D[GeometricTransformation[plane,{RotationMatrix[φ,{0,0,1}],{0,0,0}}],PlotRange->{{-20,20},{-20,20},{-20,20}}],
{φ,-90°,90°}
]

Два поворота

Перемножив матрицы получим сложный поворот по рысканию и крену

A[ψ_,θ_]:=RotationMatrix[ψ,{0,0,1}].RotationMatrix[-θ,{0,1,0}];

Manipulate[
Graphics3D[GeometricTransformation[plane,{A[ψ,θ],{0,0,0}}],PlotRange->{{-20,20},{-20,20},{-20,20}}],
{ψ,-90°,90°},
{θ,-90°,90°}
]

Три поворота

Рыскание, тангаж и крен

A[ψ_,θ_,φ_]:=RotationMatrix[ψ,{0,0,1}].RotationMatrix[-θ,{0,1,0}].RotationMatrix[φ,{1,0,0}];

Manipulate[
Graphics3D[GeometricTransformation[plane,{A[ψ,θ,φ],{0,0,0}}],PlotRange->{{-20,20},{-20,20},{-20,20}}],
{ψ,-90°,90°},
{θ,-90°,90°},
{φ,-90°,90°}
]

Интегрирование дифференциальных уравнений

Кинематические параметры шарика

Положение шарика относительно системы координат O (Subscript[x, 1])(Subscript[y, 1])(Subscript[z, 1]), связанной с первым телом

Радиус-вектор точки А относительно системы (Subscript[Ox, 1])(Subscript[y, 1])(Subscript[z, 1])

(Subscript[ρ, A])={-(Sqrt[(R^2)-(h^2)]),-2*h};

Радиус вектор точки М относительно точки А в системе координат (Subscript[Ox, 1])(Subscript[y, 1])(Subscript[z, 1])

(Subscript[ρ, M])=(Subscript[ρ, A])+{(Subscript[l, 0])+x[t],0};

Положение шарика относительно неподвижной системы координат Oxyz

(Subscript[r, M])=RotationMatrix[φ[t]].(Subscript[ρ, M])

Вектор абсолютной скорости шарика

(Subscript[v, M])=D[(Subscript[r, M]),t]//FullSimplify

Кинетическая энергия

Кинетическая энергия шарика

(Subscript[T, 2])=FullSimplify[(Subscript[m, 2])(Subscript[v, M]).(Subscript[v, M])/2]

Кинетическая энергия первого тела (стержень + сектор)

Стержень длиной h,массой  (Subscript[m, 11]),вращающийся вокруг оси O. Момент инерции (Subscript[m, 11])(h^2)/3

(Subscript[T, 11])=((Subscript[m, 11])(h^2)/3)*((φ'[t])^2)/2;

Сектор, вращающийся вокруг оси  О

(Subscript[J, 12])=(1/2)*(((Subscript[m, 12])(R^2))/2)+(Subscript[m, 12])(h^2);
(Subscript[T, 12])=(((Subscript[J, 12])*((φ'[t])^2))/2)

Кинетическая энергия системы

(Subscript[T, sys])=FullSimplify[(Subscript[T, 11])+(Subscript[T, 12])+(Subscript[T, 2])]

Потенциальная энергия шарика

Нулевой уровень - горизонтальная плоскость, проходящая через ось вращения O.

Потенциальная энергия шарика (координата y в неподвижной системе, умноженная на массу шарика и ускорение свободного падения.

(Subscript[Π, g2])=(Subscript[m, 2]) *g *(Subscript[r, M])[[2]]

Потенциальная энергия пружины

(Subscript[Π, c2])=((c(((x[t]-(Subscript[l, 0])))^2))/2);

Потенциальная энергия тела 1 (пластина)

Положение центра масс стержня и сектора относительно системы координат O (Subscript[x, 1])(Subscript[y, 1])(Subscript[z, 1])

(Subscript[ρ, 1])=((Subscript[m, 11]){0,-(h/2)}+(Subscript[m, 12]){0,-h-4 (R/(3 π))})/((Subscript[m, 11])+(Subscript[m, 12]));

Положение центра масс стержня и сектора относительно системы координат Oxyz

(Subscript[r, 1])=RotationMatrix[φ[t]].(Subscript[ρ, 1])

Координата y этого вектора будет высотой центра масс системы пластина + стержень относительно нулевого уровня энергии

(Subscript[Π, g1])=((Subscript[m, 11])+(Subscript[m, 12]))*g*(Subscript[r, 1])[[2]]

Потенциальная энергия системы

(Subscript[Π, sys])=(Subscript[Π, g2])+(Subscript[Π, c2])+(Subscript[Π, g1])

Уравнения движения

Уравнения движения

eq1=FullSimplify[D[D[(Subscript[T, sys]),φ'[t]],t]-D[(Subscript[T, sys]),φ[t]]==-D[(Subscript[Π, sys]),φ[t]]]

eq2 = FullSimplify[D[D[(Subscript[T, sys]),x'[t]], t] - D[(Subscript[T, sys]), x[t]] ==- D[(Subscript[Π, sys]), x[t]]]

Численное интегрирование

Параметры системы

params={(Subscript[m, 11])->1,(Subscript[m, 12])->1,(Subscript[m, 2])->0.3,R->1,(Subscript[l, 0])->0.4,g->9.807,c->9.0,h->R/2};

Интегрирование

numEq={eq1,eq2,φ[0]==0.3,φ'[0]==0.3,x[0]==0,x'[0]==0}//.params

sol=NDSolve[numEq,{φ[t],φ'[t],x[t],x'[t]},{t,0,5}]//Flatten

Plot[x[t]/.sol,{t,0,5},PlotTheme->{"Monochrome","FrameGrid"}]

Интеграл энергии

Проверка интеграла энергии

Plot[{(Subscript[T, sys]),(Subscript[Π, sys]),(Subscript[T, sys])+(Subscript[Π, sys])}//.params/.sol//Evaluate,{t,0.0,5.0},Frame->True,BaseStyle->14,FrameLabel->{"t, c"," T, Π, Дж"},PlotLegends->{"T","Π","T+Π"},PlotTheme->"Monochrome",ImageSize->1000]

Рисуем систему

Рисуем систему в соответствии с параметрами в списке params

ClearAll[body1]
body1[φi_,xi_] := Module[{b,rshar=0.05,dpru=0.1},
b={(* СТЕРЖЕНЬ *)
Darker[Blue, 0.1],Thickness[0.01],Line[{{0, 0}, {0, -h}}],
(* ПЛАСТИНА *)
Lighter[Blue, 0.85],Disk[{0, -h},R, {-180 °, 0 °}],
(* ОПОРА *)
White, EdgeForm[{Thick, Black}],Disk[{0, 0}, 0.03],
(* КАНАЛ *)
EdgeForm[{Thin, White}],Rectangle[(Subscript[ρ, A])+{-0.5,-dpru/2},(Subscript[ρ, A])+{2R,dpru/2}],
(* ПРУЖИНА *)
Red,Thin,Translate[Line[Spring[x[t]+(Subscript[l, 0]),dpru, 30]],(Subscript[ρ, A])],
(* ШАРИК *)
Blue,Disk[(Subscript[ρ, M]), rshar]
}/.{φ[t]->φi,x[t]->xi}//.params;
   GeometricTransformation[b, {RotationMatrix[φi], {0, 0}}]
   ];

Graphics[body1[0,0.05], Frame -> True,ImageSize->500]

Анимация

Animate[
Graphics[
body1[φ[t],x[t]]/.sol/.t->ti,PlotRange->{{-1.6,1.6},{-1.7,0.2}},ImageSize->600
],
{ti,0,5,0.05},
DisplayAllSteps->True]

Экспорт анимации

Анимацию можно сохранить в файл типа анимированный gif

Генерируем кадры: t от 0 до (Subscript[t, max]) с шагом 0.05 (100 кадров для 5 секунд) при помощи функции Table

aniTable=Table[
 Graphics[
body1[φ[t],x[t]]/.sol/.t->ti,PlotRange->{{-1.5,1.5},{-1.5,0.1}},ImageSize->600
],
 {ti, 0,5, 0.05}];

Первые пять кадров

aniTable[[1;;4]]

Экспорт

Для записи кадров в файл используем функцию Export

Export["animation.gif",aniTable]

Расширение имени файла (gif, avi) определит формат файла

Export["animation.avi",aniTable]

Файл получится большим, т.к. видео не сжато

Экспорт

Чтобы создался файл поменьше попытаемся использовать сжатие (формат MPEG-4)

Export["animation.avi", aniTable,"VideoEncoding"->"MPEG-4 Video"]

Список форматов, поддерживаемых системой (Windows)

Internal`$VideoEncodings

В некоторых операционных системах сжатие не поддерживается -- только Uncompressed (несжатый).