среда, 21 марта 2012 г.

C++ суперпозиция функций, функторы

Недавно заинтересовал вопрос: можно ли в C++ получить суперпозицию нескольких функций, как одну, самостоятельную функцию. Ответ: можно. Для этого используются указатели на функции и функторы. Функтором в С++ является класс, экземпляры которого интерпретируются, как функции. Делается это следующим образом: создаётся некоторый класс, у которого перегружается оператор (), в классе хранятся указатели на нужные функции, затем в операторе () происходит вычисление.
Простой пример: есть две функции они принимают float и возвращают float. Тогда класс, для суперпозиции таких функций можно записать, как:
typedef float(*nw)(float);

class functest{
 public:
 functest(){
  a = 0;
  b = 0;
 }
 functest(nw a1, nw b1){
  a = a1;
  b = b1;
 }
 void setfn(nw a1, nw b1){
  a = a1;
  b = b1;
 }
 float operator ()(float t){
  return b(a(t));
 }
 private:
 nw a, b;
};
Пример использования:
float one(float x){
 return x + 1;
}

float two(float b){
 return 2*b;
}

int main(){

 functest r(one,two), r1(two, one);
 float x = r(2);
 float x1 = r1(2);
 printf("%f %f",x,x1);
 getch();

}
Результатом работы программы будет x = 6, x1 = 5.
Помоем, достаточно удобный аппарат, особенно для математических расчётов: программирование интегральных преобразований, зависящих от параметра, производных функции, расположений в ряды. В графике может использоваться, для преобразований координат. Вообще везде - для сокращения записи.
Явным минусом является то, что обращение к функции через указатель занимает достаточно время.

воскресенье, 4 марта 2012 г.

Выбор юнитов для стратегий. Теоретическая часть для реализации.

После прочтения статьи про выбор объектов в OpenGL, мне особенно понравился метод выбора, используя буфер цвета. Идея метода заключается в следующем: каждом объекту, который может быть выбран присваивается уникальный id и уникальный цвет(на самом деле достаточно только цвета), затем все эти объекты помещаются в некоторую структуры данных. При попытки выделить объект, строится двумерная проекция картинки. Строить надо лишь объекты, которые можно выделить и строить их надо без текстур, однотонные с указанным цветом. Затем считывается цвет пикселя под курсором мышки и ищется объект соответствующего цвета. Затем он и становится выделенным объектом.
За идентификатор рационально использовать сам объект, если такое возможно(например, если все объекты, которые могут быть выделены предоставляют собой потомков некоторого класса, то можно использовать механизм позднего связывания).
Явный минус такого алгоритма - он позволяет выделить только одного юнита. Если больше не надо использовать стоит именно его, если же надо выделить более одного, то алгоритм можно расширить. Я могу предложить два способа, как это сделать, они похожи, отличия у них, правда, небольшие.
Нам понадобится ещё одна структура данных, где мы будет хранить выделенных юнитов. Оба способа заключается в обходе выделанного прямоугольника, считывании пикселей, поиске фигур в буфере цвета(первая структура данных) и помещение их в буфер выделения(вторая структура данных). Отличите простое: в первом случае я предлагаю сделать обработку буфера выделения так, чтобы в нём не возникало коллизий(если юнит уже есть, то его не добавлять). Второй способ заключается в том, чтобы если юнит попадет в буфер выделения, то он удаляется из буфера цвета. Главный минус первого способа - каждый раз при добавлении в буфер выделения необходимо проверять все уже выделенные фигуры, главный минус второго способа - каждый раз при выделении необходимо заново строить буфер цвета(или "перекидывать" из буфера выделения обратно).
Следующий вопрос, не менее важный, это - как выбрать эти структуры данных. Начну с конца. Буфер выделения, выбирать лучше исходя из особенностей выделяемых объектов и самого выделения. Если выделить можно некоторое конечное количество фигур, то подойдёт ограниченный линейный список(реализованный с помощью массива, т.к. это позволяет производить прямой и обратный обход памяти, что даёт существенный выигрыш во времени), если можно выделять потенциально бесконечное множество объектов, то тут уже надо "смотреть на месте".
Для буфера цвета строить общую теорию проще, т.к. его вид его элементов и назначение сразу определенно. Буду рассматривать ситуацию, когда идентификатор включен в буфер. Назначение буфера цвета - это хранение всех объектов, которые могут быть выделены, а так же возможность поиска в нём. Из необходимости поиска и будем исходить. В статье, ссылку на которую я дал вышел, в виде буфера цвета используется статический массив и линейный поиск(просматриваются подряд все элементы массива). При небольшом количество объектов это, пожалуй лучшее решение. Причина всё та же - возможность прямого и обратного обхода памяти. Однако когда количество элементов превышает уже, где-то 256 это не очень удобно, т.к. время обхода будет компенсировать выигрыш.
Проблема в построении структуры данных для поиска, в данном случае, заключается в том, что величина, по которой мы ведём поиск - векторная, состоящая из минимум трёх компонент: (r, g, b). Однако, задачу можно очень просто свести к задачи поиска по скалярной величине. Множество значений каждой из переменных ограниченно, и можно построить отображение из множества цветов во множество натуральных чисел следующим образом:
 Пожалуй, это самый просто способ решения. Построив такое отображение, мы можем искать вместо цвета, простое чисто. В силу ограниченности отображения, у него существует обратное, которая находится достаточно легко(одна из учебных задач - разить число на части), числа в разрядах 0-2 будут означать красную составляющию цвета, 3-5 - зелёную и 6-8 - синюю. Теперь, для буфера цвета, в качестве структуры данных можно использовать бинарное дерево поиска. Поиск в такой структуре один из самых быстрых(собственно, для этого она и предназначена) и кодированние/декодированние цвета тоже занимает мало количество времени. Минус использования дерева поиска - это удаление элемента из дерева может привести к перестройке всего дерева, поэтому второй, из предложенных мной, способов реализации выделения большого числа юнитов не подходит.
