LINUX.ORG.RU

Научная визуализация - масштабируемые графики типографского качества?

 , ,


0

2

Как вы знаете, учОные пишут статьи. Как правило получаются какие то данные, на основе этих данных делаются картинки а дальше эти картинки вставляются в статьи, причем один и та же картинка может быть вставлена в несколько разных статей (и презентаций). Дальше будет много букв, но тема сложная, куда деваться;-(

Картинка обычно устроена следующим образом:

  4 +-------------+  +-+ 4
  3 |             |  |P| 3
Y 2 |   графики   |  |A| 2 Z 
  1 |             |  |L| 1 
  0 +-------------+  +-+ 0 
    0  1  2  3  4  
           X

В середине график, вокруг зарамочное оформление (чиселки на осях и метки на осях), сбоку опционально может быть палитра (обозначена PAL, если цветом рисуется функция двух переменных).

Дальше начинаются проблемы. Когда картинка вставляется в статью, она должна удовлетворять двум требованиям - шрифтовке и выравниванию.

Шрифтовка это типографское требование, шрифт на картинке должен совпадать со шрифтом в тексте но быть на кегль меньше. Естественно формулы/математические обозначения в метках на осях должны совпадать с текстом, быть красивыми и пр. В журналах как правило требования к размеру шрифту не очень жесткие, но шрифт должен быть визуально приемлемым - не слишком большим и не слишком маленьким. Самая печалька, что на графике тоже могут быть надписи (легенда, кривая какого цвета что значит), и они не должны пересекаться кривыми.

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

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

Поддержку LaTeX-а и правильную шрифтовку умеет делать например gnuplot в режиме epslatex - зарамочное оформление сбрасывается в .tex файл, график в .eps файл. Главный минус такого решения - если картинку начать масштабировать (это сложно, там размеры прибиты гвоздями наглухо, но гипотетически возможно) - то численные подписи к осям могут начать налезать друг на друга. Можно эту конструкцию сразу скопмилять в один .pdf (именно так я сейчас и делаю), но тогла при масштабировании поплывет шрифтовка, да и выравнивание превратится в лютый геморрой (в сложном случае) - понятно что размеры графика в центре при этом теряются. Подписи к оси X / палитру не отключишь.

Хочется некоторый формат для научной графики, отвечающий следующим требованиям:

  1. В файл сохраняются график, палитра (например в .png) + зарамочное оформление в формате (пределы по оси, метка к оси). Понятно что должна быть смотрелка для такого формата на экране.

  2. Есть утилита которая умеет из этого сделать например .png заданного размера с заданным шрифтом, чиселки вдоль осей она расставляет сама так что чиселки не налезают друг на друга. То же самое с подписями на графике.

  3. Та уже утилита умеет сразу сделать выравненную пачку картинок которые тупо вставляются в LaTeX таблицу.

  4. В идеале утилита должна дергаться из теха просто при указании каких то макросов в таблице или при вставке отдельных картинок.

Я такой штуки не знаю, но я ее сильно хочу и наверное готов сделать. Но может что то уже есть готовое?

@Crocodoom, @thunar, @Evgueni


UPD: сухой остаток после раздумий и обсуждения такой.

Можно сделать некий формат для верстки уже подготовленного изображения (т.е. понятно что рисовать и как рисовать, осталось нарисовать красиво). Бонусы в пользу такого решения не очевидны, но они есть - мы разделяем модель данных и представление. Формат должен быть простым и расширяемым.

  1. все что относится к картинке хранится в отдельной директории/tar/tar.gz/zip.

  2. система координат связана с графиком, все меряется в долях графика, (0,0) - нижний левы угол, (1,1) - верхний правый.

  3. текстовый файл axes описывает оси. Каждая ось это одна строка,

x0 y0 x1 y1 min max [logscale] подпись-к-оси

первые четыре числа координаты начала и конца оси на графики, дальше пределы, опционально логарифмический масштаб. Во всех подписях поддерживается LaTeX

  1. текстовый файл paletter задает палитру, в первой строчке
min max [logscale] подпись-к-палитре

во второй строчке сама палитра (цвета через пробел)

  1. изображение лежит в файле image.ppm или любом разумном графическом формате

  2. линии (кривые) лежат в файлах с имя.curve, каждая кривая в своем файле, первая строка #ключ-кривой, дальше в два столбца данные. Пустая строка означает разрыв в линии. Можно сохранять линии в бинарном формате.

  3. можно добавлять векторные поля и всяко разно.

Перемещено Shaman007 из development

★★★★★

Последнее исправление: AntonI (всего исправлений: 3)
Ответ на: комментарий от Eddy_Em

Немного это сколько? Как минимум по одной на аргумент + всяко разно?

У меня три (импорт модуля, создание объекта расчета, подключение к нему модели). При этом автоматом делается куда больше чем обычный CLI - все параметры модели становятся доступны для изменения из командной строки, возможной серийный запуск (не один расчет а серия по сетке значений параметров, в том числе в многопоточном режиме с балансировкой нагрузки), все расчеты сохраяются в базу с возможностью последующего анализа и т.д.

Интерактив ненужен, какой на кластере интерактив

AntonI ★★★★★
() автор топика
Ответ на: комментарий от Eddy_Em

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

$ date
Wed Jun  2 15:19:05 MSK 2021
$ nano factorial.py
$ date
Wed Jun  2 15:20:05 MSK 2021
$ chmod a+x factorial.py
$ ./factorial.py 3
$ ./factorial.py 100
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
$ cat factorial.py 
#!/usr/bin/python
import sys
print reduce(long.__mul__, range(1, int(sys.argv[1])+1), 1L)

Давай, вперед, с засечкой времени, только честно. У меня на это ушла как ты видишь ровно минута - ты управишься за 10-20 секунд, не так ли? Только длинную целочисленную арифметику не забудь;-)

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

