Pull to refresh

Анимация в программе Asymptote

Reading time25 min
Views1.6K
Скопировано с моего блога в целях создания еще одного русскоязычного источника информации по данной теме.

Здравствуйте! Когда я в процессе освоения программы Asymptote, происходившего по мере узнавания нюансов настройки этой прогры, описанных вот в этом хабе, полностью освоил обработку файлов .asy, в которых нет анимации, я, естественно, попер на анимацию.

Заголовок спойлера
Даешь анимацию в народные массы!

К счастью, для этого в случае формата выхода .pdf ничего из описанных мной дополнительных прогр в хабе по приведенной мной выше ссылке после прогры ghostscript доустанавливать не нужно, а так бы еще черт знает сколько времени прокопался. Но я об этом тогда не знал, что многократно усложняло дело. Обработаешь файл wheel.asy и что? Ну, появлялось .pdf при обработке в TeXworks (см. данный по ссылке выше хаб) в одной папке с обрабатываемым файлом .asy, а при открытии в проводнике (см. тот же хаб) — в папке C:\Windows\System32. Ну и что? Анимации в нем не было и все тут, хоть ты тресни. А до анимации в Asymptote я научился делать анимации в tikz и pstricks. Так у меня с этим не получалось до тех пор, пока я из папки C:\Program Files\MiKTeX 2.9\tex\latex\animate не удалил файл animate.sty и не поставил на его место файл с таким же названием, скачанный позже. Вот он. Таким образом, зная, что этот пакет работает с tikz и pstricks, я склонялся к мысли, что не получается у меня, скорее всего, не из-за проблем с ТеХ, а по какой-то другой причине. Так я проискал инфу в Инете несколько лет. Сначала искал в браузере IE, ну, там нет удобного переводчика. Потом разнюхал, что в браузере Гугл есть удобный переводчик. Тоже искал, искал, искал, искал, пока не наткнулся на вот эту страницу. Оказалось, что почему-то во всех файлах .asy из папки C:\Program Files\Asymptote\examples\animations для получения из них .pdf с анимацией не хватает одной-единственной строчки

settings.twice=true;

Добавил я эту строчку в файл wheel.asy из подпапки анимации папки примеров:

Наконец-то анимация!!!!!!!
import graph;

// Uncomment the following 2 lines to support pdf animations:
usepackage("animate");
settings.tex="pdflatex";
settings.twice=true;
import animation;

size(0,200);

defaultpen(3);
dotfactor=4;

pair wheelpoint(real t)
{
  return (t+cos(t),-sin(t));
}

guide wheel(guide g=nullpath, real a, real b, int n)
{
  real width=(b-a)/n;
  for(int i=0; i <= n; ++i) {
    real t=a+width*i;
    g=g--wheelpoint(t);
  }
  return g;
}

real t1=0; 
real t2=t1+2*pi;

animation a;

draw(circle((0,0),1));
draw(wheel(t1,t2,100),linetype("0 2"));
yequals(Label("$y=-1$",1.0),-1,extend=true,linetype("4 4"));
xaxis(Label("$x$",align=3SW),0);
yaxis("$y$",0,1.2);
pair z1=wheelpoint(t1);
pair z2=wheelpoint(t2);
dot(z1);
dot(z2);

int n=10;
real dt=(t2-t1)/n;
for(int i=0; i <= n; ++i) {
  save();
  
  real t=t1+dt*i;
  draw(circle((t,0),1),red);
  dot(wheelpoint(t));

  a.add(); // Add currentpicture to animation.
  restore();
}

erase();

// Merge the images into a gif animation.
//a.movie(BBox(0.25cm),loops=10,delay=250);

// Merge the images into a pdf animation.
label(a.pdf(BBox(0.25cm),delay=250,"controls",multipage=false));


При этом, чтобы получить .pdf, я, зная на английском слов 300, догадался, что нужно раскомментировать в той форме файла wheel.asy, в которой он достается пользователю от автора(-ов) прогры Asymptote, 2 строки:

.......................
usepackage("animate");
settings.tex="pdflatex";
.......................

Глядя же на комментарии к этим строкам в окончании файла wheel.asy:

........................
// Merge the images into a gif animation.
a.movie(BBox(0.25cm),loops=10,delay=250);

// Merge the images into a pdf animation.
// label(a.pdf(BBox(0.25cm),delay=250,"controls",multipage=false));

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

a.movie(BBox(0.25cm),loops=10,delay=250);

, а раскомментировать строку

label(a.pdf(BBox(0.25cm),delay=250,"controls",multipage=false));

И как покатилось у меня после этих телодвижений колесико! Вот тоже логика авторов прогры Asymptote непонятна: для получения .gif требуется на одну дополнительную программу больше, чем для получения .pdf. Файлы с примерами будут, скорее всего, смотреть пользователи, которые вообще не умеют это делать. Ну, так и делайте, чтобы эти пользователи при открытии этих файлов сразу, ничего в них не меняя, могли получать на выходе формат, который требует меньше дополнительных программ — .pdf, нет, они сделали .gif. Зачем?? Ну, да ладно. Да, еще отмечу, что при открытии файла wheel.asy в проводнике (см. мой хаб) для получения из него работоспособного .gif наличие в одной с ним папке файла config.asy(см. мой хаб) не является обязательным. Напомню, что .gif при этом (при открытии в проводнике) образуется в папке C:\Windows\System32. При обработке же этого файла .asy в TeXworks’е наличие config.asy в одной папке с обрабатываемым файлом обязательно (или включении строк, о которых я рассказывал по приведенной предыдушей ссылке, непосредственно в сам обрабатываемый файл .asy). Ну, и вот. Получил я это катящееся колесо Прям, обхохотался весь, обрадовалси. Дело было ночью. Лег я спать. Проснулся и за комп. Пока комп включался, сидел, руки потирал. Ну, думаю, сейчас наанимачусь математически за усю мазуту). Ага, щас. Анимации-то из подпапки примеров удались практически все, там, кроме embeddedmovie.asy, externalmovie.asy и embeddedu3d.asy. 2 первых файла я дожал потом, а с последним так и не получилось: там, я так понял, какой-то файл dice.u3d нужно дополнительно откуда-то скачать, а какой — большой секрет для маленькой компании. Ну, что, «Эх, королевство маловато, разгуляться негде». Написал я, было, такой код:

