Puzzle It! — онлайн-генератор пазлов с возможностью сборки этих самых пазлов прямо в окне браузера. Ниже я хотел бы рассказать историю создания этого, интересного для меня, инструмента.
Как-то сидя вечером и просматривая Хабр наткнулся на заметку товарища ganzer о том, что человек ищет адекватный к его потребностям конструктор пазлов. Все бы ничего, но я сам, как любитель убить несколько часов за этим занятием, заметил, что в сети нет приятных и «правильных» инструментов для создания простого пазла, который можно тут же собрать.
И тут на задворках сознания начала зудить навязчивая идея. Именно с этого все и началось.
Еще раз проверив поисковики на наличие такого сервиса без каких-либо ограничений (java или flash отнес к таким ограничениям) я все-таки решил не искать зря и, что уж там, взять и сделать такой конструктор сам. Но на простом месте его делать было неинтересно, нужна была какая-то цель. Для меня такой целью стал девиз «Just For Fun» и я с энтузиазмом принялся за обдумывание своего будущего детища. Определив, что все-таки основой всего будет именно создание частичек картинки я решил, что первым делом сделаю именно это.
Первым делом, за которое я взялся после того, как назойливая мысль стала складываться в красивую картинку будущего сервиса — это математика. Если быть точнее — обыкновенная планарная геометрия с ее вечными законами. Нужно было определить, как и какие соотношения сторон должны стать основополагающими для будущей частички пазла. С все еще не утухающим энтузиазмом я взялся чертить геометрическую модель частицы картинки, строить формулы расчетов основных соотношений сторон и функции определения точнек на координатной плоскости, пробовать реализовать все полученные механизмы и…
И после двух дней, которые я просидел над справочником по математике с карандашом, линейкой и открытым Inkscape я понял, что такая модель получается не просто большой, а очень громоздкой и неповоротливой. Ди и времени на получение правильного результата тратилось очень много — чего стоит только то, что на моем рабочем ноутбуке 1 частичка пазла генерировалась с полученной моделью больше 2х секунд из исходного изображения. Теперь же, если представить, что таких частичек относительно много (в пазле 20х20 их 400) то картина получалась весьма неутешительная.
Нужно было какое-то визуальное упрощение пазла — и я решил ограничить «случайность» выбора стороны пазла до 4-5 вариантов, чего вполе хватало для приличной случайности получаемой в итоге части.
Справочник по математике снова отправился на полку.
После решения о упрощении я понял, насколько замечательная штука Inkscape. В нем я довольно просто сделал все варианты сторон и очень быстренько экспортировал все это в PNG как основной материал для производства частиц изображения.
Дальше все пошло так, как и должно было быть. Были определены основополагающие размеры: размер видимой частички без «хвостиков» — 50х50 пикселей. Соответственно, размеры всех загружаемых изображений теперь должны были быть кратными 50.
Решение по изменению размеров изображения стало одним из краеугольных камней всего проекта. Изменение размеров было реализовано путем уменьшения исходной картинки, если она «не попадала» в нужные размеры по ширине, а потом — обрезание по высоте до необходимого размера. Это позволило избежать проблем с перемасштабированием картинки, однако может вызвать интересный момент, если в обрезанных снизу 49 пикселях все-таки было что-то важное. Пока-что я не увидел правильного и православного решения этой проблемы, которое бы позволяло избежать видимой прямо на частях пазла «помощи» в сборке изображения в виде линий и прочих артефактов.
Продолжаем работу. Теперь у меня была готовая для обработки картинка. Что же с ней делать дальше?
Дальше наступил самый интересный момент — процесс построения «матрицы» всего пазла и, собственно, производство всех изображений частей пазла. С самого начала в качестве основы для частиц пазла был выбран формат PNG с его 8-bit прозрачностью, но с ним все еще остались проблемы в IE6, который я, к сожалению, даже протестировать не могу — фильтры в Linux'овом IE6 работать отказываются. Тем не менее формат позволял делать то, чего я от него добивался.
Каждый элемент конечной матрицы по итогу получал координаты на исходном изображении и 4 значения для каждой стороны. В значении стороны у меня сохранялся тип стороны (если вы заметили — их всего 4) и «инвертирование» — т.е. является ли сторона выпуклой или нет.
После получения матрицы можно было пробежаться по всем ее элементам и изготовить конечные части мозаики. В общем-то здесь все просто — пиксель с координатами из исходного изображения копировался в конечное изображение с тем значением альфа-канала, которое было в «заготовке» стороны, ну а весь остаток по стороне заполнялся поточечно треугольником к центру. Благо — реализуется это очень просто. Визуально весь этот процесс показан на картинке.
В общем и целом в этой части очень помогло хорошее знание GD — уж очень активно общался с этой библиотекой. Единственное но — попиксельное копирование все-таки ресурсоемко.
Еще один подводный камень, всплывший на пути — относительное изменение размеров из-за разных конечных размеров картинки (случайная выпуклость частиц давала о себе знать и регулярно слала умопомрачительные приветы). Поэтому иногда конструкции if() получались просто монструозные и тут же искался другой путь и куски кода переписывались заново.
После всех мытарств с производством изображений частиц было решено карту изображения, т.е. матрицу частич складывать в отдельную проиндексированную таблицу БД.
Стоит так же упомянуть о процессе сборки. Весь Javascript-функционал реализован на jQuery, но стандартный Drag'n'Drop (куда же без него в такой игре?) из пакета jQuery.UI отличается изрядной тормознутостью в FF, поэтому я занялся поиском более легковесного решения. И таки нашел — webtoolkit Drag'n'Drop. Если в будущем у вас будут подобные задачи — попробуйте. В этой реализации нет ничего лишнего и она работает просто замечательно.
При проектировании сборки мне очень помог Feedback от моих друзей и товарищей, которым я показывал будущее детище. Именно после обсуждений процесса сборки появилось «прилипание» кусочков друг к другу в случае, если они в правильном месте и перемещение целых собранных кусков. Стоит упомянуть отдельно таскание целых кусков — реализовано так же на jQuery некоторой модификацией Drag'n'Drop. При соединении нескольких кусочков в правильную конструкцию они образовывают «колонию» со своим уникальным id. И при перемещении с помощью jQuery относительно одного из кусков перемещаются так же все остальные члены «колонии». Все просто, но работает замечательно.
Так же здесь прямо в шаблоне была сделана простая функция «случайности» выпадающих частиц пазла на экране сборки. Конечно немного не то, что я хотел, но работает.
Над этой частью тоже пришлось призадуматься после того, как на рабочем ноуте изготовление полной карты изображения заняло больше 30 секунд времени и сработали PHP-овые ограничения. Поэтому в течение следующего дня был написан простейший однопоточный демон с очередью, который был полностью предназначен для того, чтобы заниматься производством изображений частиц головоломки. В общем-то большая часть генерации изображения была взята из уже имеющегося механизма и слегка поправлена относительно демона.
После тестов на хостинге я решил, что задержки в 0,25 секунды на обработку одного изображения будет вполне достаточно для нормальной работы.
Именно после введения в строй демона появился экранчик с просьбой немного подождать. Все-таки жизнь и здоровье сервера намного дороже моего сервиса и с этим нужно мириться. Общежитие как-никак, хоть и очень приятное для меня.
Да, на этом я немного застрял. Не очень люблю рутинную работу, но тут пришлось ее делать как часть общепринятой данности. На этом шаге был написан FAQ, сделаны отзывы, слегка проработано оформление страниц, поправлены валидаторы для загрузки изображений и коментариев, сделано некоторое подобие главной странички с последними загруженными пазлами… В общем-то делалось то, без чего сейчас не может жить ни один мало-мальский проект.
Иногда себя приходилось заставлять. Бедный FAQ на 15 вопросов рожался часов 10 V_v.
Но и этот шаг был побежден.
После окончательного прочесывания всех страниц и проверок функционала я залил исходники на сервер хостинга, запустил там своего демона и начал потихоньку радоваться тому, что первый шаг к захвату мира осуществлен — у меня появился первый сервис со своей идеей, которая не так сильно распространена в сети, как могла бы быть.
Конечно этот генератор пазлов далеко не идеален. Вот небольшой список того, что будет дореализовано в ближайшее время:
И это только первое, что бросается в глаза. Но работа уже начата, и если сервис кого-нибудь заинтересует — я буду продолжать над ним работать. Да и если не заинтересует — всеравно не буду его забрасывать ^_^ Как по мне — вещь очень интересная и перспективная.
Хотелось бы добавить, что весь сервис делался Just For Fun и никакой материальной отдачи за собой не подразумевал. Но если у вас есть идеи, как его сделать прибыльным — выслушаю с удовольствием. Да и вообще если у вас будут идеи по улучшению генератора — я всегда к вашим услугам.
Дарес проекта: puzzleit.org.ua
Собрать тестовый пазл можно по адресу: http://puzzleit.org.ua/puzzle/cb287c8f7d69827344d053d0f0b97362/
В общем-то такая небольшая история.
Спасибо за внимание.
З.Ы. Всех с Рождеством!
З.Ы.Ы. Отдельное спасибо Хабрахабру и его пользователям за статьи о стилизованном input=file и построении многопоточных демонов. Очень помогли.
Как-то сидя вечером и просматривая Хабр наткнулся на заметку товарища ganzer о том, что человек ищет адекватный к его потребностям конструктор пазлов. Все бы ничего, но я сам, как любитель убить несколько часов за этим занятием, заметил, что в сети нет приятных и «правильных» инструментов для создания простого пазла, который можно тут же собрать.
И тут на задворках сознания начала зудить навязчивая идея. Именно с этого все и началось.
Перед началом
Еще раз проверив поисковики на наличие такого сервиса без каких-либо ограничений (java или flash отнес к таким ограничениям) я все-таки решил не искать зря и, что уж там, взять и сделать такой конструктор сам. Но на простом месте его делать было неинтересно, нужна была какая-то цель. Для меня такой целью стал девиз «Just For Fun» и я с энтузиазмом принялся за обдумывание своего будущего детища. Определив, что все-таки основой всего будет именно создание частичек картинки я решил, что первым делом сделаю именно это.
Простая математика
Первым делом, за которое я взялся после того, как назойливая мысль стала складываться в красивую картинку будущего сервиса — это математика. Если быть точнее — обыкновенная планарная геометрия с ее вечными законами. Нужно было определить, как и какие соотношения сторон должны стать основополагающими для будущей частички пазла. С все еще не утухающим энтузиазмом я взялся чертить геометрическую модель частицы картинки, строить формулы расчетов основных соотношений сторон и функции определения точнек на координатной плоскости, пробовать реализовать все полученные механизмы и…
И после двух дней, которые я просидел над справочником по математике с карандашом, линейкой и открытым Inkscape я понял, что такая модель получается не просто большой, а очень громоздкой и неповоротливой. Ди и времени на получение правильного результата тратилось очень много — чего стоит только то, что на моем рабочем ноутбуке 1 частичка пазла генерировалась с полученной моделью больше 2х секунд из исходного изображения. Теперь же, если представить, что таких частичек относительно много (в пазле 20х20 их 400) то картина получалась весьма неутешительная.
Нужно было какое-то визуальное упрощение пазла — и я решил ограничить «случайность» выбора стороны пазла до 4-5 вариантов, чего вполе хватало для приличной случайности получаемой в итоге части.
Справочник по математике снова отправился на полку.
Процесс
После решения о упрощении я понял, насколько замечательная штука Inkscape. В нем я довольно просто сделал все варианты сторон и очень быстренько экспортировал все это в PNG как основной материал для производства частиц изображения.
Дальше все пошло так, как и должно было быть. Были определены основополагающие размеры: размер видимой частички без «хвостиков» — 50х50 пикселей. Соответственно, размеры всех загружаемых изображений теперь должны были быть кратными 50.
Решение по изменению размеров изображения стало одним из краеугольных камней всего проекта. Изменение размеров было реализовано путем уменьшения исходной картинки, если она «не попадала» в нужные размеры по ширине, а потом — обрезание по высоте до необходимого размера. Это позволило избежать проблем с перемасштабированием картинки, однако может вызвать интересный момент, если в обрезанных снизу 49 пикселях все-таки было что-то важное. Пока-что я не увидел правильного и православного решения этой проблемы, которое бы позволяло избежать видимой прямо на частях пазла «помощи» в сборке изображения в виде линий и прочих артефактов.
Продолжаем работу. Теперь у меня была готовая для обработки картинка. Что же с ней делать дальше?
Дальше наступил самый интересный момент — процесс построения «матрицы» всего пазла и, собственно, производство всех изображений частей пазла. С самого начала в качестве основы для частиц пазла был выбран формат PNG с его 8-bit прозрачностью, но с ним все еще остались проблемы в IE6, который я, к сожалению, даже протестировать не могу — фильтры в Linux'овом IE6 работать отказываются. Тем не менее формат позволял делать то, чего я от него добивался.
Каждый элемент конечной матрицы по итогу получал координаты на исходном изображении и 4 значения для каждой стороны. В значении стороны у меня сохранялся тип стороны (если вы заметили — их всего 4) и «инвертирование» — т.е. является ли сторона выпуклой или нет.
После получения матрицы можно было пробежаться по всем ее элементам и изготовить конечные части мозаики. В общем-то здесь все просто — пиксель с координатами из исходного изображения копировался в конечное изображение с тем значением альфа-канала, которое было в «заготовке» стороны, ну а весь остаток по стороне заполнялся поточечно треугольником к центру. Благо — реализуется это очень просто. Визуально весь этот процесс показан на картинке.
В общем и целом в этой части очень помогло хорошее знание GD — уж очень активно общался с этой библиотекой. Единственное но — попиксельное копирование все-таки ресурсоемко.
Еще один подводный камень, всплывший на пути — относительное изменение размеров из-за разных конечных размеров картинки (случайная выпуклость частиц давала о себе знать и регулярно слала умопомрачительные приветы). Поэтому иногда конструкции if() получались просто монструозные и тут же искался другой путь и куски кода переписывались заново.
После всех мытарств с производством изображений частиц было решено карту изображения, т.е. матрицу частич складывать в отдельную проиндексированную таблицу БД.
Сборка
Стоит так же упомянуть о процессе сборки. Весь Javascript-функционал реализован на jQuery, но стандартный Drag'n'Drop (куда же без него в такой игре?) из пакета jQuery.UI отличается изрядной тормознутостью в FF, поэтому я занялся поиском более легковесного решения. И таки нашел — webtoolkit Drag'n'Drop. Если в будущем у вас будут подобные задачи — попробуйте. В этой реализации нет ничего лишнего и она работает просто замечательно.
При проектировании сборки мне очень помог Feedback от моих друзей и товарищей, которым я показывал будущее детище. Именно после обсуждений процесса сборки появилось «прилипание» кусочков друг к другу в случае, если они в правильном месте и перемещение целых собранных кусков. Стоит упомянуть отдельно таскание целых кусков — реализовано так же на jQuery некоторой модификацией Drag'n'Drop. При соединении нескольких кусочков в правильную конструкцию они образовывают «колонию» со своим уникальным id. И при перемещении с помощью jQuery относительно одного из кусков перемещаются так же все остальные члены «колонии». Все просто, но работает замечательно.
Так же здесь прямо в шаблоне была сделана простая функция «случайности» выпадающих частиц пазла на экране сборки. Конечно немного не то, что я хотел, но работает.
Хабраэффект
Над этой частью тоже пришлось призадуматься после того, как на рабочем ноуте изготовление полной карты изображения заняло больше 30 секунд времени и сработали PHP-овые ограничения. Поэтому в течение следующего дня был написан простейший однопоточный демон с очередью, который был полностью предназначен для того, чтобы заниматься производством изображений частиц головоломки. В общем-то большая часть генерации изображения была взята из уже имеющегося механизма и слегка поправлена относительно демона.
После тестов на хостинге я решил, что задержки в 0,25 секунды на обработку одного изображения будет вполне достаточно для нормальной работы.
Именно после введения в строй демона появился экранчик с просьбой немного подождать. Все-таки жизнь и здоровье сервера намного дороже моего сервиса и с этим нужно мириться. Общежитие как-никак, хоть и очень приятное для меня.
Рутина
Да, на этом я немного застрял. Не очень люблю рутинную работу, но тут пришлось ее делать как часть общепринятой данности. На этом шаге был написан FAQ, сделаны отзывы, слегка проработано оформление страниц, поправлены валидаторы для загрузки изображений и коментариев, сделано некоторое подобие главной странички с последними загруженными пазлами… В общем-то делалось то, без чего сейчас не может жить ни один мало-мальский проект.
Иногда себя приходилось заставлять. Бедный FAQ на 15 вопросов рожался часов 10 V_v.
Но и этот шаг был побежден.
The End
После окончательного прочесывания всех страниц и проверок функционала я залил исходники на сервер хостинга, запустил там своего демона и начал потихоньку радоваться тому, что первый шаг к захвату мира осуществлен — у меня появился первый сервис со своей идеей, которая не так сильно распространена в сети, как могла бы быть.
Конечно этот генератор пазлов далеко не идеален. Вот небольшой список того, что будет дореализовано в ближайшее время:
- Изменение процесса создания частиц с использованием кривых Безье вместо сатичных изображений-макетов
- Сохранение в каком-либо виде положений частиц в «недособранном» пазле. Удобно будет, если пазл большой и собирать его 6 часов подряд не можешь.
- Возможность переворачивать частицы по нажатию мыши и, соотетственно, случайный переворот при отображении.
- Многопоточность демона для более быстрого получения конечного пазла.
- Таймеры сборки пазла, режим коллективной сборки с нескольких компьютеров
- Комментирование самого пазла
И это только первое, что бросается в глаза. Но работа уже начата, и если сервис кого-нибудь заинтересует — я буду продолжать над ним работать. Да и если не заинтересует — всеравно не буду его забрасывать ^_^ Как по мне — вещь очень интересная и перспективная.
Хотелось бы добавить, что весь сервис делался Just For Fun и никакой материальной отдачи за собой не подразумевал. Но если у вас есть идеи, как его сделать прибыльным — выслушаю с удовольствием. Да и вообще если у вас будут идеи по улучшению генератора — я всегда к вашим услугам.
Дарес проекта: puzzleit.org.ua
Собрать тестовый пазл можно по адресу: http://puzzleit.org.ua/puzzle/cb287c8f7d69827344d053d0f0b97362/
В общем-то такая небольшая история.
Спасибо за внимание.
З.Ы. Всех с Рождеством!
З.Ы.Ы. Отдельное спасибо Хабрахабру и его пользователям за статьи о стилизованном input=file и построении многопоточных демонов. Очень помогли.