AntonI ★★★★★
() автор топика
Ответ на: комментарий от AntonI

Только длинную целочисленную арифметику не забудь

Никогда этого не использовал, так что на SO придется дольше искать ☺

готовые функции для факториала нет

Так ты готовую функцию использовал уже!

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

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

А у меня интроспекция и monkey patch.

ну и в базу расчеты твоя библиотека не кладет, пакетный запуск не обеспечивает;-)

AntonI ★★★★★
() автор топика
Ответ на: комментарий от Eddy_Em

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

Kek. Ты, блин, даже конкатенацию 2-х строк в твоей любимой «сишечке» с ошибками сделал )))

htower_ ★★
()
Ответ на: комментарий от Eddy_Em

Никогда этого не использовал, так что на SO придется дольше искать

Ок, давай на обычной арифметике - но тогда с контролем переполнения и выводом сообщения об ошибке.

Так ты готовую функцию использовал уже!

Нет, reduce стандартная функция из ФП. Заметь, я из модулей только sys цепанул что бы аргументы получить.

AntonI ★★★★★
() автор топика
Ответ на: комментарий от Eddy_Em

Ну и сравни вот эту свою портянку

myoption cmdlnopts[] = {
    // set 1 to param despite of its repeating number:
    {"help",    NO_ARGS,    NULL,   'h',    arg_none,   APTR(&help),        "show this help"},
    {"node",    NEED_ARG,   NULL,   'n',    arg_int,    APTR(&GP.nodenum),   "encoder node number"},
    {"reset",   NO_ARGS,    NULL,   'r',    arg_none,   APTR(&GP.reset),     "reset encoder"},
    {"verbose", NO_ARGS,    NULL,   'v',    arg_int,    APTR(&GP.verbose),   "show more info"},
    {"motorid", NEED_ARG,   NULL,   'i',    arg_int,    APTR(&GP.motorID),   "motor controller address"},
    {"gotopos", NEED_ARG,   NULL,   'g',    arg_double, APTR(&GP.gotopos),   "target focus position"},
    {"targspeed",NEED_ARG,  NULL,   't',    arg_double, APTR(&GP.targspeed), "move motor with constant speed (rev/min)"},
    {"stop",    NO_ARGS,    NULL,   's',    arg_none,   APTR(&GP.stop),      "stop motor"},
    {"monitor", NEED_ARG,   NULL,   'm',    arg_double, APTR(&GP.monitspd),  "move a little with given speed with monitoring"},
    {"eswstate",NO_ARGS,    NULL,   'e',    arg_none,   APTR(&GP.showesw),   "show end-switches state"},
    {"logfile", NEED_ARG,   NULL,   'l',    arg_string, APTR(&GP.logname),   "logfile name and path"},
    {"server",  NO_ARGS,    NULL,   'S',    arg_none,   APTR(&GP.server),    "work as server"},
    {"port",    NEED_ARG,   NULL,   'P',    arg_string, APTR(&GP.port),      "server port number (default: " DEFPORT ")"},
    {"host",    NEED_ARG,   NULL,   'H',    arg_string, APTR(&GP.host),      "host to connect (default: localhost)"},
    {"standalone",NO_ARGS,  NULL,   'A',    arg_none,   APTR(&GP.standalone),"run as standalone application"},
    {"pidfile", NEED_ARG,   NULL,   'p',    arg_string, APTR(&GP.pidfilename),"name of PID-file (default: " DEFPIDNAME ")"},
    {"preset",  NEED_ARG,   NULL,   '0',    arg_longlong,APTR(&GP.chpresetval),"change preset value"},
    {"nomotor", NO_ARGS,    NULL,   'M',    arg_none,   APTR(&GP.nomotor),   "don't initialize motor"},
    {"noencoder",NO_ARGS,   NULL,   'E',    arg_none,   APTR(&GP.noencoder), "don't initialize encoder"},
    {"focout",  NEED_ARG,   NULL,   'f',    arg_string, APTR(&GP.focfilename),"filename where to store focus data"},
    end_option
};