settings.twice=true;
usepackage("animate");
import solids;
settings.tex="pdflatex";
import animation;
settings.outformat="pdf";

settings.axes3=true;
import graph3;

import palette;
currentprojection=orthographic(1,1,1);

limits((-1.3,-1.3,-1.3),(1.3,1.3,1.3));

unitsize(6cm);
animation a;
int n=44;
for(int i=0; i<=n; ++i){
real g(real x) {return sqrt(1-x^2);}
pair G(real x) {return (x,g(x));}
triple G3(real x) {return (x,g(x),0);}

path q=graph(G,1-i/n,1);
path3 q3=path3(q);

revolution c=revolution(q3,X,0,360);
draw(surface(c),red);

real f(real x) {return sqrt(1-x^2);}
pair F(real x) {return (x,f(x));}
triple F3(real x) {return (x,f(x),0);}

path p=graph(F,-1,-1+i/n);
path3 p3=path3(p);

revolution b=revolution(p3,X,0,360);
draw(surface(b),green);

axes3("$x$","$y$","$z$",Arrow3);
a.add();
  restore();
}

erase();
label(a.pdf("controls,loop",delay=1000));

Получил в TeXworks'е следующую многообещающую ошибку:

error: out of memory

При открытии этого файла .asy в проводнике (см. ссылка на хаб чуть выше) ошибка была та же. Ага. парился, я парился, мутил я мутил, установил, что при $n=3$ еще получается .pdf с анимацией, а вот уже при $n=4$ что ни на есть эта самая ошибка и возникает. Давай я спрашивать на самом что ни на есть асимптотистом форуме Asymptote. Но англосаксы люди законопослушные, от инструкций отступать не любят. Не придумали они ничего. Что делать? Стоило ли перелопачивать пол-Интернета, чтобы споткнуться на такой фигне? Ну, ничего, русские не сдаются! Да к тому же нормальные герои всегда идуть в обходъ… Начал я наблюдать за процессом обработки файла wheel.asy в TeXworks и что же я увидел? Вначале появляются файлы _wheel+0.pdf, _wheel+1.pdf,..., _wheel+10.pdf, потом из этих файлов образуется многостраничный файл _wheel.pdf и дальше по программе. А чтобы это все сделать, в файл .asy, из которого собираются получить анимацию, импортируется модуль animation. И тут у меня возникла идея. А что, если в этом модуле (файл C:\Program Files\Asymptote\animation.asy) найти место, генерирующее файлы типа _wheel+0.pdf и отключить его? А эти файлы наштамповывать обработкой программой Asymptote файлов .asy, порождаемых обходом цикла в батнике? Эти файлы .asy можно сделать создающимися с именами имя+0.asy, имя+1.asy, и т. д. до стольки, до скольки нужно. Где имя — это какой-нибудь связанный с создаваемой анимацией набор латинских букв, который вы на уже готовом пдф с анимацией, скорее всего, захотите изменить. Но это будет потом. А пока, после создания файлов имя+0.asy, имя+1.asy… по моей задумке должна была следовать их обработка программой Asymptote. Это бы создавало файлы имя+0.pdf, имя+1.pdf… А перед кодом, создающим файлы имя+0.asy, имя+1.asy..., я в батник должен был вставлять строки для создания файла имя.asy, вернее, просто строки для ввода в этот файл. А файл же имя.asy образовывался бы просто в результате работы батника. В этот файл имя.asy я задумывал импортировать переделанный мной модуль animation вместо самого модуля animation. Начало у этого файла имя.asy я задумал сделать простейшим, в духе начала файла wheel.asy. После цикла я просто тупо копировал строки из этого файла и вставлял их в батник так:

echo строка >> имя.asy

,

echo строка >> имя.asy

Батник я писал так, чтобы в созданном файле имя.asy само тело цикла было пустым, а там, где for, получалось бы так:

