30 июля 2010 в 13:41

Кубик Рубика на canvas

Недавние посты об алгоритме сборки кубика 5×5 сподвигли меня написать эмулятор кубика на канвас. Автором статей про сборку предлагался свой кубик на OpenGL, но он мне многим не понравился. Надеюсь, с моим кому-нибудь удастся освоить алгоритм быстрее. Некоторые особенности и преимущества:
  • Кроссплатформенность, кроссбраузерность (IE за браузер не считаем), ненужность инсталлятора и прочие преимущества веб-приложения.
  • Поддержка кубиков от 2×2 и до бесконечности (пока грани не станут сильно маленькими и рендеринг не начнёт жестоко тормозить). В интерфейс вынесено до 11×11, но в библиотеке ограничений нет.
  • Псевдообъёмные грани для красоты.
  • Бесконечный undo-буфер.
  • Возможность замеса кубика (shuffle).
  • Слои вращаются легко и интуитивно, быстро привыкаешь. Крутить весь кубик (мышкой с зажатым шифтом или правой кнопкой) не так легко, но тоже можно привыкнуть.
  • Вся библиотека компактная, размещается в одном js-файле и не имеет никаких внешних зависимостей.
  • Лицензия MIT, а также открытые и не очень страшные исходники позволяют вставить кубик на ваш сайт и доработать по вкусу.
Не знаю, стоит ли рассказывать про особенности разработки. Было много сложностей, но все достаточно мелкие. Я почему-то поленился использовать однородные координаты, из-за чего, в частности, проекция ортогональная, а не перспективная. Модель рендеринга тоже немного странная. В общем виде кубик представляется в виде 6, 12 или 18 граней, а каждая грань содержит до size^2 элементов, каждый элемент — это квадратик. На внутренних срезах, которые видно при вращении, элемент всего один площадью на всю грань. Здесь подписаны 9 видимых граней, остальные 9 расположены симметрично им:

Потребовалось также сортировать грани в правильном порядке, чтобы корректно удалялись невидимые части. Здесь правильный порядок — вдоль оси вращения (если вращения нет, порядок неважен).

Объёмность граней ненаучная: это просто круговой градиент канвас, центр которого отклоняется в направлении нормали. Честно считать освещённость по BRDF показалось для этой задачи неоправданно.

Что хотелось ещё сделать, но лень:
  • Управление с клавиатуры.
  • Перед вращением слоёв мышкой подсвечивать вращаемый слой, чтобы уменьшить число ошибок. Сейчас вращение начинается, если с зажатой кнопкой провести мышкой на 50 пикселей в направлении вращения. Подсветку можно включать на меньшем расстоянии, тогда пользователь вовремя остановится. Может, это и не нужно, у меня ошибки случались редко.
  • Починить кубик 2×2, почему-то он не работает. исправлено — спасибо fllln
  • Сделать перспективную проекцию.
  • Сохранение/загрузку (можно в cookies).
  • Поддержка IE6-8 (вероятно, радиальный градиент в excanvas не заработает).
Если кто-нибудь хочет заняться, то пожалуйста. Если есть вопросы по коду, задавайте, с радостью отвечу.
Тагир Валеев @lany
карма
483,2
рейтинг 10,9
Программист
Похожие публикации
Самое читаемое Разработка