с тем что у меня:

model = calc.wrap(Model())
model.stoch0 = False
model.parallel = True
model.f_rank = 2
model.f_use = True
model.data_rank = 6
model.J = 1.
model.dt = 1e-2
model.T = 1.
model.K = 0.
model.gamma = 1.
model.alpha = 0.1
model.Hext = vecf(0., 0., calc.Hz)
model.nK = vecf(0., 0., 1.)
model.M0 = vecf(0., 0., 1.)

Tc = 1.5 if cvar.mode=='CCC' else 2.2 if cvar.mode=='VCC' else 3.3
if Tc-.8<=model.T and model.T<=Tc+1.4: calc.t_relax0 *= 2
if Tc-.4<=model.T and model.T<=Tc+.7: calc.t_relax0 *= 2
if Tc-.2<=model.T and model.T<=Tc+.3: calc.t_relax0 *= 2
calc.t_max = calc.t_relax0+2*calc.t_relax+2*calc.t_eq

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

AntonI ★★★★★
() автор топика
Последнее исправление: AntonI (всего исправлений: 1)
Ответ на: комментарий от Evgueni

Там тупо бизнес

Вот поэтому там все работает. А если не работает, то помогут умные дяди из техподдержки. И можно обойтись без тупых постов на ЛОР.

Djanik
()
Ответ на: комментарий от AntonI

Да, но при сложении двух векторов нампая насколько я понимаю дергается виртуальная функция на каждую ячейку вектора. При сложении и умножении - две. Ну или на каждую операция в AST запускается отдельный цикл и создается временный массив с результатом, даже не знаю что хуже. Быстро (по сравнению с Ъ плюсами) это не будет.

Надо бы бенчи погонять.

У меня вон самая сложная штука с точки зрения вычислений: насчитывание суммы логарифмов от билинейной формы с эрмитовской матрицей 8x8 от примерно миллиона событий, хотя бывает и сильно меньше, сильно больше уже не бывает. Неужто нумпай будет тормозить на таком?

luke ★★★★★
()
Ответ на: комментарий от luke

В питоне я могу взять любой класс и в рантайме его расковырять - получить список методов, полей, типы полей, поменять какие то поля и т.д.

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

AntonI ★★★★★
() автор топика
Ответ на: комментарий от luke

С т.з. написания CLI - за счет интроспекции я беру экземпляр любого класса и могу менять его параметры через аргументы командной строки как хочу. Могу даже методы дергать.
Фактически я могу сделать тонкий клиент для экз класса и дергать его из баша.

AntonI ★★★★★
() автор топика
Ответ на: комментарий от luke

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

AntonI ★★★★★
() автор топика
Ответ на: комментарий от AntonI

Я правильно понимаю, что в плюсах чтобы так сделать — это нужно либо самому руками для каждого поля и метода сварганить опции для того же boost, либо же упороться в метапрограммировании?

luke ★★★★★
()
Ответ на: комментарий от AntonI

И ты умеешь мультиаргументы (когда один и тот же параметр с разными аргументами может встречаться или, если он без аргументов, то инкрементировать соответствующую переменную)? И ты умеешь подаргументы (типа --effects eff1=shade,eff2=smooth …)?

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от luke

Да. Нужно как минимум перечислить поля класса в макросе (у меня такой есть).

А можно ничего не перечислять а просто взять SWIG который пробросит это в питон;-)

AntonI ★★★★★
() автор топика
Последнее исправление: AntonI (всего исправлений: 1)
Ответ на: комментарий от Eddy_Em