for(int i=0; i <= n; ++i) {

В батнике строкой выше перед строкой кода, вводящей эту строку в файл имя.asy стоит строка

echo int n=%n%; >>%name%.asy

, а третья и четвертая строки этого батника такие:
set n=24
set name=OdnopGip

, так что к началу обработки цикла в файле имя.asy Asymptote уже знает значение n в данном случае и все срабатывает. Переменная среды name означает, в переводе на язык данной статьи, имя из имя.asy. Объявление в данном случае значений name или n в батнике через set представляется мне крайне удобным: изменение их значений всего лишь в одном месте батника влечет изменение вообще всех имен .tex, .pdf файлов, образующихся при обработке какого-нибудь определенного файла .asy или количества кадров в получаемом анимированном пдф, по сути изменение подробности анимации. Ну, и вот. Вот я наготовил кадры имя+0.pdf, имя+1.pdf..., при этом, т. к. после приготовления каждого кадра он открывался, то я в свой батник должен был включить команду для его закрытия. Т. к. я тогда не знал, как из батника закрыть окно с конкретным заголовком, то я в свой батник в тело цикла обработки кадро-заготовок .asy включил следующую команду:
taskkill /IM AcroRd32.exe

, что, как потом выяснилось в процессе обкатки этого всего, еще лучше, чем закрытие каждого конкретного кадра: иногда кадр еще не успевает появится на мониторе, а уже начинается обработка следующего файла .asy, по окончании которой на мониторе появляется следующий кадр, а конечной командой этого следующего шага цикла и закроются и кадр от этого следующего шага, и кадр предыдущего шага, мимо появления которого (кадра) пробежал работающий батник, хотя понятно, что после включения этой команды в батник во время его выполнения книги в .pdf уже не почитаешь, это плохо, да. Дальше по логике мне нужно было переименовать файлы имя+0.asy, имя+1.asy,… в _имя+0.asy, _имя+1.asy,..., т. к. наличие файлов именно с такими именами в одной папке с обрабатываемым файлом .asy, в котором есть анимация, предполагает обработка этого файла при условии импорта родного модуля animation перед началом изготовления файла имя.pdf (да, я мог бы внутри цикла батника с переменной k вгонять строки кода, составляющего кадр, не в файл имя+k.asy, а в файл _имя+k.asy, но это бы потребовало бо́льшего количества ввода символа "_". И дальше уже шла обработка программой Asymptote ранее созданного файла имя.asy. Короче, по первоначальной задумке батник должен был бы выглядящим следующим образом:

cd /d %0\..
echo off
set n=99
set name=cvetok

echo settings.twice=true; > %name%.asy
echo usepackage("animate"); >>%name%.asy
echo settings.tex="pdflatex"; >>%name%.asy
echo import izgraphiki; >>%name%.asy
echo animation a; >>%name%.asy
echo int n=%n%; >>%name%.asy
echo for(int i=0; i ^<= n; ++i) { >>%name%.asy
echo   a.add(); >>%name%.asy
echo  restore(); >>%name%.asy
echo } >>%name%.asy
echo erase(); >>%name%.asy
echo label(a.pdf(BBox(0.25cm),delay=250,"controls",multipage=false)); >>%name%.asy

for /l %%k in (0,1,%n%) do (
echo import graph; >> %name%+%%k.asy
echo settings.tex="pdflatex"; >> %name%+%%k.asy
echo limits((-1.2,-1.2^),(1.2,1.2^)^); >> %name%+%%k.asy
echo size(0,100^); >> %name%+%%k.asy
echo real f(real t^) {return cos(2*t^);} >> %name%+%%k.asy
echo path g=polargraph(f,0,2pi*%%k/%n%,operator ..^)--cycle; >> %name%+%%k.asy
echo fill(g,green+white^); >> %name%+%%k.asy
echo xaxis("$x$",above=true^); >> %name%+%%k.asy
echo yaxis("$y$",above=true^); >> %name%+%%k.asy
echo draw(g^); >> %name%+%%k.asy
)

for /l %%l in (0,1,%n%) do (
asy %name%+%%l.asy
taskkill /IM AcroRd32.exe
)
for /l %%l in (0,1,%n%) do (
rename %name%+%%l.pdf _%name%+%%l.pdf
)
asy %name%.asy

Тут мы видим, что я выполняю импорт упоминавшегося выше модифицированного мной модуля animation.asy. Этому модулю я дал незамысловатое имя izgraphiki.asy. Да и для получаемого мной файла .pdf с анимацией я не придумал имени, лучшего, чем cvetok. Видимо, из-за скудости своей фантазии. Ну, да ладно, уж как есть. Про «мой» модуль izgraphiki.asy я расскажу в самом окончании этого повествования, чтобы сейчас не потерять нить рассказа, а пока отмечу, что попытка дать этому файлу имя iz-graphiki.asy, а потом для пробы еще и iz+graphiki.asy не приводила к намеченной цели: работа моего батника останавливалась в самом разгаре и больше ни под каким видом не возобновлялась. Видимо, в модулях программы Asymptote должны быть только буквы латинского алфавита. Во всяком случае соблюдение этого условия уж точно не повредит. Итак, работа приведенного мной выше батника оканчивалась появлением вожделенного файла .pdf с анимацией из произвольно большого (в разумных пределах, чтоб не ждать часа 3, работать-то это работает, анимированное .pdf появляется, все нормально, но у компа-то скорость выполнения операций всего лишь конечная). Давай я делать анимационные построения 3d объектов. И тут выяснилась еще одна заморочка. Кадры с этими объектами мне удаваться-то удавалось наштамповать, но, ох, уж это но. На этих кадрах эти объекты получалось вращать в пространстве, тут все нормально, но как раз из-за этого наличия объема в кадрах-пдфках их нельзя использовать для построения кадров в пдф с анимацией. Давай я дальше думать. Наблюдая за обработкой немного измененного файла sphere.asy:

немного измененный файл sphere.asy
settings.twice=true; 
usepackage("animate"); 
import solids; 
 settings.tex="pdflatex"; 
import animation; 
settings.outformat="pdf"; 

currentprojection=orthographic((0,5,2));
currentlight=(0,5,5);

int nbpts=200;
real step=2*pi/nbpts;
int angle=90;

unitsize(1cm);

triple[] P=new triple[nbpts];
for(int i=0; i < nbpts; ++i) {
  real t=-pi+i*step;
  P[i]=(3sin(t)*cos(2t),3sin(t)*sin(2t),3cos(t));
}

transform3 t=rotate(angle,(0,0,0),(1,0.25,0.25));
revolution r=sphere(O,3);
draw(surface(r),lightgrey);
draw(r,backpen=linetype("8 8",8));

animation A;

for(int phi=0; phi < 360; phi += angle) {
  bool[] front=new bool[nbpts];
  save();
  for(int i=0; i < nbpts; ++i) {
    P[i]=t*P[i];
    front[i]=dot(P[i],currentprojection.camera) > 0;
  }
  draw(segment(P,front,operator ..),1mm+blue+extendcap);
  draw(segment(P,!front,operator ..),grey);
  A.add();
  restore();
}

label(A.pdf());


, я заметил, что в процессе этой обработки появляются и не исчезают последовательно все кадры анимации, но только они, если можно так сказать, в 2d версии: на взгляд это полноценная 3d графика, но если пытаться рассматривать их с разных сторон в окне просмотрщика Abode Reader с помощью перемещения с нажатой левой кнопкой мышки, как это ожидается от 3d графики, полученной с помощью программы Asymptote, если только возможность этого рассматривания не отключать целенаправленно перед началом изготовления .pdf с анимацией в файле .asy, то это оказывается невозможным. Имена этих 2d-кадров: _sphere+0.pdf, _sphere+1.pdf,… Узнав про генерацию пространственных 2d-кадров при обработке файлов .asy, в которых есть анимация 3d объектов, мне пришло в голову получать из батника .pdf-кадры для использования в дальнейшем при обработке файла .asy, в котором есть цикл с пустым телом, который я генерирую в самом начале работы батника, обработкой не просто файлов .asy, а обработкой файлов .asy, в каждом из которых есть цикл. Эти циклы в каждом из этих файле-заготовке .asy для получения из него кадра .pdf я сделал состоящими из одного-единственного кадра: эти заготовки я делал не для получения из них .pdf с анимацией, а для получения 2d-кадра, попутно возникающего в процессе обработки каждого из файла-заготовки .asy. Чтобы получать файлы-заготовки.asy с такими циклами, я сделал так, что в циклах всех файлов-заготовок.asy переменные этих циклов в начале и конце циклов принимают одинаковые значения — 0. Различия же между .pdf-кадрами, образующимися в результате обработки прогрой Asymptote файлов-заготовок.asy, вызываются различиями тел циклов файлов-заготовок.asy, которые (различия) в свою очередь обусловлены введением каждого из последовательных значений переменной цикла батника в функции тела цикла файлов-заготовок.asy, создаваемых батником, когда переменная его цикла, приняв очередное свое значение, замирает, ожидая окончания создания файла-заготовки.asy, соответствующего этому значению переменной цикла батника. Короче, эти различия создаются переменной цикла батника. Имена же у этих 2d-кадров имеют вид: имя+k+0.pdf, где, т. к. эти 2d-кадры являются следствием изменения переменной цикла батника, $k$ принимает значения от 0 до конечного значения переменной цикла батника с шагом 1. Для получения же анимированного .pdf из файла .asy с пустым телом цикла, созданного батником ранее, имена кадров-.pdf должны иметь следующий вид: _имя+k.pdf. Поэтому следующей командой в батнике я сделал переименование этих кадров-.pdf. Сделал я это в цикле. И, наконец, команда обработки прогрой Asymptote файла .asy с пустым телом цикла. После этой обработки и появляется .pdf c анимацией. В батнике же после этой команды я не написал команды для открытия файла .pdf c анимацией, потому что это открытие предполагается по умолчанию, этот файл .pdf c анимацией так и так открывается по окончании этой обработки, точно так же, как открывается файл .pdf, порожденный обработкой программой Asymptote файла .asy без анимации. Если читатель наблюдал процесс обработки файлов .asy из подпапки с анимациями папки примеров, находящейся в папке программы Аsymptote, то он поймет, что часть имя из _имя+k.pdf должна совпадать с именем файла .asy с пустым телом цикла. Файлы _имя+k.pdf являются результатом переименования некоторых других файлов .pdf (файлов имя+k+0.pdf), которые, в свою очередь являются побочным продуктом обработки программой некоторых файлов .asy поэтому для упрощения задачи я сделал так, чтобы эти файлы .asy создавались батником под именами имя+0.asy, имя+1.asy,… Так, ну, принцип работы задуманного мной батника я описал, пришла пора привести пример одного из таких батников. К примеру, такого:

cd /d %0\..
echo off
set n=44
echo settings.twice=true; >>sphera.asy
echo usepackage("animate"); >>sphera.asy
echo settings.tex="pdflatex"; >>sphera.asy

echo import izgraphiki; >>sphera.asy

echo animation a; >>sphera.asy

echo int n=%n%; >>sphera.asy
echo for(int i=0; i ^<= n; ++i) { >>sphera.asy
echo a.add(); >>sphera.asy
echo  restore(); >>sphera.asy
echo } >>sphera.asy

echo erase(); >>sphera.asy
echo label(a.pdf(BBox(0.25cm),delay=250,"controls,loop,autoplay",multipage=false)); >>sphera.asy

for /l %%k in (0,1,%n%) do (
echo settings.twice=true; >> sphera+%%k.asy

echo usepackage("animate"^); >> sphera+%%k.asy
echo import solids; >> sphera+%%k.asy
echo  settings.tex="pdflatex"; >> sphera+%%k.asy

echo import animation; >> sphera+%%k.asy

echo settings.outformat="pdf"; >> sphera+%%k.asy

echo settings.axes3=true; >> sphera+%%k.asy 
echo import graph3; >> sphera+%%k.asy

echo import palette; >> sphera+%%k.asy
echo currentprojection=orthographic(1,1,1^); >> sphera+%%k.asy

echo limits((-1.3,-1.3,-1.3^),(1.3,1.3,1.3^)^); >> sphera+%%k.asy

echo unitsize(6cm^); >> sphera+%%k.asy


echo animation a=animation(global=false^); //без опции (global=false^) возникает ошибка >>sphera+%%k.asy
echo //C:\Program Files\Asymptote/plain_shipout.asy: 87.10: runtime: Cannot rename _sphera+2+0_.pdf to >>sphera+%%k.asy
echo //_sphera+2+0.pdf >>sphera+%%k.asy
echo for(int i=0; i ^<= 0; ++i^) { >>sphera+%%k.asy
echo real f(real x^) {return sqrt(1-x^^2^);} >> sphera+%%k.asy
echo pair F(real x^) {return (x,f(x^)^);} >> sphera+%%k.asy
echo triple F3(real x^) {return (x,f(x^),0^);} >> sphera+%%k.asy

echo path p=graph(F,-1+i,-1+i+%%k/%n%^); >> sphera+%%k.asy
echo path3 p3=path3(p^); >> sphera+%%k.asy

echo revolution b=revolution(p3,X,0,360^); >> sphera+%%k.asy
echo draw(surface(b^),green^); >> sphera+%%k.asy

echo real g(real x^) {return sqrt(1-x^^2^);} >> sphera+%%k.asy
echo pair G(real x^) {return (x,f(x^)^);} >> sphera+%%k.asy
echo triple G3(real x^) {return (x,g(x^),0^);} >> sphera+%%k.asy

echo path q=graph(G,1-i-%%k/%n%,1-i^); >> sphera+%%k.asy
echo path3 q3=path3(q^); >> sphera+%%k.asy

echo revolution c=revolution(q3,X,0,360^); >> sphera+%%k.asy
echo draw(surface(c^),red^); >> sphera+%%k.asy

echo axes3("$x$","$y$","$z$",Arrow3^); >> sphera+%%k.asy
echo   a.add(^); >>sphera+%%k.asy
echo  restore(^); >>sphera+%%k.asy
echo } >>sphera+%%k.asy

echo erase(^); >>sphera+%%k.asy

echo label(a.pdf(^)^); >>sphera+%%k.asy

)

rem for /f %%l in (0,1,%n%) do asy '_sphera+%%l'.asy

for /l %%l in (0,1,%n%) do (
asy sphera+%%l.asy
taskkill /F /IM AcroRd32.exe
)

for /l %%l in (0,1,%n%) do rename sphera+%%l+0.pdf _sphera+%%l.pdf

asy sphera.asy

Обратите, пожалуйста, внимание на строку

echo for(int i=0; i ^<= n; ++i) { >>sphera.asy

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

for /l %%k in (0,1,%n%) do (

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

settings.twice=true; 
usepackage("animate"); 
settings.tex="pdflatex"; 
import izgraphiki; 
animation a; 
int n=44; 
for(int i=0; i <= n; ++i) { 
a.add(); 
 restore(); 
} 
erase(); 
label(a.pdf(BBox(0.25cm),delay=250,"controls,loop,autoplay",multipage=false));

Потом происходит генерация файлов sphera+0.asy, sphera+1.asy,..., sphera+44.asy, обработка каждого из которых впоследствии программой Asymptote в качестве побочного продукта даст 2d-кадры sphera+0.pdf, sphera+1.pdf,..., sphera+44.pdf. К примеру, содержание файла sphera+4.asy будет следующим:

settings.twice=true; 
usepackage("animate"); 
import solids; 
 settings.tex="pdflatex"; 
import animation; 
settings.outformat="pdf"; 
settings.axes3=true;  
import graph3; 
import palette; 
currentprojection=orthographic(1,1,1); 
limits((-1.3,-1.3,-1.3),(1.3,1.3,1.3)); 
unitsize(6cm); 
animation a=animation(global=false); //��� ����� (global ��������� ������ 
//C:\Program Files\Asymptote/plain_shipout.asy: 87.10: runtime: Cannot rename _sphera+2+0_.pdf to 
//_sphera+2+0.pdf 
for(int i=0; i <= 0; ++i) { 
real f(real x) {return sqrt(1-x^2);} 
pair F(real x) {return (x,f(x));} 
triple F3(real x) {return (x,f(x),0);} 
path p=graph(F,-1+i,-1+i+4/44); 
path3 p3=path3(p); 
revolution b=revolution(p3,X,0,360); 
draw(surface(b),green); 
real g(real x) {return sqrt(1-x^2);} 
pair G(real x) {return (x,f(x));} 
triple G3(real x) {return (x,g(x),0);} 
path q=graph(G,1-i-4/44,1-i); 
path3 q3=path3(q); 
revolution c=revolution(q3,X,0,360); 
draw(surface(c),red); 
axes3("$x$","$y$","$z$",Arrow3); 
  a.add(); 
 restore(); 
} 
erase(); 
label(a.pdf()); 

я этот код скопировал, открыв файл sphera+4.asy в TeXworks'е, а там кодировка установлена UTF-8:

image

В батнике же кодировка ANSI. Видимо, поэтому мой комментарий в теле второго цикла батника заменяется квадратами в этом файле-заготовке .asy, что, однако, нисколько не мешает его обработке программой Asymptote. После этой обработки из этого файла получается такой 2d-кадр. Я специально даю ссылку для скачивания одного из таких 2d-кадров, а не вставляю сюда фотку этого кадра, чтобы читатель мог увидеть непосредственно, что из себя представляет используемый мной 2d-кадр. Суть других файлов-заготовок .asy та же, только отличаются они друг от друга значениями аргументов функций в теле цикла. Дальше происходит переименование 2d-кадров sphera+0+0.pdf, sphera+1+0.pdf,..., sphera+44+0.pdf в _sphera+0.pdf, _sphera+1.pdf,..., _sphera+44.pdf, и, наконец, обработка программой Asymptote файла с пустым телом цикла sphera.asy. В результате получается вот такая анимация. Я дал ссылку на .gif только потому, что при попытке вставки самого .gif сюда анимация почему-то полностью не показывалась. Использовал же я .gif, а не .pdf потому что, как скорее всего, читатель этого кошмара знает, анимация в браузере, т. е., здесь, в .pdf не отображается, а в .gif отображается. Это .gif я получил только что, выполнением только что состряпанного на скорую руку мной батника следующего содержания:

cd /d %0\..
echo off
set n=44
echo import graph; >sphera.asy
echo settings.twice=true; >>sphera.asy
echo usepackage("animate"); >>sphera.asy
echo settings.tex="pdflatex"; >>sphera.asy

echo import izgraphikigif; >>sphera.asy

echo animation a; >>sphera.asy
echo yequals(Label("$y=-1$",1.0),-1,extend=true,linetype("4 4")); >>sphera.asy
echo int n=%n%; >>sphera.asy
echo for(int i=0; i ^<= n; ++i) { >>sphera.asy
echo a.add(); >>sphera.asy
echo  restore(); >>sphera.asy
echo } >>sphera.asy

echo erase(); >>sphera.asy
echo a.movie(); >>sphera.asy

for /l %%k in (0,1,%n%) do (
echo settings.twice=true; > sphera+%%k.asy

echo usepackage("animate"^); >> sphera+%%k.asy
echo import solids; >> sphera+%%k.asy
echo  settings.tex="pdflatex"; >> sphera+%%k.asy

echo import animation; >> sphera+%%k.asy

echo settings.outformat="pdf"; >> sphera+%%k.asy

echo settings.axes3=true; >> sphera+%%k.asy 
echo import graph3; >> sphera+%%k.asy

echo import palette; >> sphera+%%k.asy
echo currentprojection=orthographic(1,1,1^); >> sphera+%%k.asy

echo limits((-1.3,-1.3,-1.3^),(1.3,1.3,1.3^)^); >> sphera+%%k.asy

echo unitsize(6cm^); >> sphera+%%k.asy


echo animation a=animation(global=false^); //без опции (global=false^) возникает ошибка >>sphera+%%k.asy
echo //C:\Program Files\Asymptote/plain_shipout.asy: 87.10: runtime: Cannot rename _sphera+2+0_.pdf to >>sphera+%%k.asy
echo //_sphera+2+0.pdf >>sphera+%%k.asy
echo for(int i=0; i ^<= 0; ++i^) { >>sphera+%%k.asy
echo real f(real x^) {return sqrt(1-x^^2^);} >> sphera+%%k.asy
echo pair F(real x^) {return (x,f(x^)^);} >> sphera+%%k.asy
echo triple F3(real x^) {return (x,f(x^),0^);} >> sphera+%%k.asy

echo path p=graph(F,-1+i,-1+i+%%k/%n%^); >> sphera+%%k.asy
echo path3 p3=path3(p^); >> sphera+%%k.asy

echo revolution b=revolution(p3,X,0,360^); >> sphera+%%k.asy
echo draw(surface(b^),green^); >> sphera+%%k.asy

echo real g(real x^) {return sqrt(1-x^^2^);} >> sphera+%%k.asy
echo pair G(real x^) {return (x,f(x^)^);} >> sphera+%%k.asy
echo triple G3(real x^) {return (x,g(x^),0^);} >> sphera+%%k.asy

echo path q=graph(G,1-i-%%k/%n%,1-i^); >> sphera+%%k.asy
echo path3 q3=path3(q^); >> sphera+%%k.asy

echo revolution c=revolution(q3,X,0,360^); >> sphera+%%k.asy
echo draw(surface(c^),red^); >> sphera+%%k.asy

echo axes3("$x$","$y$","$z$",Arrow3^); >> sphera+%%k.asy
echo   a.add(^); >>sphera+%%k.asy
echo  restore(^); >>sphera+%%k.asy
echo } >>sphera+%%k.asy

echo erase(^); >>sphera+%%k.asy

echo label(a.pdf(^)^); >>sphera+%%k.asy

)

for /l %%l in (0,1,%n%) do (
asy sphera+%%l.asy
taskkill /F /IM AcroRd32.exe
)

rem for /l %%l in (0,1,%n%) do rename sphera+%%l+0.pdf _sphera+%%l.pdf

asy sphera.asy

Я не буду подробно описывать принцип работы этого батника, читатель его поймет, понаблюдав за процессом получения .gif из многострадального файла wheel.asy. Отмечу лишь, что отсутствие строки

yequals(Label("$y=-1$",1.0),-1,extend=true,linetype("4 4"));

в файле sphera.asy приводит к тому, что ничего не получается. Я даже представить себе не мог такое, потому что это совершенно не с чем увязать и потому выяснял это тупым удалением/«отменой удаления» в отдельном похожем файле .asy с пустым телом цикла. Для сравнения давайте посмотрим на анимированное .gif, собранное программой Imagemagick из 2d-кадров:

image

Блин, опять то же самое. Вот ссылка на фотокостинге ой, фотохостинге. А?

Я его получил выполнением команды magick *.eps sphere.gif в командной строке, запущенной из папки, в которой находятся 2d-кадры с расширением .eps. Эти кадры у меня остались от сборки .gif приведенным выше последним батником. Как говорится, найдите 10 различий)). Так, кажись, не забыл ничего. Осталось только рассказать, как я и обещал, о модифицированных мной модулях, полученных изменением одного и того же модуля animation.asy. Почему я тогда обещал расказать об одном модуле, а собираюсь рассказать о нескольких (о двух) модулях? Потому что в процессе написания этой статьи мне понадобилось изготовить анимированное .gif из большого числа кадров. При этом выяснилось, что использумый мной модуль izgraphiki.asy – модификация модуля animation.asy – для производства анимированного .pdf с большим числом кадров (возможно с как бы (см. выше) 3d графикой на них) совершенно не подходит для производства анимированного .gif с большим числом кадров (возможно с как бы (см. выше) 3d графикой на них). Впрочем, использование мной уже двух «собственных» модулей внимательный читатель мог заметить и сам. В «моем» модуле для генерации .pdf, заготовку для которого я получил копированием с переименованием файла animation.asy, мне пришлось закомментировать в определении (мне кажется, я сделал это с определением) чего-то все 4 строки, составляющие саму суть этого определения, оставив само это что-то, но с пустым определением: удаление вместе со своим определением и самого этого объекта вызывало ошибку: указывалось, что этот объект неизвестен. Сохранение же этого объекта в модуле, но с удаленными (или закомментированными) строками самого этого определения решило эту проблему. Вот эти строки модуля animation.asy:

string format=nativeformat();
plain.shipout(name,f,format=format,view=false);
files.push(name+"."+format);
shipped=false;

А вот, как эти же строки выглядят в модуле izgraphiki.asy:

//string format=nativeformat();
//plain.shipout(name,f,format=format,view=false);
//files.push(name+"."+format);
//shipped=false;

А вот и сам модуль izgraphiki.asy:

модуль izgraphiki.asy
/*****
* animation.asy
* Andy Hammerlindl and John Bowman 2005/11/06
*
* Produce GIF, inline PDF, or other animations.
*****/

// animation delay is in milliseconds
real animationdelay=50;

typedef frame enclosure(frame);

frame NoBox(frame f) {
return f;
}

enclosure BBox(real xmargin=0, real ymargin=xmargin,
pen p=currentpen, filltype filltype=NoFill) {
return new frame(frame f) {
box(f,xmargin,ymargin,p,filltype,above=false);
return f;
};
}

struct animation {
picture[] pictures;
string[] files;
int index;

string prefix;
bool global; // If true, use a global scaling for all frames; this requires
// extra memory since the actual shipout is deferred until all frames have
// been generated. 

void operator init(string prefix="", bool global=true) {
prefix=replace(stripdirectory(outprefix(prefix))," ","_");
this.prefix=prefix;
this.global=global;
}
string basename(string prefix=stripextension(prefix)) {
return "_"+prefix;
}

string name(string prefix, int index) {
return stripextension(prefix)+"+"+string(index);
}

private string nextname() {
string name=basename(name(prefix,index));
++index;
return name;
}

void shipout(string name=nextname(), frame f) {
// string format=nativeformat();
// plain.shipout(name,f,format="png",view=false); //generiruet!!!!
//files.push(name+"."+format);
// shipped=false;
}
void add(picture pic=currentpicture, enclosure enclosure=NoBox) {
if(global) {
++index;
pictures.push(pic.copy());
} else this.shipout(enclosure(pic.fit()));
}
void purge(bool keep=settings.keep) {
if(!keep) {
for(int i=0; i < files.length; ++i)
delete(files[i]);
}
}

int merge(int loops=0, real delay=animationdelay, string format="gif",
string options="", bool keep=settings.keep) {
string args="-loop " +(string) loops+" -delay "+(string)(delay/10)+
" -alpha Off -dispose Background "+options;
for(int i=0; i < files.length; ++i)
args += " " +files[i];
int rc=convert(args,prefix+"."+format,format=format);
this.purge(keep);
if(rc == 0) animate(file=prefix+"."+format,format=format);
else abort("merge failed");
return rc;
}

void glmovie(string prefix=prefix, projection P=currentprojection) {
if(!view() || settings.render == 0) return;
fit(prefix,pictures,view=true,P);
}

// Export all frames with the same scaling.
void export(string prefix=prefix, enclosure enclosure=NoBox,
bool multipage=false, bool view=false,
projection P=currentprojection) {
if(pictures.length == 0) return;
if(!global) multipage=false;
bool inlinetex=settings.inlinetex;
if(multipage)
settings.inlinetex=false;
frame multi;
frame[] fits=fit(prefix,pictures,view=false,P);
for(int i=0; i < fits.length; ++i) {
string s=name(prefix,i);
if(multipage) {
add(multi,enclosure(fits[i]));
newpage(multi);
files.push(s+"."+nativeformat());
} else {
if(pictures[i].empty3() || settings.render <= 0)
this.shipout(s,enclosure(fits[i]));
else // 3D frames
files.push(s+"."+nativeformat());
}
}
if(multipage) {
plain.shipout(prefix,multi,view=view);
settings.inlinetex=inlinetex;
}
shipped=true;
}

string load(int frames, real delay=animationdelay, string options="",
bool multipage=false) {
if(!global) multipage=false;
string s="\animategraphics["+options+"]{"+format("%.18f",1000/delay,"C")+
"}{"+basename();
if(!multipage) s += "+";
s += "}{0}{"+string(frames-1)+"}";
return s;
}

bool pdflatex() 
{
return latex() && pdf();
}

string pdf(enclosure enclosure=NoBox, real delay=animationdelay,
string options="", bool keep=settings.keep, bool multipage=true) {
if(settings.inlinetex) multipage=true;
if(!global) multipage=false;
if(!pdflatex())
abort("inline pdf animations require -tex pdflatex or -tex xelatex");
if(settings.outformat != "") settings.outformat="pdf";
string filename=basename();
string pdfname=filename+".pdf";

if(global)
export(filename,enclosure,multipage=multipage);
shipped=false;

if(!keep) {
exitfcn currentexitfunction=atexit();
void exitfunction() {
if(currentexitfunction != null) currentexitfunction();
if(multipage || !settings.inlinetex)
this.purge();
if(multipage && !settings.inlinetex)
delete(pdfname);
}
atexit(exitfunction);
}

if(!multipage)
delete(pdfname);

return load(index,delay,options,multipage);
}

int movie(enclosure enclosure=NoBox, int loops=0, real delay=animationdelay,
string format=settings.outformat == "" ? "gif" : settings.outformat,
string options="", bool keep=settings.keep) {
if(global) {
if(format == "pdf") {
export(enclosure,multipage=true,view=true);
return 0;
}
export(enclosure);
}
return merge(loops,delay,format,options,keep);
}
}

animation operator init() {
animation a=animation();
return a;
}


Отмечу, что строка

plain.shipout(name,f,format="png",view=false); //generiruet!!!!

этого модуля отличается от своей строки-прообраза в модуле animation.asy, но это след проведенных мной изысканий, я уж менять не стал: это ничему не мешает, а оказывать влияние при расскоментировании этих строк оказывает. А вот для получения анимированного .gif из строк, закомментированных мной в модуле izgraphiki.asy, мне потребовалось закомментировать в скопированном и переименованном в izgraphikigif.asy файле animation.asy лишь одну строчку —
plain.shipout(name,f,format= format,view=false);

, т. е. эта строка в модуле izgraphikigif.asy выглядит следующим образом:

//plain.shipout(name,f,format= format,view=false);

Конечно, можно было эту строку совсем удалить. Таким образом, модули animation.asy и
izgraphikigif.asy вообще различаются лишь в одной строке, но тем не менее. В свою очередь, попытка использовать модуль izgraphikigif.asy для изготовления анимированного .pdf не увенчалась успехом, так что свести эти 2 модуля к одному не получится. Хотя, быть может, как-нибудь через if, не знаю, утверждать не возьмусь: я, получается, на сегодняшний день не настолько хорошо знаю язык Asymptote. Отмечу, что, несмотря на то, что мои использования батников, подобных батнику описанной выше структуры, для создания других анимаций сработала, в одном случае мне это не удалось – при попытке получить анимированную эпициклоиду выполнением такого батника:

Неудавшаяся эпициклоида
cd /d %0\..
echo off
set n=2
set name=kriv
echo settings.twice=true; >%name%.asy
echo settings.prc=false; >> %name%.asy
echo // Uncomment the following 2 lines to support pdf animations: >>%name%.asy
echo usepackage("animate"^); >>%name%.asy
echo settings.tex="pdflatex"; >>%name%.asy

echo import sinoid; >>%name%.asy

echo animation a; >>%name%.asy

echo int n=%n%; >>%name%.asy
echo for(int i=0; i ^<= n; ++i^) { >>%name%.asy

echo   a.add(^); // Add currentpicture to animation. >>%name%.asy
echo  restore(^); >>%name%.asy
echo } >>%name%.asy

echo erase(^); >>%name%.asy

echo label(a.pdf(BBox(0.25cm^),delay=250,"controls",multipage=false^)^); >>%name%.asy

for /l %%k in (0,1,%n%) do (
echo import graph; > %name%+%%k.asy

echo settings.twice=true; >> %name%+%%k.asy
echo usepackage("animate"^); >> %name%+%%k.asy
echo settings.tex="pdflatex"; >> %name%+%%k.asy
echo import animation; >> %name%+%%k.asy
echo limits((-6.3,-6.3^),(6.3,6.3^)^); >> %name%+%%k.asy
echo size(0,100^); >> %name%+%%k.asy

echo animation a=animation(global=false^);>>%name%+%%k.asy
echo for(int i=0; i ^<= 0; ++i^) { >>%name%+%%k.asy
echo real r=0.925; >> %name%+%%k.asy
echo real R=3.61; >> %name%+%%k.asy
echo real d=1.39; >> %name%+%%k.asy
echo real x(real t^) {return (R+r^)*cos(t^)-d*cos((R+r^)*t/r^);} >> %name%+%%k.asy
echo real y(real t^) {return (R+r^)*sin(t^)-d*sin((R+r^)*t/r^);} >> %name%+%%k.asy
echo draw(graph(x,y,0,2*pi*%%k/%n%^),green^); >> %name%+%%k.asy
echo axes("$x$","$y$",Arrow^); >> %name%+%%k.asy

echo   a.add(^); // Add currentpicture to animation. >>%name%+%%k.asy
echo  restore(^); >>%name%+%%k.asy
echo } >>%name%+%%k.asy
echo erase(^); >>%name%+%%k.asy
echo // Merge the images into a pdf animation. >>%name%+%%k.asy
echo label(a.pdf(^)^); >>%name%+%%k.asy
)

rem for /f %%l in (0,1,%n%) do asy '%name%+%%l'.asy

rem asy *.asy
for /l %%l in (0,1,%n%) do (
asy %name%+%%l.asy
taskkill /IM AcroRd32.exe
)

for /l %%l in (0,1,%n%) do rename %name%+%%l.pdf _%name%+%%l.pdf

asy %name%.asy


. Хоть убей, не пойму, почему это не получается: файл kriv.asy с пустым циклом образуется, 2d-кадры.pdf образуются, в каждом из них есть графика, все ОК, Обработка файла kriv.asy программой Asymptote проходит без сучка и задоринки, появляется файл kriv.pdf ненулевого размера, его открываешь, там, как обычно для .pdf, полученных из файлов .asy такого содержания: кнопки управления анимацией, над ними белый прямоугольник, в котором должны мелькать кадры анимации. Должны, да не обязаны: По белому прямоугольнику или кнопку play щелкаешь, эта кнопка заменяется кнопкой Пауза, все отлично, а кадров нет и все тут, один белый прямоугольник! Почему – вопрос на миллион. Тут, кстати, виден еще один момент, который ставит меня в тупик: я использовал имя kriv вместо само собой напрашивающегося имени epicykloida. При попытке использовать это имя ничего не получалось, как не получалось и при создании другой анимации использовать имя Astroida. Изменение в обоих случая в батниках значения одной только переменной name в одном случае на krivaja, в другом – на kriv сразу привело к появлению .pdf с нормально отображающейся анимацией. Но понятно, что эта проблема вызывается программой Asymptote: и при попытке обработки, например, файла с пустым телом цикла Astroida.asy, наблюдается та же самая проблема. Что касается изготовления файлов формата .mpg из произвольно большого (повторюсь, в разумных пределах) числа кадров в духе этой статьи, то я этим пока не занимался, но, я думаю, каких-либо непреодолимых проблем возникнуть не должно. Да, пока вспомнил, это не относится к описываемым здесь батникам, а относится к анимации в Asymptote вообще: файл .asy, из которого получается анимированное .pdf отличается от файла .asy, из которого получается анимированное .gif, одной-единственной, последней, строкой.

Простейшая последняя строка файла .asy, из которого получится анимированное .pdf, такая:

label(a.pdf());

Простейшая же последняя строка файла .asy, из которого получится анимированное .gif, выглядит так:

a.movie();

А простейшая последняя строка файла .asy, из которого получится работоспособный .mpg, такая же, как простейшая последняя строка файла .asy, из которого получится анимированное .gif. При этом работоспособный .mpg появляется уже при обработке файла .asy, в который не вписана так долго искомая мной строка

settings.twice=true;

Вот теперь, кажется, все.
Tags:
Hubs:
+4
Comments2

Articles