В общем, это достаточный теоретический минимум, чтобы реализовать выделение юнитов. Особенно это полезно в играх-стратегиях. Однако применение возможно и в других областях, например, взаимодействие пользователя с некоторыми предметами, посредством мышки(например некоторая физическая система и её элементы можно двигать, щёлкая на них мышкой).

четверг, 1 марта 2012 г.

Проект по физике - работа идёт.

Завершил сегодня вычислительную часть и отправил её, на разработку графики и интерфейса. Пока проект насчитывает 7 частей:
  1. Колебания непрерывной струны
  2. Колебание дискретной струны
  3. Поперечные волны на непрерывной струне
  4. Поперечные волны на дискретной струне
  5. Стоячие волны на непрерывной струне
  6. Стоячие волны на дискретной струне
  7. Волны на границе раздела двух сред.
Самым сложным, для реализации вычислений, представляется второй пункт. Там решается система из k ОДУ, где k - количество грузов на струне. Решается всё методом Рунге-Кутта четвёртого порядка.
Для решение волнового уравнения из первого пункта используется Метод Фурье, позволяющий сразу получить решение в виде ряда Фурье, не прибегая к МКР или МКЭ, применяя только численно интегрирование начальных условий. Об этих пунктах я писал раньше, перейдём к остальным.
Колебания в остальных пунктах представлены в виде гармонических (в седьмом пункте ввиде суммы двух гармонических), и, теоретически, предоставляют из собой первый член разложения в ряд Фурье функций из первых двух пунктов. А фактически просто вычисляются синусы и косинусы определённых аргументов.
Ну чтож, ждём интерфейс.

среда, 22 февраля 2012 г.

FiguresWars - переход на Qt+OpenGL


Если кратко, то был добавлен интерфейс Qt и OpenGL было внедрено с помощью Qt. По смыслу игры ничего нового сделано не было. Единственное, что могу сказать Qt - удобная штука, правда надо немного помучится, чтобы его освоить.

воскресенье, 19 февраля 2012 г.

PicView - изучаем Qt

Прежде чем совмещать Qt и OpenGL, я решил изучить, хотя бы.. понемногу, того и того в отдельности. На ogl я делал "тестовую графику" для проекта по физике. На Qt  я решил сделать...да то, что получится. Очень важным элементом для меня была подгрузка картинок и их отображение, а так же понять как вообще работает Qt и так же было интересно пользоваться дизайнером окон Qt Creator.
Первое, что я сделал в новом проекте - это создал кнопку и сделал так, что при нажатии на неё, она скрывается. Абсолютно бессмысленная вещь. Однако принцип работы: как для элементов, созданных в дизайнере делать события я понял(правой кнопкой мыши на элемент и "Добавить слот"). Дальше я принялся загружать картинки(они отображались в виджитах типа label). Полез в гугл.нарыл кучу разных способов, но работал только один:
QString f;
...
QImage im;
im.load(f);
l->setPixmap(QPixmap::fromImage(im));
Где l - это указатель на наш label. образ метода был взят отсюда, спасибо автору статьи. В общем, это статья и определила тип приложения, которое я собирался сделать. А именно - небольшой просмоторщик картинок. Естественно. особый интерес представляла  загрузка файлов из папки проекта(ресурсные файлы, итд). Долго у меня не получилось, поэтом оказалась, что всё дело в странности реализации режима debug в Qt Creator,а именно: он создаёт папку, например: пусть в папке "MP" находится ваш проект с именем "Pj", при сборке debug, QtC создаёт папку, например "Pj-build-desktop-Qt_4_8_0_for_Desktop_-_MSVC2008__Qt_SDK_________" и в этой папке есть папка "debug", где находится файл Pj.exe(например). Я кидал ресурсы в папки MP/Pj и MP/.../debug. А при запуске в этом режиме QtC считает корневой папку MP/Pj-build-...
в неё и надо скидывать ресурсы. При это в release, он считай корневой ту папку, в которой находится исполняемый файл.
После того, как я понял этот странный факт, всё пошло быстро. к приложению была добавлена иконка(найденная, на просторах интернета, а точнее - на этом сайте).