Комментарии (97)

  • –65
    >> IE за браузер не считаем

    Дальше не читал.
    • +18
      хорошо, что не:

      > >> Кубик Рубика
      >
      > Дальше не читал.
    • +7
      Пора переходить на что-то нормальное а не зацикливаться на старом.
      • НЛО прилетело и опубликовало эту надпись здесь
      • +3
        Меня всегда удивляло — какое другим людям дело, какой у меня браузер? Кому какое дело, на каком языке я пишу? Кому какое дело, какая у меня ОС?
        Так нет же, всегда находятся такие, кто будет кричать — ставь файрфокс! Ставь оперу! Ставь Виндоуз! Линух форева!
        Личный вопрос можно? Какое вам дело, что у кого-то стоит IE? Лично мне нет до этого никакого дела.
    • +2
  • +2
    на этот раз пятницо явилось в образе Кубика Рубика:)
  • +4
    Вы просто умничка!
  • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      Цвета легко настраиваются. Скажите шесть хороших цветов в формате #rrggbb, я заменю :-)
      • +2
        Используйте оригинальные цвета кубика.
        Красный: #FF0000
        Оранжевый: #FF8000
        Жёлтый: #FFEB00
        Белый: #FFFFFF
        Синий: #0000FF
        Зелёный: #00DC00
        • +3
          И не забудьте:
          Белый напротив желтого,
          Синий напротив зеленого,
          Красный напротив оранжевого.
          И с одной стороны видны белый, синий и красный так, что белый сверху, синий справа, а красный слева.

          Так будет максимально верно.
          • +3

            Как-то так.
            • 0
              Сделал, только зелёный поставил сильно темнее. А то приведённые цвета — пытка для дихроматов :-) У меня вроде был в детстве кубик с тёмно-зелёным.
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Градиенты отключаются кнопкой Flat над кубиком.
          • НЛО прилетело и опубликовало эту надпись здесь
    • НЛО прилетело и опубликовало эту надпись здесь
      • НЛО прилетело и опубликовало эту надпись здесь
        • НЛО прилетело и опубликовало эту надпись здесь
          • НЛО прилетело и опубликовало эту надпись здесь
            • НЛО прилетело и опубликовало эту надпись здесь
              • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        Можно просто кодировать статус в строчку, которую человек может закинуть себе на флэшку или переслать по е-мейлу и потом назад вставить в поле для загрузки. Не хочется добавлять к этому сервер-сайд код.
        • НЛО прилетело и опубликовало эту надпись здесь
          • +1
            Ну это не тот случай, чтобы защита нужна была. Чемпионат по сборке кубиков онлайн? :-) Программы должны помогать людям, даже если человек хочет читерить :-)
          • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              почему бы и нет?
              сделать сайт с авторизацией, рекордами и соревнованиями (как мелькавший тут тетрис), и с вашим «рубик-движком» :)
      • 0
        у меня прочиталось «пришел с работы дáмой» — долго думал :)
    • +2
      У меня в IE9 работает
      • НЛО прилетело и опубликовало эту надпись здесь
  • +4
    шикарно, а из Shuffle (animated) при 11x11 вообще можно скринсейвер делать
  • +2
    Спасибо, в детстве небыло, хоть сейчас покручу
  • +5
    Что-то по-моему перспектива — наоборот, задняя стенка больше передней.
    • +1
      визуальный эффект. Глаз привык к перспективе.
      • +2
        Да нет, не визуальный, вот посмотрите например на стандартное положение кубика, верхние грани, левая и правая не параллельны, ну и если кубик повернуть фронтально — то задняя стенка немного — но выпирает.
        • 0
          Правда-правда визуальный эффект :-)
          • +1
            Я даже в графическом редакторе проверил… и правда визуальный эффект… жуть :)
        • +1
          Вы знаете, я не поверю, что человек осознанно программировал обратную перспективу. А то, что аксонометрия дает легкий эффект обратной перспективы — общеизвестно.
          • +2
            Человек вполне себе осознанно может ошибаться :) в том числе при реализации перспективы.
            • +2
              Это ж как нужно ошибиться? ;) Это из разряда:
              — Что вы делаете в моей машине?
              — Ой, я просто ошибся и сел не в свою!
              ;)

              ЗЫ Undo после Shuffle прикольно работает ;)
    • 0
      Просто изометрия. То, что задняя грань такая же по длине как передняя можно легко проверить.
  • 0
    Для IE есть excanvas(http://code.google.com/p/explorercanvas/), IE9 превью умеют работать с канвасом — почему бы и не допилить?
    • +1
      Написал же — лень :-) Я знаю про excanvas и использовал её.
  • +5
    перспективу бы не изометрическую и все было бы отлично :)
  • +1
    Еще бы, для ленивых владельцев разобранного кубика, сделать инструмент для быстренькой пошаговой сборки :)
    • +1
      Кнопка shuffle кладёт вращения в undo-буфер. Поэтому можно после shuffle нажать 50 раз undo, и кубик вернётся в исходное состояние =)
  • 0
    Управление — лучшее из тех, что я видел (и делал:)).
    Единственное, надо перспективу сделать, чтоб во время поворота самого кубика мозг не нагревался и будет великолепно.
    • 0
      Ах, да забыл, вращение кубика шифтом неудобно, идеально, когда всё ложится на мышь, например вместо зажатия шифта можно сделать зажатие правлй кнопки (всё равно не используется)
      • 0
        Сделал. На вебе обычно не стоит закладываться на правую кнопку (к примеру, пользователь может запретить переопределять контекстно меню в опциях Javascript). Кроме того есть мако-юзеры без правой кнопки :-) Так что я оставил шифт как альтернативу.
        • 0
          у оперы на «зажатая правая кнопка+движение влево-вправо» повешены гестуры, т.е. взад-вперед по истории страниц можно бегать. Повернул Ваш кубик влево, отпустил правую кнопку и обратно на хабр попал :)

          так, что шифт очень даже нужен.
  • +2
    lany — искренне восхищаюсь ;)
    сколько вы потратили времени если не секрет?
    • +1
      Пару дней с перерывами. Чистого времени — не знаю… Часов 15, может…
  • +1
    Спасибо, всё очень понравилось.
  • +1
    перспективную проекцию бы. делается просто, плюсов — много.
  • 0
    Самый простой способ: снять на видео «Shuffle (animated)» и прокручивая обратно — решить головоломку.
    Даешь высокие технологии! :)
    • 0
      Нажмите после shuffle (необязательно animated) кнопку undo 50 раз (можно быстро подряд) и наслаждайтесь =)
      • НЛО прилетело и опубликовало эту надпись здесь
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Сделал, но у меня не было такой проблемы. Посмотрите, помогло ли :-)
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          А, вы про эти команды. Я думал, про мышиные эвенты на самом канвасе. Сейчас лучше?
          • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              Ну если подскажете, что дописать, то допишу :-)
  • +1
    Багофича:
    если усердно крутить кубик, то он увеличивается или уменьшается:

    dl.dropbox.com/u/1604499/capture-2.mp4
    (1,2 Мб если заботитесь о траффике)
    • +1
      404.
      Там есть кнопки zoomIn/zoomOut. Может, вы их случайно нажали?
      • 0
        Сорри, сейчас будет.
        • 0
          Ага, интересная бага. После последней модификации вращения в матрице преобразований стала накапливаться погрешность. Сделал нормализацию матрицы, спасибо.
  • 0
    автор, добавьте заголовок HTML5 doctype html и ie9 автоматом переключится в режим поддержки стандартов и все заработает
    • 0
      Сейчас лучше?
      • 0
        да, все работает, спасибо
  • 0
    Спасибо автору, поразмялся немного. К управлению, действительно, привыкаешь довольно быстро.

    • 0
      Чуть попроще.
      • 0
        Так этот-то из полностью собранного делается в 4 поворота :)
    • 0
      А так собрать слабо? :D
      • +1
        Неа


  • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Небольшая проблема: shuffle иногда крутит один и тот же слой несколько раз подряд в разных направлениях.
    • +1
      Я знаю. Ну пускай, вам что ли жалко? :-) Если кажется, что замес недостаточно сложный, нажмите шаффл ещё раз :-)
      • 0
        Не жалко. Клевая игрушка:)
  • +4
    Спасибо! Мне понравилось!

    • 0
      А вот это мне точно слабо о_О
      • 0
        Насколько я понимаю, это делается аналогично схемам K11-K13 из алгоритма 5×5, любой из которых будто бы естественно расширяется на любые внутренние кубики. Но выглядит красиво, да :-)
        • 0
          Я ничего, кроме как 2x2x2 и 3x3x3, не пробовал ни разу.
          За что Вам ещё раз отдельное спасибо — сейчас кручу 4x4x4 :)
          • 0
            Да я тоже на своей программе большие размеры впервые попробовал :-) Теперь хочу уже купить хардварный 5×5 =)
      • +1
        Это собирается так же, как и окошки выше.
        1) Любой центральный синий слой вращаем в сторону белого
        2) Любой красный в сторону белого
        3) Синий обратно
        4) Красный обратно
        В результате 4 поворотов получаем 1 синий «пиксель» на белом фоне.
        Аналогично можно собрать любую двухцветную картинку. За это я и люблю многомерные кубики. Жаль у меня только 7*7*7 есть, уже хочу 11*11*11 и больше. )
  • 0
    я нажал Shuffle (animated), потом понял, что это надолго, и нажал Shuffle (fast)
    он сработал, но Shuffle (animated) продолжился :)
    есть ли способ остановить/отменить Shuffle (animated)?
    • 0
      Есть. Надо нажать Reset :-)
      Можно написать отдельную функцию в три строчки, которая очистит очередь анимации.
      • 0
        я имел в виду — остановить без ресета, с сохранением уже имеющегося Shuffle
        интуитивно я нажал Shuffle (fast) именно затем, чтоб Shuffle (animated) прекратился

        на самом деле — это всё плюшки :)
        сделано супер!
  • 0
    Меня предыдущие посты сподвигли на написание on-line решалки для кубика. Пока программа показывает только процесс решения по пунктам. Теперь думаю, как бы визуализировать процесс, чтобы показывала что-то типа видео-ролика с поворотами граней.
    • 0
      Ну если вы на JS пишете, то прикручивать решатель к моему коду совсем несложно. Загоняйте очередную комбинацию в cube.addSliceRotation, затем ждите, пока cube.animationQueue не опустеет, тогда продолжайте. Текущее состояние кубика в cube.state[грань][номер_элемента], взаимное расположение граней в cube.neighbors. Можно прикрутить к анимации callback, чтобы после завершения текущей анимации управление назад переходило к решателю.
      • 0
        Надо будет попробовать.
  • 0
    Впечатляет!
    Последнее время мне еще понравилась реализация классического кубика с поддержкой 3D очков — Cube. Даже заказал себе по этому поводу неплохие красно-синие очки с DealExtreme…

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