Может, ты заглянешь в исходники своей библиотеки? И увидишь там тонны кода на С++!!!

Я то привожу не код библиотеки а код ее использования. Если ты про сырцы питона то они на Сях, плюсов там нет. Если ты про сырцы моей библиотеки - то плюсы там есть, но совсем для другого. Разбор аргументов делается на питоне.

И ты умеешь подаргументы

Ты не поверишь… я даже умею подподподаргументы. Число «под…» ограниченно только числом вложений обрабатываемого питоновского класса. Оболочка для гнуплота у меня именно так и работает.

Где твой код для факториала с обычной арифметикой? «пыхтонист» сделал его за минуту, ты утверждал что пишешь в разы быстрее - так вот, 20 секунд давно прошли;-)

AntonI ★★★★★
() автор топика
Последнее исправление: AntonI (всего исправлений: 1)
Ответ на: комментарий от luke

Слишком сложно. Это IMHO и есть основная причина почему каждый новый год изобретают очередного убийцу C++. Просто каждому очевидно, что это бездна.

Evgueni ★★★★★
()
Ответ на: комментарий от AntonI

Не, ну это как-то всё же не показательно, потому что для факториала можно сделать проще:

$ calc
C-style arbitrary precision calculator (version 2.12.5.0)
Calc is open software. For license details type:  help copyright
[Type "exit" to exit, or "help" for help.]

; 100!
	93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
; 

Ну или:

> maxima

Maxima 5.41.0 http://maxima.sourceforge.net
using Lisp GNU Common Lisp (GCL) GCL 2.6.12
Distributed under the GNU Public License. See the file COPYING.
Dedicated to the memory of William Schelter.
The function bug_report() provides bug reporting information.
(%i1) 100!;
(%o1) 933262154439441526816992388562667004907159682643816214685929638952175999\
932299156089414639761565182862536979208272237582511852109168640000000000000000\
00000000
(%i2) 

Простые вещи везде делаются просто. А вот как сделать относительно сложные вещи, если ты «так себе погромист» за разумное время. Вот в чём вопрос.

Evgueni ★★★★★
()
Ответ на: комментарий от Djanik

Хочу в страну розовых невидимых пони. Не, честно хочу, но это увы и ах фантазии...

Evgueni ★★★★★
()
Ответ на: комментарий от Evgueni

Зато плюсы в каждом утюге есть (пока ещё). Даже с фортраном больше заморачиваться приходится.

luke ★★★★★
()

Научный сотрудник не должен париться над деталями вёрстки. Оставьте эту работу верстальщикам.

yvv ★★☆
()
Ответ на: комментарий от Evgueni

А вот как сделать относительно сложные вещи, если ты «так себе погромист» за разумное время

Универский курс по программированию по-идее должен не про разные язычки и гуи на плюсах, а про алгоритмы и структуры данных (хоть на сях, хоть даже на паскале — совсем не имеет значения на самом деле). А уже новомодные языки или навороты можно будет самостоятельно осваивать, если привить вкус к качественным книгам.

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

Понятно, что 90% будет до лампочки, ну так они и в институт не пойдут потом.

И ещё было бы неплохо давать численные задачки на курсе алгебры, хотя бы в качестве альтернативных месов (вместо насчитывания детерминантов 3x3 руками).

luke ★★★★★
()
Ответ на: комментарий от luke

должен

Университетский курс вообще-то про специальность должен. И на это по хорошему и должно уходить фактически всё время. Но что-то я размечался...

Evgueni ★★★★★
()
Ответ на: комментарий от Evgueni

Это скорее преимущество, не то чтобы это что-то хорошее. Просто удобно.

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

luke ★★★★★
()
Ответ на: комментарий от yvv

Да что ты говоришь! Т.е. ты думаешь, что если накарябаешь от руки текст на бумажках, нарисуешь от руки графики, отсканируешь это и вышлешь в редакцию, это примут?

Как минимум, ты должен подготовить хоть приблизительный макет, выдать нормальные рисунки, формулы правильные написать... В общем, если даже супер-пупер главный научный сотрудник латех не знает, у него всегда есть товарищ чином помладше, который оформит его статью правильно.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

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

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

yvv ★★☆
()
Последнее исправление: yvv (всего исправлений: 1)
Ответ на: комментарий от yvv

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

А в astro-ph вообще элементарно выкладывается: готовишь исходники и заливаешь их туда, они там собираются и в итоге ты видишь свой же pdf, к которому лишь добавлен необходимый минимум информации...

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

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