Некоторые элементы интерфейса, были созданы программно, а именно опция "Открыть" в меню "Файл" и элемент label в статус-баре. С созданной опцией так же программно было связанно событие открытия файла.
Приложение работает следующим образом: с помощью опции "Открыть", в меню "Файл", выбираете картинку, а затем нажимаете "Set Image".
Вот то, что получилось, исходники прилагаются. Приложение требует библиотек QtGui4.dll и QtCore4.dll. Из-за того, что они немного увесисты(10 Мб вместе), прикладывать их не стал.

суббота, 18 февраля 2012 г.

Проект по физике - Волны на струне непрерывные и дискретные волны

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

Непрерывная струна
Это обычная струна в нашем представлении. Некоторая тонкая нить, жестко закреплённая на концах. Колебания описываются волновым уравнением. В условиях задачи решение его получается методом Фурье. Краевые условия подобраны так: это две прямые, которые "скругляются параболой", а начальная скорость всех точек считает равным нулю.
С этим видимо колебаний почти не возникло проблем: получается решение в виде ряда, коэффициенты которого просто высчитываются (параллельный подсчёт коэффициентов Фурье я описывал раньше).

Дискретная струна
Это тонкая невесомая нить, на которой закреплены грузы равной массы. через равное расстояние.  Начальное положение высчитывается так же, однако, чтобы хоть как-то облегчить задачу, "потянуть" струну можно только за груз.
Колебание такой струны описывается системой из k уравнений следующего вида:
N - сила натяжения нити, m - масса груза, Δl - расстояние между грузами, а un - искомые функции.
Метод Эйлера решить эту систему не сильно помог.
Расспараллеленный на OpenMP, метод Рунге-Кутт, при нужной погрешности очень тормозил (тестирование проводилось при k=13, m =4кг, l =20м), однако, убрав параллельные области, я избавился от тормозов.

Сейчас обе модели работают, и дают верные(точнее, похожие на верные) результаты. Итог: использование параллельного программирование не всегда даёт прирост в скорости. Иногда на создание/уничтожение потоков(или пересылку сообщений) затрачивается больше времени, чем выигрывается на ускорение вычислений. Это тоже надо учитывать.

На очереди две соединённые непрерывные струны разной плотности. После проверки выложу примерный код(или сам код), реализующий описанные мой системы.

суббота, 11 февраля 2012 г.

Figures Wars - прогресс за два дня

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

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

Юниты
Сокращения: HP - жизни, AT - атака, T - тип, M - графическое отображение
Рабочий. HP - 2, AT - 0, T - 0, M - конус, остриём вниз.
База. HP - 20, AT - 0, T - 1, M - шар.
Башня. HP - 10, AT - 1, T - 3,  M - параллелепипед. На каждом ходе башня атакует клетки во все 4 направления.

Команды
Курсивом помечаются аргументы функции. 
move x y d - юнит с координатами (x, y) перемещается в направление d
build x y t d - юнит с координатами (x, y) строить здание типа t в направление d
basecreate x y t - база с координатами (x, y) производит юнита типа t
mworker x y - запомнить рабочего с координатами (x, y)
mbase x y - запомнить базу с координатами (x, y)
mwmove d - запомненный рабочий перемещается в направлении d
mwbuild t d - запомненный рабочий строит здание t в направлении d
mbcreate t - запомненная база производит юнита типа t
| - разделитель, введён чтобы облегчить восприятие алгоритма. Отделяет команды созданные для одного шага.

Скрины

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

Благорадрность

Спасибо Антону (@MrFeod, блог) за внедрение OpenGL в проект.