Глядя на удивительный успех, достигнутый Джозефом Лордом в ускорении работы клеточных автоматов на базе процессора , я подумал, что будет интересным проектом перенести некоторый старый код динамической обработки ActionScript 3 на Swift. Оригинальный AS3 порт был на Oaxoa и на основе Джоз Стам в гидродинамике в реальном времени для игр. Я использовал его много раз в проектах AS3 на протяжении многих лет, но никогда не настраивал его.
Моя итерация — почти прямая копия исходного кода AS3, но я потратил некоторое время на его оптимизацию.
CFD решатель использует несколько массивов для горизонтального и вертикального направления ( U и об ) и плотности ( г ). В некоторых случаях исходный код будет вызывать одну и ту же функцию, например diffuse () и advect () , последовательно ориентируясь на массивы u и v по отдельности. Таким образом, за счет повторного использования кода я создал почти дубликаты копий тех функций, которые нацелены на u и v одновременно.
В исходном коде широко использовалась небольшая функция для получения индекса одномерного массива на основе параметров x и y . По сути, я много раз использовал это и следил за дублирующими вычислениями — создавая константы для повторно используемых значений.
Как и в других моих недавних проектах Swift, как решение, так и рендеринг выполняются в отдельных потоках. Я использую библиотеку Tobias Due Munk Async, которая абстрагирует Grand Central Dispatch для обработки потоков и кода на основе кода генерации растровых данных Джозефа для рендеринга.
Пользовательский интерфейс довольно прост — есть один UIImageView для хранения визуализированных плотностей жидкости и кнопка сброса для запуска «взрыва».
Внутри ViewController я переопределил метод touchesMoved () . Это позволяет мне регистрировать любые события касания в UIImageView следующим образом:
override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!) { let touch = event.allTouches().anyObject().locationInView(uiImageView); let touchX = Int(touch.x / 3); let touchY = Int(touch.y / 3); [...] }
Я не совсем уверен, почему мне нужно умножить значения на 3 — я подозреваю, что это связано с слуховым разрешением новых устройств Apple iOS — но touchX и touchY установлены на координаты в массивах CFD, поэтому я могу легко увеличить плотность, где пользователь коснулся.
Я также храню записи о предыдущих координатах касания, чтобы использовать дельту для обновления массивов u и v, чтобы события касания изменяли направление потока жидкости. Поскольку предыдущие позиции касания являются необязательными, я использовал необязательную привязку внутри переопределенных функций touchesMoved () :
[...] if let ptx = previousTouchX { if let pty = previousTouchY { u[targetIndex] = u[targetIndex] + Double((touchX - ptx) / 2) v[targetIndex] = v[targetIndex] + Double((touchY - pty) / 2) } } [...]
… и когда жест касания закончен, я могу обнулить предыдущие значения:
override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) { previousTouchX = nil; previousTouchY = nil; }
Есть некоторая смесь между использованием и не использованием точек с запятой. Моя работа заключается в кодировании ActionScript в течение дня, и добавление их является второй натурой, поэтому, пожалуйста, простите непоследовательность.
Код написан в XCode 6 Beta 6 и в симуляторе iOS на моем iMac, каждый шаг занимает около 0,04 секунды для сетки 200 x 200. Предстоит еще больше оптимизации, но если у вас есть возможность запустить ее на недавнем iPad на базе A7, я бы хотел услышать результаты.
Весь исходный код доступен здесь, в моем репозитории GitHub.