А потом журнал его берёт и верстает по новой. Да-да, именно так и происходит.

yvv ★★☆
()
Ответ на: комментарий от luke

Тем что на питоне разбор аргументов для конечного пользователя вообще не требует никаких телодвижений?

AntonI ★★★★★
() автор топика
Ответ на: комментарий от Evgueni

Я же специально написал - не надо пользоваться встроенной функцией факториала:-)

Для таких «соревнований» простые задачи самое оно.

Но хочется че то посложнее - могу предложить @Eddy_Em написать на сишечке символьное дифференцирование, и сравнить скорость разработки и число строк с питоном. Подобные можно не приводить:-)

AntonI ★★★★★
() автор топика
Ответ на: комментарий от AntonI

И какой алгоритм выравнивания тиков у вас тогда? Я вижу это так: зная диапазон на оси, определяем шаг тика. Зная шаг и диапазон, определяем максимальное количество знаков в тике. Размер шрифта известен, можно найти (не совсем точно, но насколько тут важна эта небольшая ошибка?) ширину области тиков и соответственно, расстояние между столбцами таблицы. У меня такая задача крайне редко возникает, обычно в таблице набор рисунков с одинаковыми диапазонами по осям, так что подписи к тикам ставлю только на одной строке/столбце, но в общем виде я бы решал эту задачу как описал.

JaM
()
Ответ на: комментарий от JaM

Шаг тика мы не знаем, он зависит от шрифта, диапазона и размера изображения и определяется автоматически. Еще не хватало его руками задавать…

Алгоритм для его определения неск десятков строк на питоне. Выбирается в итоге оптимальное разбиение. Но это только начало

AntonI ★★★★★
() автор топика
Ответ на: комментарий от AntonI

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

JaM
()
Ответ на: комментарий от AntonI

С точки зрения вычислительной среды это простой оператор. С тем же успехом можно написать не пользоваться оператором сложения. Простые задачи ничего не доказывают. Они уже все попеределаны по 100 раз и можно использовать cut and paste.

Evgueni ★★★★★
()
Ответ на: комментарий от JaM

Скажем в гнуплоте при определении шага тиков шрифт и размер картинки не учитываются. В матплотлибе учитывается но криво, регулярно лажает. Так что даже это нормально не сделано - а тут все спрашиваются зачем таким заниматься…

можно найти (не совсем точно, но насколько тут важна эта небольшая ошибка?) ширину области тиков и соответственно, расстояние между столбцами таблицы.

Ошибка в несколько pt означает что у Вас нет выравнивания.

AntonI ★★★★★
() автор топика
Ответ на: комментарий от Evgueni

Если речь идет о сравнении скорости разработки (мы ведь об этом, да?) то именно простые задачи доказывают все. Время решения сложной задачи зависит от того кто ее решает - понятно что Эдди быстрее сделает софтину для обработки изображения а я быстрее справлюсь с решением какого нить УРЧП. О ЯП это не скажет ровным счетом ничего, это лишь скажет о том что Эдди лучше обрабатывает изображения а я лучше решаю УРЧП.

AntonI ★★★★★
() автор топика
Ответ на: комментарий от AntonI

В гнуплоте не учитываются, но вы всё равно пишите обёртку, так что рассчтитать шаг и выдать его гнуплоту вполне можете.

Несколько pt? Да бросьте, все символы в тике вам известны, определить один раз, а потом складывать ширины - не проблема. Можно с помощью ghostscript и точно определять ширину для каждого тика, но это такое себе удовольствие, медленно слишком будет.

JaM
()
Ответ на: комментарий от JaM

В гнуплоте не учитываются, но вы всё равно пишите обёртку, так что рассчтитать шаг и выдать его гнуплоту вполне можете.

Я судя по всему в этой задаче уйду от гнуплота.

все символы в тике вам известны, определить один раз, а потом складывать ширины - не проблема.

«Никакая работа не кажется сложной если тебе не приходиться ее делать самому»(с)

Ну и какая ширина будет у вот такой штуки $1.5\times10^{-3}$ ?

AntonI ★★★★★
() автор топика
Ответ на: комментарий от AntonI

Я сторонник «рецептурного программирования», а так как по всем простым случаям есть подробные рецепты, то в этих случаях в реальной работе время на написание равно времени на поиск рецепта по оглавлению в решебнике. А дальше cut and paste.

Evgueni ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.