├── .gitignore ├── .gitmodules ├── Builder ├── build ├── builder.php └── libcanvas-dynamic.php ├── Docs └── Ru │ ├── App │ ├── App.md │ ├── Element.md │ ├── Layer.md │ ├── Light │ │ └── Light.md │ └── MouseHandler.md │ ├── Core │ ├── Canvas.md │ ├── Context2D.md │ ├── LibCanvas.md │ ├── Mouse.md │ ├── Point.md │ ├── Point3D.md │ └── Size.md │ ├── Engines │ ├── Hex │ │ ├── Projection.md │ │ ├── hex-coords.png │ │ └── hex-sizes.png │ ├── Isometric │ │ ├── Projection.md │ │ ├── iso-result.png │ │ └── iso.png │ └── Tile │ │ ├── Cell.md │ │ ├── Element.md │ │ ├── Mouse.md │ │ └── Tile.md │ ├── Plugins │ ├── Animation │ │ ├── Animation.md │ │ ├── Frames.md │ │ ├── Image.md │ │ ├── Sheet.md │ │ └── frames-demo.png │ ├── Curve.md │ ├── Curves.md │ └── curves.png │ └── Shapes │ ├── Circle.md │ ├── Ellipse.md │ ├── Line.md │ ├── Path.md │ ├── Polygon.md │ ├── Rectangle.md │ └── RoundedRectangle.md ├── README.md ├── Source ├── .gitignore ├── App │ ├── App.js │ ├── Behavior.js │ ├── Behaviors │ │ ├── Behaviors.js │ │ ├── Clickable.js │ │ └── Draggable.js │ ├── Clickable.js │ ├── Container.js │ ├── Dom.js │ ├── Draggable.js │ ├── Dragger.js │ ├── Element.js │ ├── Layer.js │ ├── LayerShift.js │ ├── Light │ │ ├── Element.js │ │ ├── Image.js │ │ ├── Light.js │ │ ├── Text.js │ │ └── Vector.js │ ├── MouseHandler.js │ └── PointSearch.js ├── Context │ ├── Context2D.js │ ├── DrawImage.js │ ├── Gradients.js │ ├── Path.js │ ├── Pixels.js │ └── Text.js ├── Core │ ├── Canvas.js │ ├── Geometry.js │ ├── Mouse.js │ ├── Point.js │ ├── Point3D.js │ ├── Shape.js │ └── Size.js ├── Engines │ ├── Hex │ │ ├── Engine.js │ │ └── Projection.js │ ├── Isometric │ │ ├── Engine.js │ │ └── Projection.js │ └── Tile │ │ ├── Cell.js │ │ ├── Element.js │ │ ├── Mouse.js │ │ └── Tile.js ├── LibCanvas.js ├── Plugins │ ├── Animation │ │ ├── Animation.js │ │ ├── Frames.js │ │ ├── Image.js │ │ └── Sheet.js │ ├── Canvas2DContext.js │ ├── Curve │ │ ├── Curve.js │ │ ├── Quadratic.js │ │ └── Qubic.js │ ├── Curves.js │ ├── Image.js │ ├── ImageBuilder.js │ ├── ImagePrototype.js │ ├── ProjectiveTexture.js │ └── SpriteFont.js ├── Shapes │ ├── Circle.js │ ├── Ellipse.js │ ├── Line.js │ ├── Path.js │ ├── Polygon.js │ ├── Rectangle.js │ └── RoundedRectangle.js ├── overall.js └── package.yml └── libcanvas-full-compiled.js /.gitignore: -------------------------------------------------------------------------------- 1 | .directory 2 | .idea 3 | *.iml -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "AtomJS"] 2 | path = AtomJS 3 | url = git://github.com/theshock/atomjs.git 4 | [submodule "Packager"] 5 | path = Packager 6 | url = git@github.com:theshock/packager.git 7 | -------------------------------------------------------------------------------- /Builder/build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | 3 | getProcessedFiles() as $file){ 16 | fwrite($stderr, " * $file\n"); 17 | } 18 | fwrite($stderr, " */\n\n"); 19 | 20 | echo $builder->getResult(); 21 | 22 | fclose($stderr); 23 | -------------------------------------------------------------------------------- /Builder/builder.php: -------------------------------------------------------------------------------- 1 | _packager = new Packager($pkgDir); 17 | if (empty($components)) { 18 | $files = $this->_packager->get_all_files(); 19 | } else { 20 | $files = $this->_packager->components_to_files($components); 21 | } 22 | $this->_files = $this->_packager->complete_files($files); 23 | $this->_result = $this->_packager->build($this->_files); 24 | } 25 | 26 | public function getResult() 27 | { 28 | return $this->_result; 29 | } 30 | 31 | public function getProcessedFiles() 32 | { 33 | return $this->_files; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Builder/libcanvas-dynamic.php: -------------------------------------------------------------------------------- 1 | getResult(); 8 | 9 | 10 | -------------------------------------------------------------------------------- /Docs/Ru/App/App.md: -------------------------------------------------------------------------------- 1 | LibCanvas.App 2 | ============= 3 | 4 | `LibCanvas.App` - это основа фреймворка для построения интерактивных приложений на LibCanvas. 5 | 6 | #### Global 7 | 8 | После вызова LibCanvas.extract() можно использовать короткий алиас "App" 9 | 10 | ### Инициализация 11 | 12 | ```js 13 | var app = new LibCanvas.App( object settings ) 14 | ``` 15 | 16 | Settings может содержать следующие параметры: 17 | 18 | * `appendTo` - элемент, в который необходимо добавить приложение. По-умолчанию `body` 19 | * `size` - размеры окна приложения, объект LibCanvas.Size 20 | * `simple` - если `true`, то сгенерирует упрощённую вёрстку - из одного холста, но без возможности создавать и сдвигать слои 21 | 22 | #### Пример 23 | 24 | ```js 25 | var app = new App({ 26 | appendTo: '#container', 27 | size: new Size(800, 500) 28 | }) 29 | ``` 30 | 31 | #### Обычная разметка для трёх слоёв: 32 | 33 | ```html 34 |
35 |
36 | 37 | 38 | 39 |
40 |
41 | ``` 42 | 43 | #### Упрощённая разметка (максимум один слой): 44 | 45 | ```html 46 | 47 | ``` 48 | 49 | #### Изменение размера приложения: 50 | 51 | Изменится только размер приложения, размер каждого из слоёв останется прежним. 52 | 53 | ```js 54 | app.container.size = new Size( 1500, 1200 ); 55 | ``` 56 | 57 | 58 | ### Методы 59 | 60 | #### createLayer 61 | 62 | ```js 63 | LibCanvas.App.Layer createLayer( object settings ) 64 | ``` 65 | 66 | Создаёт и возвращает слой LibCanvas.App 67 | 68 | ```js 69 | var layer = app.createLayer({ name: 'units' }); 70 | ``` 71 | 72 | #### destroy 73 | 74 | ```js 75 | LibCanvas.App destroy( ) 76 | ``` 77 | 78 | Уничтожает приложение 79 | 80 | ```js 81 | app.destroy(); 82 | ``` 83 | 84 | #### zIndexCompare 85 | 86 | ```js 87 | int zIndexCompare( LibCanvas.App.Element left, LibCanvas.App.Element right ) 88 | ``` 89 | 90 | Сравнивает позицию каждого из элементов и возвращает -1, если левый выше, +1 если выше правый и 0, если они на одном уровне 91 | -------------------------------------------------------------------------------- /Docs/Ru/App/Element.md: -------------------------------------------------------------------------------- 1 | LibCanvas.App.Element 2 | ===================== 3 | 4 | `LibCanvas.App.Element` - абстрактный класс-каркас для создания элементов, которые будут отрисовываться 5 | 6 | В отличии от остальных классов LibCanvas этот используется исключительно через наследование. 7 | Наш инструмент - его переопределение и создание собственных методов в классе наследнике: 8 | 9 | ```js 10 | 11 | atom.declare( 'Unit', App.Element, { 12 | renderTo: function (ctx, resources) { 13 | ctx.fill( this.shape, 'red' ); 14 | } 15 | }); 16 | 17 | new Unit( layer, { 18 | shape: new Circle(100, 100, 50) 19 | }); 20 | 21 | ``` 22 | 23 | ### Встроенные методы и свойства 24 | 25 | `atom.Events events` - объект, который слушает события 26 | `atom.Settings settings` - объект с настройками. Основные свойства: 27 | * `zIndex` - порядок отрисовки элемента 28 | * `shape` - фигура, которая обозначает элемент 29 | * `hidden` (*boolean*) - прячет или отображает элемент. Скрытие через `hidden` предпочтительнее, чем просто пустая отрисовка, т.к. тогда элемент не учавствует в просчёте коллизий и не стирается его предыдущее месторасположение. 30 | 31 | #### `redraw` 32 | 33 | `LibCanvas.App.Element redraw()` 34 | 35 | Метод, который сообщает приложению, что элемент изменился. 36 | Внимание! Сам метод ничего не перерисовывает, он только ставит элемент в очередь на отрисовку. 37 | Вызов элемент очень быстр и может безболезненно повторятся много раз за кадр 38 | Контекст метода привязан к элементу, так что может быть передан в качестве колбека без потери контекста 39 | 40 | ```js 41 | animate( element, { 42 | color: 'red', 43 | onChange: element.redraw 44 | }) 45 | ``` 46 | 47 | #### `destroy` 48 | 49 | `LibCanvas.App.Element destroy()` 50 | 51 | Удаляет элемент с контекста (но не отвязывает от событий мыши, если вы были подписаны через mouseHandler!) 52 | Контекст метода привязан к элементу, так что может быть передан в качестве колбека без потери контекста 53 | 54 | ```js 55 | destroyButton.onclick = element.destroy; 56 | ``` 57 | 58 | ### Методы для переопределения 59 | 60 | #### renderTo 61 | 62 | ```js 63 | void renderTo( LibCanvas.Context2D ctx, atom.Registry resources ) 64 | ``` 65 | 66 | Рендерит елемент в контекст. Описывайте в этом методе только рендер, но не изменения объекта! 67 | По-умочанию вызывает метод `renderTo` у свойства `renderer` если есть или ничего не делает 68 | 69 | ```js 70 | 71 | atom.declare( 'Unit', App.Element, { 72 | renderTo: function (ctx, resources) { 73 | ctx.fill( this.shape, 'red' ); 74 | ctx.stroke( this.shape, 'blue' ); 75 | } 76 | }); 77 | 78 | ``` 79 | 80 | #### configure 81 | 82 | ```js 83 | void configure() 84 | ``` 85 | 86 | Вызывается сразу после конструирования. Используется в наследниках `App.Element` вместо конструктора 87 | 88 | #### get currentBoundingShape 89 | 90 | Геттер, который возвращает фигуру, описывающую влияние элемента на контекст. По-умолчанию - прямоугольник, в который вложен `shape` элемента 91 | 92 | #### isTriggerPoint 93 | 94 | ```js 95 | boolean isTriggerPoint( Point point ) 96 | ``` 97 | 98 | Определяет, является ли точка точкой срабатывания для мыши или другого устройства ввода. По-умочанию - проверяет на принадлежность `shape` 99 | 100 | #### onUpdate 101 | 102 | ```js 103 | void onUpdate( int time ) 104 | ``` 105 | 106 | Если у слоя включён invoke, то вызывается каждый кадр и служит для изменения свойств объекта согласно времени. 107 | В аргументе `time` передаётся время в милисекундах прошедшее с момента последнего кадра для корректировки и отвязки скорости приложения от FPS 108 | 109 | ```js 110 | 111 | atom.declare( 'Unit', App.Element, { 112 | onUpdate: function (time) { 113 | // вращаемся со скоростью 90 градусов в секунду 114 | this.rotate( (90).degree() * time / 1000 ); 115 | 116 | // т.к. изменения происходят каждый кадр - всегда вызываем отрисовку 117 | this.redraw(); 118 | } 119 | }); 120 | ``` 121 | 122 | #### clearPrevious 123 | 124 | ```js 125 | void clearPrevious( LibCanvas.Context2D ctx ) 126 | ``` 127 | 128 | Очищает предыдущее расположение элемента в ctx. По-умолчанию - стирает `this.previousBoundingShape` 129 | 130 | #### distanceMove 131 | 132 | ```js 133 | void distanceMove( LibCanvas.Point point ) 134 | ``` 135 | 136 | Смещает элемент на расстояние `point`. Используется, например, в `App.Draggable`. -------------------------------------------------------------------------------- /Docs/Ru/App/Layer.md: -------------------------------------------------------------------------------- 1 | LibCanvas.App.Layer 2 | =================== 3 | 4 | `LibCanvas.App.Layer` - класс для создания слоёв LibCanvas приложения 5 | 6 | ### Инициализация 7 | 8 | Создаётся только при помощи метода `LibCanvas.App#createLayer`: 9 | 10 | ```js 11 | LibCanvas.App.Layer app.createLayer( object settings ); 12 | ``` 13 | 14 | Settings может содержать следующие параметры: 15 | 16 | * `name` (*string*) - имя слоя (необходимо только для отладки) 17 | * `zIndex` (*number*) - z-index слоя 18 | * `invoke` (*boolean*) - необходимо ли вызывать у всех объектов метод `onUpdate` каждый кадр (по-умолчанию `false`) 19 | * `intersection` - при перерисовке одного из элементов, необходимо ли перерисовывать остальные. Значения: 20 | * `auto` (по-умолчанию) - только те, которые необходимо для корректной отрисовки 21 | * `manual` - нет, ни одного (например, когда вы хотите лично управлять перерисовкой) 22 | * `all` - да, все (например, если это дешевле, чем просчитывать все пересечения) 23 | * `full` - стирать весь холст и рисовать всё с нуля 24 | 25 | #### Пример 26 | 27 | ```js 28 | var layer = app.createLayer({ 29 | name : 'units', 30 | zIndex: 3, 31 | invoke: true, 32 | intersection: 'all' 33 | }) 34 | ``` 35 | 36 | #### Изменение размера слоя 37 | 38 | Изменится только размер определённого слоя. Размер приложения и остальных слоёв останется прежним. 39 | Необходимо помнить, что изменение размера слоя уничтожит все отрисованные данные, потому необходимо вызвать `layer.redrawAll()` 40 | 41 | ```js 42 | layer.dom.size = new Size(1500, 1200); 43 | layer.redrawAll() 44 | ``` 45 | 46 | ### Свойства 47 | 48 | #### ctx 49 | 50 | `2d-libcanvas` контекст элемента canvas слоя 51 | 52 | ```js 53 | layer.ctx.fillAll( 'red' ) 54 | ``` 55 | 56 | ### Методы 57 | 58 | #### stop 59 | 60 | ```js 61 | LibCanvas.App.Layer stop() 62 | ``` 63 | 64 | Остановить отрисовку слоя 65 | 66 | ```js 67 | layer.stop() 68 | ``` 69 | 70 | #### start 71 | 72 | ```js 73 | LibCanvas.App.Layer start() 74 | ``` 75 | 76 | Возобновить отрисовку слоя 77 | 78 | ```js 79 | layer.start() 80 | ``` 81 | 82 | #### hide 83 | 84 | ```js 85 | LibCanvas.App.Layer hide() 86 | ``` 87 | 88 | Временно скрыть слой 89 | 90 | ```js 91 | layer.hide() 92 | ``` 93 | 94 | #### show 95 | 96 | ```js 97 | LibCanvas.App.Layer show() 98 | ``` 99 | 100 | Снова показать слой 101 | 102 | ```js 103 | layer.show() 104 | ``` 105 | 106 | #### redrawAll 107 | 108 | ```js 109 | LibCanvas.App.Layer redrawAll() 110 | ``` 111 | 112 | Перерисовывает все элементы слоя 113 | 114 | ```js 115 | layer.redrawAll() 116 | ``` 117 | -------------------------------------------------------------------------------- /Docs/Ru/App/Light/Light.md: -------------------------------------------------------------------------------- 1 | LibCanvas.App.Light 2 | =================== 3 | 4 | `LibCanvas.App.Light` - надстройка над `LibCanvas.App` для более лёгкого и доступного интерфейса 5 | 6 | ### Инициализация 7 | 8 | ```js 9 | var helper = new LibCanvas.App.Light( LibCanvas.Size size, object settings ) 10 | ``` 11 | 12 | `size` - размер приложения 13 | 14 | Settings может содержать следующие параметры: 15 | 16 | * `mouse` - будет ли использоваться мышь (по-умолчанию `true`) 17 | * `appendTo` - в какой элемент необходимо прикрепить приложение (по-умолчанию `body`) 18 | 19 | #### Пример 20 | 21 | ```js 22 | var helper = new LibCanvas.App.Light( new Size(800, 500) ) 23 | ``` 24 | 25 | ### Методы 26 | 27 | #### createVector 28 | 29 | ```js 30 | App.Light.Vector createVector( LibCanvas.Shape shape, object settings ) 31 | ``` 32 | 33 | Создаёт, добавляет в приложение и возвращает элемент App.Light.Vector, который служит для отрисовки геометрических фигур в приложении 34 | 35 | ```js 36 | var vector = helper.createVector( new Circle(100, 100, 20) ); 37 | ``` 38 | 39 | #### createText 40 | 41 | ```js 42 | App.Light.Text createText( LibCanvas.Shape shape, object style, object settings ) 43 | ``` 44 | 45 | Создаёт, добавляет в приложение и возвращает элемент App.Light.Text, который служит для отрисовки текста в приложении 46 | Содержимое `style` согласно интерфейса метода [Context2D.text](https://github.com/theshock/libcanvas/blob/master/Docs/Ru/Core/Context2D.md#%D0%9C%D0%B5%D1%82%D0%BE%D0%B4-text) 47 | 48 | ```js 49 | var text = helper.createText( 50 | new Rectangle(0, 0, 100, 40), 51 | { 52 | text: 'Hello, World!', 53 | bold: true 54 | } 55 | ); 56 | ``` 57 | 58 | #### createImage 59 | 60 | ```js 61 | App.Light.Image createImage( LibCanvas.Shape shape, Image image, object settings ) 62 | ``` 63 | 64 | Создаёт, добавляет в приложение и возвращает элемент App.Light.Image, который служит для отрисовки картинок в приложении 65 | 66 | ```js 67 | 68 | atom.ImagePreloader.run({ logo: 'html5-logo.png' }, 69 | function (images) { 70 | var element = helper.createImage( 71 | new Rectangle(64, 64, 256, 256), 72 | images.get('logo') 73 | ); 74 | }); 75 | ``` -------------------------------------------------------------------------------- /Docs/Ru/App/MouseHandler.md: -------------------------------------------------------------------------------- 1 | LibCanvas.App.MouseHandler 2 | ========================== 3 | 4 | `LibCanvas.App.MouseHandler` - класс, который отвечает за взаимосвязь событий LibCanvas.Mouse и объектов LibCanvas.App.Element. 5 | 6 | ### Инициализация 7 | 8 | ```js 9 | LibCanvas.App.MouseHandler( object settings ); 10 | ``` 11 | 12 | Settings может содержать следующие параметры: 13 | 14 | * `app` (*LibCanvas.App*) - приложение, которое необходимо слушать 15 | * `mouse` (*LibCanvas.Mouse*) - объект LibCanvas.Mouse, который будет оповещать об изменениях мыши 16 | * `search` (*LibCanvas.App.ElementsMouseSearch*) - можно указать альтернативный объект, отвечающий за поиск тригернутого элемента, может использоваться для оптимизации 17 | 18 | #### Пример 19 | 20 | ```js 21 | var app, mouse, mouseHandler; 22 | 23 | app = new LibCanvas.App({ size: new Size(300,200) }); 24 | mouse = new LibCanvas.Mouse(app.container.bounds); 25 | mouseHandler = new LibCanvas.App.MouseHandler({ app: app, mouse: mouse }); 26 | ``` 27 | 28 | Поисковик по-умолчанию (LibCanvas.App.ElementsMouseSearch) проверяет элементы на срабатывание вызывая `isTriggerPoint( Point )` 29 | 30 | ### События 31 | 32 | После подписки все элементы получают следующие события: 33 | 34 | * click 35 | * dblclick 36 | * contextmenu 37 | * wheel 38 | * mousedown 39 | * mouseup 40 | * mouseout 41 | * mouseover 42 | * mousemove 43 | 44 | ### Методы 45 | 46 | #### stop 47 | 48 | ```js 49 | LibCanvas.App.MouseHandler stop() 50 | ``` 51 | 52 | Остановить обработку событий мыши 53 | 54 | ```js 55 | mouseHandler.stop() 56 | ``` 57 | 58 | #### start 59 | 60 | ```js 61 | LibCanvas.App.MouseHandler start() 62 | ``` 63 | 64 | Возобновить обработку событий мыши 65 | 66 | ```js 67 | mouseHandler.start() 68 | ``` 69 | 70 | #### subscribe 71 | 72 | ```js 73 | LibCanvas.App.MouseHandler subscribe(LibCanvas.App.Element element) 74 | ``` 75 | 76 | Подписать элемент на события мыши. 77 | 78 | ```js 79 | mouseHandler.subscribe( element ); 80 | 81 | element.events.add( 'click', function (e) { 82 | console.log( 'element поймал клик мыши', e ); 83 | }) 84 | ``` 85 | 86 | #### unsubscribe 87 | 88 | ```js 89 | LibCanvas.App.MouseHandler unsubscribe(LibCanvas.App.Element element) 90 | ``` 91 | 92 | Отписать элемент от событий мыши. Если элемент был удалён с приложения при помощи метода "destroy", то отписка от событий мыши будет активированна автоматически при первом срабатывании события (но не сразу после уничтожения). Скрытые через `hidden: true` элементы всё так же получают события мыши. 93 | 94 | ```js 95 | mouseHandler.unsubscribe( element ); 96 | // Элемент больше не ловит события мыши 97 | ``` 98 | 99 | #### getOverElements 100 | 101 | ```js 102 | LibCanvas.App.Element[] getOverElements() 103 | ``` 104 | 105 | Получить список элементов, над которыми находится мышь в данный момент (в порядке уменьшения z-index) 106 | 107 | ```js 108 | var overElements = mouseHandler.getOverElements(); 109 | overElements.invoke('destroy'); 110 | ``` 111 | 112 | #### fall 113 | 114 | ```js 115 | LibCanvas.App.MouseHandler fall() 116 | ``` 117 | 118 | Сообщает о необходимости провалиться событию мыши. Важно помнить, что если элемент подписан на события мыши, то он "блокирует" все элементы ниже. То есть при клике на нём события не сработают на элементах под ним даже если они тоже попадают в радиус действия мыши. Если по какой-то причине такое поведение не устраивает (элемент должен ловить события сам, но и не блокировать их для элементов, которые покрывает) можно использовать "проваливание": 119 | 120 | ```js 121 | mouseHandler.subscribe( element ); 122 | 123 | element.events.add( 'mousedown', function (e) { 124 | console.log('Мышь нажата над нашим элементом', e); 125 | // Но элемент под ним тоже получит это событие 126 | mouseHandler.fall(); 127 | }); 128 | ``` 129 | 130 | -------------------------------------------------------------------------------- /Docs/Ru/Core/Canvas.md: -------------------------------------------------------------------------------- 1 | Создание своих контекстов 2 | ========================= 3 | 4 | Улучшение прототипа HTMLCanvasElement позволяет легко создавать свои контексты при помощи LibCanvas. 5 | Достаточно вызвать `HTMLCanvasElement.addContext(name, ContextClass)`, где `ContextClass` - это класс с конструктором, принимающим первым аргументом ссылку на элемент canvas. 6 | 7 | #### Пример 8 | 9 | ```js 10 | new function () { 11 | 12 | var ContextClass = atom.declare({ 13 | initialize: function (canvas) { 14 | this.canvas = canvas; 15 | this.ctx2d = canvas.getContext('2d'); 16 | }, 17 | welcome: function () { 18 | this.ctx2d.fillText('Hello World', 0, 0); 19 | } 20 | }); 21 | 22 | HTMLCanvasElement.addContext('2d-hello', ContextClass); 23 | } 24 | ``` 25 | 26 | #### Использование: 27 | 28 | ```js 29 | var myContext = canvas.getContext('2d-hello'); 30 | myContext.welcome(); 31 | myContext.fillRect(0, 0, 10, 10); // Error 32 | ``` 33 | 34 | Методы стандартного контекста не реализованы, потому `fillRect` вернет ошибку. 35 | Самый простой способ избежать этого - унаследоваться от контекста LibCanvas. 36 | Обратите внимание - конструктор мы унаследовали от родительского `LibCanvas.Context2D`, потому достаточно реализовать методы. 37 | 38 | #### Пример 39 | 40 | ```js 41 | new function () { 42 | var HelloContextClass = atom.declare( LibCanvas.Context2D, { 43 | welcome: function () { 44 | return this.ctx2d.fillText('Hello World', 0, 0); 45 | } 46 | }); 47 | 48 | HTMLCanvasElement.addContext('2d-libcanvas-hello', HelloContextClass); 49 | } 50 | ``` 51 | 52 | #### Использование: 53 | 54 | ```js 55 | var myContext = canvas.getContext('2d-libcanvas-hello'); 56 | myContext.welcome(); 57 | myContext.fillRect(0, 0, 10, 10); // Success 58 | ``` 59 | 60 | Желательно в названии указывать иерархию контекстовв порядке убывания, через тире. 61 | 62 | #### Canvas2DContext 63 | Вы можете использовать плагин `Canvas2DContext` для создания своего контекста на базе нативного (с максимально похожим интерфейсом) 64 | 65 | ```js 66 | new function () { 67 | 68 | var HelloContextClass = atom.declare( LibCanvas.Canvas2DContext, { 69 | welcome: function () { 70 | return this.ctx2d.fillText('Hello World', 0, 0); 71 | } 72 | }); 73 | 74 | HTMLCanvasElement.addContext('2d-hello', HelloContextClass); 75 | 76 | } 77 | ``` 78 | 79 | #### Использование: 80 | 81 | ```js 82 | var myContext = canvas.getContext('2d-hello'); 83 | myContext.welcome(); 84 | myContext.fillRect(0, 0, 10, 10); // Success 85 | ``` 86 | 87 | ### Внимание! 88 | Крайне не рекомендуется переопределять встроенные в браузер контексты (которые всё-равно можно получить через метод `getOriginalContext`), т.к. это принесет в приложение очень неожиданное поведение. 89 | Так же не рекомендуется переопределять контекст `2d-libcanvas` 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /Docs/Ru/Core/LibCanvas.md: -------------------------------------------------------------------------------- 1 | LibCanvas 2 | ========= 3 | 4 | `LibCanvas` - это глобальный объект, который является корнем пространства имён библиотеки. Он содержит несколько статических методов 5 | 6 | 7 | ## Статический метод extract 8 | 9 | ```js 10 | object LibCanvas.extract(object to = window) 11 | ``` 12 | 13 | Позволяет извлечь некоторые классы LibCanvas в глобальное пространство имен (или в локальный объект) для более короткой записи 14 | 15 | #### Пример 16 | 17 | ```js 18 | // Стандартный подход: 19 | var circle = new LibCanvas.Shapes.Circle(100, 100, 20); 20 | 21 | // Извлекаем в локальную переменную 22 | var LC = LibCanvas.extract({}); 23 | var circle = new LC.Circle(100, 100, 20); 24 | 25 | // Извлекаем в глобальное пространство имен: 26 | LibCanvas.extract(); 27 | var circle = new Circle(100, 100, 20); 28 | ``` 29 | 30 | ## Статический метод buffer 31 | 32 | ```js 33 | canvasElement LibCanvas.buffer(int width, int height, bool withCtx) 34 | canvasElement LibCanvas.buffer(LibCanvas.Size size, bool withCtx) 35 | ``` 36 | 37 | Создает и возвращает элемент Canvas размером width*height. 38 | Если withCtx правдив, то свойство `ctx` элемента будет равно контексту '2d-libcanvas' 39 | 40 | 41 | #### Пример 42 | 43 | ```js 44 | var buffer = LibCanvas.buffer(100, 100, true); 45 | buffer.ctx.fillAll('black'); 46 | 47 | libcanvas.ctx.drawImage(buffer, 10, 10); 48 | ``` -------------------------------------------------------------------------------- /Docs/Ru/Core/Mouse.md: -------------------------------------------------------------------------------- 1 | LibCanvas.Mouse 2 | =============== 3 | 4 | `LibCanvas.Mouse` предоставляет интерфейс для прозрачного управления мышью 5 | 6 | #### Global 7 | 8 | После вызова LibCanvas.extract() можно использовать короткий алиас "Mouse" 9 | 10 | ## Статические свойства 11 | 12 | #### метод prevent 13 | 14 | Может использовать, чтобы заглушить событие мыши по-умолчанию: 15 | 16 | ```js 17 | window.onclick = Mouse.prevent; 18 | ``` 19 | 20 | #### метод getOffset 21 | 22 | ```js 23 | LibCanvas.Point getOffset( MouseEvent e, DOMElement element ) 24 | ``` 25 | 26 | Определяет положение мыши относительно элемента 27 | 28 | ```js 29 | var offset = Mouse.getOffset( event, canvas ); 30 | ``` 31 | 32 | #### метод addWheelDelta 33 | 34 | ```js 35 | MouseEvent addWheelDelta( MouseEvent e ) 36 | ``` 37 | 38 | Добавляет кроссбраузерное свойство `delta` в объект события, которое обозначает направление движения колёсика мыши 39 | 40 | ## Создание экземпляра LibCanvas.Mouse 41 | 42 | Первым аргументом принимает элемент, события которого надо слушать. 43 | 44 | ```js 45 | var mouse = new LibCanvas.Mouse( myCanvas ); 46 | ``` 47 | 48 | ## Свойства 49 | 50 | `point` - `LibCanvas.Point`, с текущими координатами мыши относительно начала элемента 51 | 52 | `previous` - `LibCanvas.Point`, предыдущие координаты мыши относительно начала элемента 53 | 54 | `delta` - `LibCanvas.Point`, последнее смещение координат мыши 55 | 56 | `events` - объект `atom.Events` 57 | 58 | ## События 59 | 60 | * click 61 | * dblclick 62 | * contextmenu 63 | * wheel 64 | * over 65 | * out 66 | * down 67 | * up 68 | * move 69 | 70 | #### Пример 71 | 72 | ```js 73 | mouse.events.add( 'click', function (event, mouse) { 74 | // нарисует круг радиусом 10 пикселей в точке клика: 75 | canvas.ctx.fill( 76 | new Circle( mouse.point, 10 ) 77 | ); 78 | }); 79 | ``` 80 | 81 | #### Особенности 82 | 83 | Событие `wheel` имеет дополнительное свойство `delta`, которого обозначает направление движения колёсика - "-1" или "1". 84 | 85 | -------------------------------------------------------------------------------- /Docs/Ru/Core/Point3D.md: -------------------------------------------------------------------------------- 1 | LibCanvas.Point3D 2 | ================= 3 | 4 | `LibCanvas.Point3D` - класс, который описывает точку в трёхмерном пространстве. 5 | 6 | #### Global 7 | 8 | После вызова LibCanvas.extract() можно использовать короткий алиас "Point3D" 9 | 10 | ## Создание экземпляра LibCanvas.Point3D 11 | 12 | 13 | Создать экземпляр класса `LibCanvas.Point` можно одним из следующих способов: 14 | 15 | ```js 16 | var xCoord = 15, yCoord = 20, zCoord = 25; 17 | 18 | // передав в конструктор координаты 19 | var point = new LibCanvas.Point3D( xCoord, yCoord, zCoord ); 20 | 21 | // массив координат 22 | var point = new LibCanvas.Point3D([xCoord, yCoord, zCoord]); 23 | 24 | // объект координат 25 | var point = new LibCanvas.Point3D({ x : xCoord, y : yCoord, z: zCoord }); 26 | 27 | // Другой объект LibCanvas.Point3D 28 | var point = new LibCanvas.Point3D(anotherPoint); 29 | 30 | // что равнозначно с 31 | var point = anotherPoint.clone(); 32 | 33 | // после использования LibCanvas.extract(): 34 | var point = new Point3D( xCoord, yCoord, zCoord ); 35 | ``` 36 | 37 | ## Метод equals 38 | 39 | ```js 40 | boolean equals(LibCanvas.Point3D to, int accuracy) 41 | ``` 42 | 43 | Метод сравнивает две точки не по ссылкам 44 | 45 | #### аргумент `accuracy` 46 | 47 | Если указан, то означает количество знаков, с точностью которых будут сравниватся точки (для неточного сравнения) 48 | 49 | #### Пример 50 | 51 | ```js 52 | var bar = new LibCanvas.Point3D(15, 15, 10); 53 | var foo = new LibCanvas.Point3D(15, 15, 10); 54 | 55 | trace(bar == foo); // false 56 | trace(bar.equals(foo)); // true 57 | ``` 58 | 59 | #### Пример с accuracy 60 | 61 | ```js 62 | var bar = new LibCanvas.Point3D(7, 12.88888324, 15.1111127); 63 | var foo = new LibCanvas.Point3D(7, 12.88888115, 15.1111093); 64 | 65 | console.log(bar == foo); // false 66 | console.log(bar.equals(foo)); // false 67 | console.log(bar.equals(foo, 8)); // false 68 | console.log(bar.equals(foo, 4)); // true 69 | ``` 70 | 71 | ## Метод clone 72 | 73 | ```js 74 | LibCanvas.Point3D clone() 75 | ``` 76 | 77 | Возвращает точку с такими же координатами 78 | 79 | #### Пример 80 | 81 | ```js 82 | var point = new LibCanvas.Point3D(15, 15, 10); 83 | var clone = point.clone(); 84 | console.log(point == clone); // false 85 | console.log(point.equals(clone)); // true 86 | ``` 87 | 88 | ## Метод move 89 | 90 | ```js 91 | LibCanvas.Point move(LibCanvas.Point point3d) 92 | ``` 93 | 94 | #### Пример 95 | 96 | ```js 97 | var point = new LibCanvas.Point3D(10, 10, 10); 98 | var distance = new LibCanvas.Point3D( 5, -3, 1); 99 | 100 | point.move(distance); // Point3D(15, 7, 11) 101 | ``` 102 | 103 | ## Метод diff 104 | 105 | ```js 106 | LibCanvas.Point3D diff(LibCanvas.Point3D point) 107 | ``` 108 | 109 | Этот метод означает примерно следующее : 110 | на сколько надо сдвинуться точке, чтобы она оказалась на месте той, которая передана первым аргументом 111 | 112 | #### Пример 113 | 114 | ```js 115 | var pO = new LibCanvas.Point3D(10, 10, 7); 116 | var pA = new LibCanvas.Point3D(15, 18, 7); 117 | 118 | pA.diff(pO); // Point3D(-5, -8, 0) 119 | `` 120 | 121 | ## Метод map 122 | 123 | ```js 124 | LibCanvas.Point3D map(callback, context) 125 | ``` 126 | 127 | Изменяет значения точки согласно результату вызова callback 128 | 129 | #### Пример 130 | 131 | ```js 132 | var point = new LibCanvas.Point3D(1, 2, 3); 133 | 134 | point.map(function (value, coord, point) { 135 | return value * value; 136 | }); 137 | 138 | atom.trace( point ); // Point3D(1, 4, 9) 139 | ``` 140 | 141 | ## Метод add 142 | 143 | ```js 144 | LibCanvas.Point3D add(value) 145 | ``` 146 | 147 | Увеличить значение всех координат точки на `value` 148 | 149 | #### Пример 150 | 151 | ```js 152 | var point = new LibCanvas.Point3D(1, 2, 3); 153 | 154 | point.add(5); 155 | 156 | atom.trace( point ); // Point3D(6, 7, 8) 157 | ``` 158 | 159 | ## Метод mul 160 | 161 | ```js 162 | LibCanvas.Point3D mul(value) 163 | ``` 164 | 165 | Умножить значение всех координат точки на `value` 166 | 167 | #### Пример 168 | 169 | ```js 170 | var point = new LibCanvas.Point3D(1, 2, 3); 171 | 172 | point.mul(5); 173 | 174 | atom.trace( point ); // Point3D(5, 10, 15) 175 | ``` -------------------------------------------------------------------------------- /Docs/Ru/Core/Size.md: -------------------------------------------------------------------------------- 1 | LibCanvas.Size 2 | ============== 3 | 4 | `LibCanvas.Size` - расширение `LibCanvas.Point`, обладатает теми же свойствами, но `width` является алиасом для `x`, а `height` - алиасом для `y`. 5 | 6 | ```js 7 | var size = new LibCanvas.Size({ width: 15, height: 35 }); 8 | ``` 9 | 10 | #### Global 11 | 12 | После вызова LibCanvas.extract() можно использовать короткий алиас "Size" 13 | -------------------------------------------------------------------------------- /Docs/Ru/Engines/Hex/hex-coords.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theshock/libcanvas/26010cb04c6777fde6c5174e25115597de072fc7/Docs/Ru/Engines/Hex/hex-coords.png -------------------------------------------------------------------------------- /Docs/Ru/Engines/Hex/hex-sizes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theshock/libcanvas/26010cb04c6777fde6c5174e25115597de072fc7/Docs/Ru/Engines/Hex/hex-sizes.png -------------------------------------------------------------------------------- /Docs/Ru/Engines/Isometric/Projection.md: -------------------------------------------------------------------------------- 1 | Engines.Hex.Isometric 2 | ===================== 3 | 4 | Библиотека для работы с изометрической проекцией 3d-координат. 5 | 6 | #### Global 7 | 8 | После вызова LibCanvas.extract() можно использовать короткий алиас "IsometricEngine" 9 | 10 | IsometricEngine.Projection 11 | ========================== 12 | 13 | Предпоставляет лёгкую возможность транслировать координаты из 3d-координат в пространстве в изометрические 2d-координаты и обратно. 14 | 15 | #### Координаты 16 | 17 | Координаты транслируются очень просто. Положительный сдвиг по оси Х смещает точку вверх-вправо, а положительный сдвиг по оси У - вниз-вправо. Положительный сдвиг по оси Z перемещает точку точно вверх. 18 | 19 | ![Изометрическая карта](https://raw.github.com/theshock/libcanvas/master/Docs/Ru/Engines/Isometric/iso.png) 20 | 21 | #### Размер пикселя 22 | 23 | Существуют разные подходы к трансформации пикселей. Правильная изометрическая проекция требует угла в 30 градусов, но часто в компьютерных играх используется более острый угол, чтобы пропорции ячейки были 2:1. За эту величину отвечает настройка "factor": 24 | 25 | ```js 26 | factor: new Point3D(0.866, 0.5, 0.866) // правильная проекция 27 | factor: new Point3D( 1, 0.5, 1) // пропорциональная проекция 28 | ``` 29 | 30 | Первое значение отвечает за ширину пикселя по Х координате, второе - за ширину пикселя по У координате, третье - за высоту по Z координате. 31 | 32 | Вы также можете пропорционально изменять пиксель при помощи настройки `size` 33 | 34 | Настройка `start` необходима для указания стартовых координат (где находится пиксель [0;0;0]). 35 | 36 | Пример: 37 | 38 | ```js 39 | var projection = new IsometricEngine.Projection({ 40 | factor: new Point3D(1, 0.5, 1), // карта будет пропорциональной 41 | size : 2, // увеличиваем пиксели в два раза 42 | start : new Point(100, 100) - сетка начинается с лёгким отступом 43 | }); 44 | ``` 45 | 46 | ### Методы 47 | 48 | #### toIsometric 49 | 50 | ```js 51 | Point toIsometric( Point3D point3d ) 52 | ``` 53 | 54 | Транслирует координаты точки из трёхмерных в пространстве в двумерные на экране. Используется, например, при отрисовке. 55 | 56 | ```js 57 | var playersCoord = [ 58 | new Point3D(100, 50, 10), 59 | new Point3D( 80, 20, 0), 60 | new Point3D( 20, 130, 4) 61 | ]; 62 | 63 | playersCoord.forEach(function (coord3d) { 64 | ctx.fill(new Circle( 65 | projection.toIsometric(coord3d), 10 66 | )); 67 | }); 68 | ``` 69 | 70 | #### to3D 71 | 72 | ```js 73 | Point3D to3D( Point point3d[, int z = 0] ) 74 | ``` 75 | 76 | Транслирует координаты точки из двумерных на экране в трёхмерные в пространстве. Может использоваться для определения координаты поля при клике мышью. Т.к. точно нельзя определить на какой высоте находится текущая точка - можно использовать опциональный аргумент. 77 | 78 | ```js 79 | mouse.events.add('click', function (e) { 80 | var mapCoord = projection.to3d( mouse.point ); 81 | 82 | atom.trace( mapCoord ); 83 | }); 84 | ``` 85 | 86 | #### Пример 87 | 88 | ```js 89 | atom.dom(function () { 90 | function point (x, y, z) { 91 | return projection.toIsometric(new Point3D( x, y, z )); 92 | } 93 | function createColor (x, y) { 94 | // Цвет будет рассчитываться динамически 95 | // atom.Color проследит, чтобы мы не вылезли за границы 96 | // При смещении по оси Х клетка будет краснее 97 | // При смещении по оси Y клетка будет менее зелёной 98 | // При увеличении разницы между X и Y клетка будет терять синеву 99 | // Итог: левая клетка - зелёная, правая - розовая, нижняя - синяя, верхняя - жёлтая 100 | return new atom.Color( 128 + 24*x, 255 - 24*y, 128-24*(x-y)).toString(); 101 | } 102 | function drawPoly(x, y) { 103 | // создаём полигон из 4 соседних точек. Высота в примере всегда равна нулю 104 | var poly = new Polygon( 105 | point(x+0,y+0,0), 106 | point(x+0,y+1,0), 107 | point(x+1,y+1,0), 108 | point(x+1,y+0,0) 109 | ); 110 | 111 | buffer.ctx 112 | .fill(poly, createColor(x, y)) 113 | .stroke(poly); 114 | 115 | buffer.ctx.fillText( x + '/' + y, poly.center ); 116 | } 117 | function drawMap (width, height) { 118 | var x, y; 119 | for (x = 0; x < width; x++) for (y = 0; y < height; y++) { 120 | drawPoly(x,y); 121 | } 122 | } 123 | 124 | var buffer, x, y, projection; 125 | 126 | LibCanvas.extract(); 127 | 128 | // Создаём холст, на котором будем рисовать карту 129 | buffer = LibCanvas.buffer(800, 480, true); 130 | // Добавляем его на наш экран 131 | atom.dom(buffer).appendTo('body'); 132 | // Базовая настройка холста - заливаем фоном и указываем стили 133 | buffer.ctx 134 | .fillAll('#eee') 135 | .set({ 136 | fillStyle : 'black', 137 | strokeStyle : '#777', 138 | textAlign : 'center', 139 | textBaseline: 'middle', 140 | font: '14px monospace' 141 | }); 142 | 143 | // создаём проекцию, размер пикселя будет увеличен в 60 раз 144 | projection = new IsometricEngine.Projection({ 145 | start: new Point(40, 240), 146 | size : 60 147 | }); 148 | // отрисовываем карту размером 7*7 клеток 149 | drawMap(7, 7); 150 | }); 151 | ``` 152 | 153 | ![Результат выполнения кода](https://raw.github.com/theshock/libcanvas/master/Docs/Ru/Engines/Isometric/iso-result.png) 154 | -------------------------------------------------------------------------------- /Docs/Ru/Engines/Isometric/iso-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theshock/libcanvas/26010cb04c6777fde6c5174e25115597de072fc7/Docs/Ru/Engines/Isometric/iso-result.png -------------------------------------------------------------------------------- /Docs/Ru/Engines/Isometric/iso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theshock/libcanvas/26010cb04c6777fde6c5174e25115597de072fc7/Docs/Ru/Engines/Isometric/iso.png -------------------------------------------------------------------------------- /Docs/Ru/Engines/Tile/Cell.md: -------------------------------------------------------------------------------- 1 | Engines.Tile.Engine.Cell 2 | ======================== 3 | 4 | Каждая ячейка тайлового движка является объектом `TileEngine.Cell` 5 | Эти объекты не создаются вручную, только внутри `TileEngine` и используются для двух целей: 6 | * Изменение и получение значения ячейки 7 | * Контейнер данных для отрисовки ячейки 8 | 9 | ### Свойства 10 | 11 | * `engine` - ссылка на движок `TileEngine` 12 | * `point` - координаты ячейки на поле 13 | * `rectangle` - прямоугольник, описывающий ячейку в пикселях 14 | * `value` - текущее значение ячейки. Изменение этого свойства автоматически обновляет `requireUpdate` у `TileEngine` 15 | -------------------------------------------------------------------------------- /Docs/Ru/Engines/Tile/Element.md: -------------------------------------------------------------------------------- 1 | Engines.Tile.Engine.Element 2 | =========================== 3 | 4 | `TileEngine.Element` - это мост для связи `TileEngine` и `LibCanvas.App`. 5 | Необходим для встраивания тайлового движка в `LibCanvas.App` и подписи на события мыши. 6 | 7 | ### Инициализация 8 | 9 | ```js 10 | new TileEngine.Element( App.Layer layer, object settings ) 11 | ``` 12 | 13 | Settings может содержать следующие параметры: 14 | 15 | * `engine` (*TileEngine*) - обязательный параметр, ссылка на тайловый движок 16 | * `from` (*LibCanvas.Point*) - необязательный параметр, смещение отрисовки 17 | 18 | ```js 19 | var engineElement = new TileEngine.Element( 20 | app.createLayer('tile-engine'), 21 | { engine: engine } 22 | ) 23 | ``` 24 | 25 | Не стоит добавлять на слой с тайловым движком ещё какие либо элементы - отрисовка может быть некорректной. 26 | 27 | ### TileEngine.Element.app 28 | 29 | ```js 30 | TileEngine.Element.app( App app, TileEngine engine, Point from = null ) 31 | ``` 32 | 33 | Используется для более простых приложений - создаёт корректный слой в `LibCanvas.App`, создаёт и добавляет в слой элемент, возвращает этот элемент. 34 | По сути это просто сокращённая запись для создания элемента и слоя для него. 35 | 36 | ```js 37 | var engineElement = TileEngine.Element.app( app, engine ) 38 | ``` -------------------------------------------------------------------------------- /Docs/Ru/Engines/Tile/Mouse.md: -------------------------------------------------------------------------------- 1 | Engines.Tile.Engine.Mouse 2 | ========================= 3 | 4 | `TileEngine.Mouse` - специальный обработчик для оптимизированной обработки мыши при помощи делегации вместо подписывания каждой ячейки на свои события. 5 | 6 | ### Инициализация 7 | 8 | ```js 9 | new TileEngine.Mouse( TileEngine.Element element, LibCanvas.Mouse mouse ) 10 | ``` 11 | 12 | Важно помнить, что `element` должен быть предварительно подписан на события мыши при помощи объекта `App.MouseHandler` 13 | 14 | ### События 15 | 16 | * `over` - мышь наведена на ячейку 17 | * `out` - мышь убрана с ячейки 18 | * `down` - мышь нажата на ячейку 19 | * `up` - мышь отжата на ячейке 20 | * `click` - `down` и `up` сработали на одной ячейке 21 | * `contextmenu` - контекстное меню вызвано над ячейкой 22 | 23 | Во всех событиях первым аргументом передаётся ячейка, которая вызвала событие. 24 | 25 | 26 | #### Полный пример 27 | 28 | ```js 29 | element = TileEngine.Element.app( app, engine ); 30 | 31 | mouse = new Mouse(app.container.bounds); 32 | 33 | new App.MouseHandler({ mouse: mouse, app: app }) 34 | .subscribe( element ); 35 | 36 | new TileEngine.Mouse( element, mouse ).events.add({ 37 | over: function (cell) { 38 | console.log( 'mouse over ', cell ); 39 | }, 40 | out: function (cell) { 41 | console.log( 'mouse out of ', cell ); 42 | }, 43 | click: function (cell) { 44 | console.log( 'mouse click at ', cell ); 45 | } 46 | }); 47 | ``` -------------------------------------------------------------------------------- /Docs/Ru/Engines/Tile/Tile.md: -------------------------------------------------------------------------------- 1 | Engines.Tile.Engine 2 | =================== 3 | 4 | Основа тайлового движка. Отвечает за построение матрицы, хранение методов отрисовки и является фабрикой ячеек. 5 | 6 | #### Global 7 | 8 | После вызова LibCanvas.extract() можно использовать короткий алиас "TileEngine" 9 | 10 | ### Инициализация 11 | 12 | ```js 13 | var engine = new TileEngine( object settings ) 14 | ``` 15 | 16 | Settings может содержать следующие параметры: 17 | 18 | * `size` (*LibCanvas.Size*) - размеры поля в ячейках 19 | * `cellSize` (*LibCanvas.Size*) - размер ячейки в пикселях 20 | * `cellMargin` (*LibCanvas.Size*) - отступы между ячейками 21 | * `defaultValue` (*mixed*) - значение по-умолчанию для ячейки 22 | 23 | ```js 24 | engine = new TileEngine({ 25 | // Поле размером 40*25 ячеек 26 | size: new Size(40, 25), 27 | // каждая ячейка размером 10*10 28 | cellSize: new Size(10, 10), 29 | // отступ между ячейками 1 пиксель 30 | cellMargin: new Size(1, 1), 31 | // значение по-умолчанию 32 | defaultValue: 'unknown' 33 | }) 34 | ``` 35 | 36 | ### Свойства 37 | 38 | `get width` - ширина поля в ячейках 39 | `get height` - высота поля в ячейках 40 | `get requireUpdate` - есть обновившиеся после последнего рефреша ячейки 41 | 42 | ### Методы 43 | 44 | #### setMethod 45 | 46 | ```js 47 | TileEngine setMethod( string name, Image value ) 48 | TileEngine setMethod( string name, function value ) 49 | TileEngine setMethod( string name, mixed value ) 50 | TileEngine setMethod( object methods ) 51 | ``` 52 | 53 | Указывает способ отрисовки для каждого значения ячейки. 54 | 55 | Если значение типа Image, то в ячейку с соответствующим значением будет отрисована картинка 56 | 57 | Если значение типа function, то будет вызвана функция отрисовки с параметрами `[ctx, cell]` 58 | 59 | Если любое другое значение, то будет вызван fill прямоугольника соответствующим значением (паттерн, градиент, строка цвета) 60 | 61 | ```js 62 | engine.setMethod({ 63 | unknown: 'black', 64 | grass : images.get('grass'), 65 | magic : function (ctx, cell) { 66 | var color = (cell.point.x > cell.point.y) ? 'red' : 'blue'; 67 | 68 | ctx.fill( cell.rectangle, color ); 69 | } 70 | }); 71 | ``` 72 | 73 | #### countSize 74 | 75 | ```js 76 | Size countSize() 77 | ``` 78 | 79 | Посчитать размеры тайлового поля в пикселях согласно размерам поля, размерам ячейки и отступам 80 | 81 | ```js 82 | var canvas = LibCanvas.buffer(engine.countSize(), true) 83 | ``` 84 | 85 | #### refresh 86 | 87 | ```js 88 | TileEngine refresh(Context2D ctx, Point translate = null) 89 | ``` 90 | 91 | Перерисовать изменившиеся с последнего рефреша ячейки. 92 | До первой отрисовки всеячейки считаются изменившимися 93 | При необходимости можно задать смещение отрисоки 94 | 95 | ```js 96 | engine.refresh(canvas.ctx, new Point(100, 100)) 97 | ``` 98 | 99 | #### getCellByIndex 100 | 101 | ```js 102 | TileEngine.Cell getCellByIndex(Point point) 103 | ``` 104 | 105 | Вернуть ячейку по соответсвующим координатам поля 106 | 107 | ```js 108 | engine.getCellByIndex(new Point(3, 1)) 109 | ``` 110 | 111 | #### getCellByPoint 112 | 113 | ```js 114 | TileEngine.Cell getCellByPoint(Point point) 115 | ``` 116 | 117 | Вернуть ячейку по соответсвующим координатам в пикселях 118 | 119 | ```js 120 | engine.getCellByPoint(new Point(743, 351)) 121 | ``` 122 | 123 | -------------------------------------------------------------------------------- /Docs/Ru/Plugins/Animation/Animation.md: -------------------------------------------------------------------------------- 1 | Plugins.Animation 2 | ================= 3 | 4 | Класс для создания одного случая проигрывания анимации на базе прототипа `Plugins.Animation.Sheet` 5 | 6 | ### Инициализация 7 | 8 | ```js 9 | new Animation( object settings ) 10 | ``` 11 | 12 | Settings может содержать следующие параметры: 13 | 14 | * `sheet` (*Animation.Sheet*) - прототип анимации 15 | 16 | ```js 17 | animation = new Animation({ 18 | sheet : this.animationSheet, 19 | onUpdate: this.redraw, 20 | onStop : this.destroy, 21 | }); 22 | ``` 23 | 24 | ### События 25 | 26 | `update` - вызывается когда переключается кадр анимации или при запуске анимации 27 | 28 | `stop` - вызывается когда анимация завершается последним кадром или при принудительном завершении методом `stop` 29 | 30 | ### Методы 31 | 32 | #### stop 33 | 34 | ```js 35 | Animation stop(); 36 | ``` 37 | 38 | Остановить анимацию 39 | 40 | ```js 41 | animation.stop(); 42 | ``` 43 | 44 | #### run 45 | 46 | ```js 47 | Animation run(); 48 | ``` 49 | 50 | Запустить анимацию сначала. 51 | 52 | ```js 53 | animation.run(); 54 | ``` 55 | 56 | #### synchronize 57 | 58 | ```js 59 | Animation synchronize(); 60 | ``` 61 | 62 | Синхронизировать старт анимации с другой анимацией. 63 | 64 | ```js 65 | fooAnimation.synchronize( coreAnimation ); 66 | barAnimation.synchronize( coreAnimation ); 67 | 68 | coreAnimation.run(); 69 | 70 | // fooAnimation.startTime == barAnimation.startTime == coreAnimation.startTime 71 | ``` 72 | 73 | #### get 74 | 75 | ```js 76 | Animation get(); 77 | ``` 78 | 79 | Получить текущий кадр анимации или `null` если анимация остановлена, закончилась или не запущена. 80 | 81 | ```js 82 | var frame = animation.get(); 83 | ``` 84 | 85 | ### Комплексный пример на базе LibCanvas.App 86 | 87 | ```js 88 | 89 | /** @class ExplosionLauncher */ 90 | atom.declare( 'ExplosionLauncher', { 91 | initialize: function (layer, images) { 92 | this.layer = layer; 93 | 94 | this.animationSheet = new Animation.Sheet({ 95 | frames: new Animation.Frames( images.get('explosion'), 50, 50 ), 96 | delay : 40 97 | }) 98 | }, 99 | 100 | explode: function (coordinates) { 101 | new Explosion( this.layer, { 102 | sheet: this.animationSheet, 103 | shape: new Circle(coordinates, 50) 104 | }); 105 | } 106 | }); 107 | 108 | /** @class Explosion */ 109 | atom.declare( 'Explosion', App.Element, { 110 | 111 | configure: function () { 112 | this.animation = new Animation({ 113 | sheet : this.settings.get('sheet'), 114 | onUpdate: this.redraw, 115 | onStop : this.destroy, 116 | }); 117 | }, 118 | 119 | renderTo: function (ctx) { 120 | ctx.drawImage({ 121 | image : this.animation.get(), 122 | center: this.shape.center 123 | }); 124 | } 125 | 126 | }); 127 | ``` -------------------------------------------------------------------------------- /Docs/Ru/Plugins/Animation/Frames.md: -------------------------------------------------------------------------------- 1 | Plugins.Animation.Frames 2 | ======================== 3 | 4 | Класс, который используется для "нарезки" картинок из одной тайловой картинки. 5 | Обычно кадры анимации похожи между собой и имеет смысл множество их собрать в одно изображение подобно технологии css-sprites. 6 | Допустим, у вас ширина кадра анимации 50 пикселей, высота - 40, 15 кадров. Вы можете нарисовать их в три ряда в картинке размером 250*120. 7 | 8 | ![Пример нарезки кадров анимации](https://raw.github.com/theshock/libcanvas/master/Docs/Ru/Plugins/Animation/frames-demo.png) 9 | 10 | ### Инициализация 11 | 12 | ```js 13 | new Animation.Frames( Image image, int width = null, int height = null ) 14 | ``` 15 | 16 | * `image` (*Image*) - картинка-источник для нарезки 17 | * `width` (*int*) - ширина кадра. Равен ширине картинки, если `null` 18 | * `height` (*int*) - высота кадра. Равен высоте картинки, если `null` 19 | 20 | 21 | ```js 22 | var frames = new Animation.Frames( images.get('ship'), 100 ); 23 | ``` 24 | 25 | ### Методы 26 | 27 | `get length` - количество кадров в массиве 28 | 29 | ```js 30 | console.log( frames.length ); 31 | ``` 32 | 33 | 34 | #### get 35 | 36 | ```js 37 | canvasElement get( int index ); 38 | ``` 39 | 40 | Возвращает картинку-кадр, часть исходной картинки. 41 | 42 | ```js 43 | var frame = frames.get( 5 ); 44 | ``` 45 | 46 | -------------------------------------------------------------------------------- /Docs/Ru/Plugins/Animation/Image.md: -------------------------------------------------------------------------------- 1 | Plugins.Animation.Element 2 | ========================= 3 | 4 | Класс, который используется для создания картинки-анимации для вставки в dom-дерево 5 | 6 | ```js 7 | atom.dom Animation.Image.element( Animation animation ); 8 | atom.dom Animation.Image.element( Animation.Sheet sheet ); 9 | ``` 10 | 11 | ### Пример 12 | 13 | ```js 14 | function appendAnimatedLogoTo( targetElement ) { 15 | var logoSheet = new Animation.Sheet({ 16 | frames: new Animation.Frames( images.get('logo'), 50, 50 ), 17 | delay : 40, 18 | looped: true 19 | }); 20 | 21 | Animation.Image.element( logoSheet ).appendTo( targetElement ); 22 | } 23 | ``` -------------------------------------------------------------------------------- /Docs/Ru/Plugins/Animation/Sheet.md: -------------------------------------------------------------------------------- 1 | Plugins.Animation.Sheet 2 | ======================= 3 | 4 | Класс для описания анимации, её прототипа от которого будут отталкиваться все объекты. 5 | Описывает задержку между фреймами, их зацикленность и порядок воспроизведения. Является технических классом, сущности которого используются только для передачи настроек в класс анимации. 6 | 7 | ### Инициализация 8 | 9 | ```js 10 | new Animation.Sheet( object settings ) 11 | ``` 12 | 13 | Settings может содержать следующие параметры: 14 | 15 | * `frames` (*Animation.Frames*) - список исходных кадров, которые необходимо объеденить в анимацию 16 | * `delay` (*int*) - задержка между кадрами в миллисекундах 17 | * `looped` (*bool*) - зациклена ли анимация или заканчивается с посленим кадром 18 | * `sequence` (*int[]*) - порядок кадров (по-умолчанию - с первого до последнего кадра анимации) 19 | 20 | ```js 21 | new Animation.Sheet({ 22 | frames: new Animation.Frames( sourceImage ), 23 | delay : 40, 24 | looped: true, 25 | sequence: [ 0,1,1,2,1,2,2,3,4,5,4,5 ] 26 | }) 27 | ``` -------------------------------------------------------------------------------- /Docs/Ru/Plugins/Animation/frames-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theshock/libcanvas/26010cb04c6777fde6c5174e25115597de072fc7/Docs/Ru/Plugins/Animation/frames-demo.png -------------------------------------------------------------------------------- /Docs/Ru/Plugins/Curve.md: -------------------------------------------------------------------------------- 1 | Plugins.Curve 2 | ============= 3 | 4 | Математическая база для Кривых Безье. Может использоваться для создания анимаций движения по пути и для других целей. 5 | 6 | ### Инициализация 7 | 8 | ```js 9 | // Quadratic curve, one control point 10 | var curve = new LibCanvas.Plugins.Curve({ 11 | from: new Point(100, 100), 12 | to : new Point(200, 300), 13 | points: [ 14 | new Point(200, 100) 15 | ] 16 | }); 17 | 18 | // Qubic curve, two control points 19 | var curve = new LibCanvas.Plugins.Curve({ 20 | from: new Point(100, 100), 21 | to : new Point(200, 300), 22 | points: [ 23 | new Point(200, 100), 24 | new Point(100, 200), 25 | ] 26 | }); 27 | ``` 28 | 29 | * `from` (*LibCanvas.Point*) - точка начала кривой 30 | * `to` (*LibCanvas.Point*) - точка окончания кривой 31 | * `points` (*LibCanvas.Point[]*) - массив контрольных точек кривой 32 | 33 | ### Методы 34 | 35 | #### getPoint 36 | 37 | ```js 38 | LibCanvas.Point getPoint(float t) 39 | ``` 40 | 41 | `t` - число между 0 и 1. Возвращает координаты точки прямой. 42 | 43 | 44 | ```js 45 | var point = curve.getPoint( 0.45 ) 46 | ``` 47 | 48 | 49 | #### getAngle 50 | 51 | ```js 52 | float getAngle(float t) 53 | ``` 54 | 55 | `t` - число между 0 и 1. Возвращает угол кривой в соответствующем месте 56 | 57 | 58 | ```js 59 | var angle = curve.getAngle( 0.45 ) 60 | ``` 61 | -------------------------------------------------------------------------------- /Docs/Ru/Plugins/Curves.md: -------------------------------------------------------------------------------- 1 | Plugins.Curves 2 | ============== 3 | 4 | Отрисовка кривых безье с динамической шириной и цветом. Расширяет встроенный объект Context2D ('2d-libcanvas'), предоставляя удобный метод `drawCurve` 5 | 6 | ![libcanvas curves example](https://raw.github.com/theshock/libcanvas/master/Docs/Ru/Plugins/curves.png) 7 | 8 | ### Инициализация 9 | 10 | ```js 11 | Context2D drawCurve(object params) 12 | ``` 13 | 14 | * `from` (*Point*) - точка начала кривой 15 | * `to` (*Point*) - точка окончания кривой 16 | * `points` (*Point[]*) - массив контрольный точек. Может содержать 0, 1 или 2 точки 17 | * `inverted` (*Boolean*) - добавляет "ленточность" (см. скриншот выше) 18 | * `gradient` (*object*) - описывает плавное изменение цвета кривой 19 | * `from` (*string*) - начальный цвет 20 | * `to` (*string*) - окончательный цвет 21 | * `fn` (*string*) - функция изменения цвета (см. [atom.Transition](https://github.com/theshock/atomjs/blob/master/Docs/En/Declare/Transition.md)) 22 | * `width` (*object*) - описывает плавное изменение цвета кривой 23 | * `from` (*number*) - начальная ширина 24 | * `to` (*number*) - начальная ширина 25 | * `fn` (*string*) - функция изменения ширины (см. [atom.Transition](https://github.com/theshock/atomjs/blob/master/Docs/En/Declare/Transition.md)) 26 | 27 | ```js 28 | 29 | ctx.drawCurve({ 30 | from : new Point(100, 250), 31 | to : new Point(200, 100), 32 | points: [ new Point(100, 100), new Point(250, 250) ], 33 | inverted: true, 34 | gradient:{ 35 | from: '#ff0', 36 | to : '#f00', 37 | fn : 'linear' 38 | }, 39 | width:{ 40 | from: 30, 41 | to : 1, 42 | fn : 'sine-in' 43 | } 44 | }); 45 | ``` -------------------------------------------------------------------------------- /Docs/Ru/Plugins/curves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theshock/libcanvas/26010cb04c6777fde6c5174e25115597de072fc7/Docs/Ru/Plugins/curves.png -------------------------------------------------------------------------------- /Docs/Ru/Shapes/Circle.md: -------------------------------------------------------------------------------- 1 | Circle 2 | ====== 3 | `LibCanvas.Shapes.Circle` - класс, который описывает простую геометрическую фигуру "круг" 4 | 5 | #### Global 6 | 7 | После вызова LibCanvas.extract() можно использовать короткий алиас "Circle" 8 | 9 | ## Создание экземпляра LibCanvas.Shapes.Circle 10 | 11 | ```js 12 | var circle = new LibCanvas.Shapes.Circle( centerX, centerY, radius ); 13 | 14 | var circle = new LibCanvas.Shapes.Circle( centerPoint, radius ); 15 | 16 | var circle = new LibCanvas.Shapes.circle({ 17 | center : centerPoint, 18 | radius : toPoint 19 | }); 20 | ``` 21 | 22 | Не забывайте про передачу объектов по ссылке: 23 | 24 | ```js 25 | var circle = new LibCanvas.Shapes.Circle( point, 10 ); 26 | circle.center.x = 100; 27 | alert(point.x); // 100 28 | ``` 29 | 30 | Если необходимо, такого поведения можно избежать, передавая клон точки 31 | 32 | ```js 33 | var circle = new LibCanvas.Shapes.Circle( center.clone(), radius ); 34 | ``` 35 | 36 | ## Метод hasPoint 37 | 38 | ```js 39 | bool hasPoint(LibCanvas.Point point); 40 | ``` 41 | 42 | Возвращает true если точка находится внутри круга 43 | 44 | #### Пример 45 | 46 | ```js 47 | var circle = new LibCanvas.Shapes.Circle({ 48 | center : [25, 25], 49 | radius : 15 50 | }); 51 | circle.hasPoint( new Point(22, 24) ); // true 52 | circle.hasPoint( new Point(88, 88) ); // false 53 | ``` 54 | 55 | ## Метод move 56 | 57 | ```js 58 | LibCanvas.Shapes.Center move(LibCanvas.Point distance, bool reverse); 59 | ``` 60 | 61 | Вызывает метод move у центра 62 | 63 | #### Пример 64 | 65 | ```js 66 | var circle = new LibCanvas.Shapes.Circle({ 67 | center : [25, 25], 68 | radius : 15 69 | }); 70 | circle.move({ x : 2, y : 3 }); 71 | // circle.center == Point(27, 28) 72 | ``` 73 | 74 | #### Возвращает `this` 75 | 76 | ## Метод intersect 77 | 78 | ```js 79 | bool intersect(LibCanvas.Shape shape); 80 | ``` 81 | 82 | Проверяет пересечение двух фигур. Точное если `shape` является кругом и через boundingRectangle если `shape` - другая фигура. 83 | 84 | #### Пример 85 | 86 | ```js 87 | var circleA = new Circle(25, 25, 15); 88 | var circleB = new Circle(30, 30, 10); 89 | circleA.intersect( circleB ); // true 90 | ``` 91 | 92 | ## Метод scale 93 | 94 | ```js 95 | Circle scale(number power, LibCanvas.Point pivot); 96 | ``` 97 | 98 | Увеличивает круг в `power` раз относительно точки `pivot` или относительно центра круга 99 | 100 | #### Пример 101 | 102 | ```js 103 | var circle = new Circle(30, 30, 10); 104 | 105 | circle.scale( 2 ); 106 | 107 | // Увеличили круг в два раза: 108 | // 109 | // var circle = new Circle(30, 30, 20); 110 | ``` 111 | 112 | #### Возвращает `this` 113 | 114 | 115 | ## Метод draw 116 | 117 | ```js 118 | Circle draw(LibCanvas.Context2D ctx, String type); 119 | ``` 120 | 121 | Отрисовывает круг в контекст, используя текущие настройки 122 | 123 | #### аргумент `type` 124 | Способ отрисовки. Может принимать значения `fill`, `stroke`, `clear` 125 | 126 | #### Пример 127 | 128 | ```js 129 | var circle = new Circle(100, 100, 20); 130 | 131 | var ctx = canvasElem 132 | .getContext('2d-libcanvas') 133 | .set({ 134 | 'fillStyle': 'red', 135 | 'strokeStyle': 'black' 136 | }); 137 | // Зальем красным круг в контексте 138 | circle.draw(ctx, 'fill'); 139 | // Обведем черным круг в контексте 140 | circle.draw(ctx, 'stroke'); 141 | ``` 142 | 143 | Но такой способ рекомендуется использовать только если по какой либо причине не доступен следующий: 144 | 145 | ```js 146 | var ctx = canvasElem 147 | .getContext('2d-libcanvas') 148 | .fill (circle, 'red') 149 | .stroke(circle, 'black'); 150 | ``` 151 | 152 | #### Возвращает `this` 153 | 154 | ## Метод processPath 155 | 156 | ```js 157 | Circle processPath(LibCanvas.Context2D ctx, bool noWrap = false) 158 | ``` 159 | 160 | Проходит путь с помощью `ctx.arc` 161 | 162 | #### аргумент `noWrap` 163 | если указан в false(по умолчанию), то обрамляет с помощью `beginPath`, `endPath` 164 | 165 | #### Пример 166 | 167 | ```js 168 | new Circle(100, 150, 30).processPath(ctx); 169 | 170 | // равносильно: 171 | 172 | ctx 173 | .beginPath() 174 | .arc(100, 150, 30, 0, Math.PI*2, false) 175 | .closePath(); 176 | ``` 177 | 178 | ## Метод clone 179 | 180 | ```js 181 | Circle clone() 182 | ``` 183 | 184 | Возвращает новый круг с таким же радиусом и клонированным центром 185 | 186 | #### Пример 187 | 188 | ```js 189 | var circle = new Circle(100, 150, 30); 190 | 191 | var circleClone = circle.clone(); 192 | ``` 193 | 194 | ## Метод equals 195 | 196 | ```js 197 | Circle equals( Circle circle ) 198 | ``` 199 | 200 | Проверяет, равны ли два круга 201 | 202 | #### Пример 203 | 204 | ```js 205 | var circleA = new Circle(100, 150, 30); 206 | var circleB = new Circle(100, 150, 30); 207 | 208 | console.log( circleA == circleB ); // false 209 | console.log( circleA.equals(circleB) ); // true 210 | ``` -------------------------------------------------------------------------------- /Docs/Ru/Shapes/Ellipse.md: -------------------------------------------------------------------------------- 1 | LibCanvas.Shapes.Ellipse 2 | ======================== 3 | 4 | `Ellipse` - наследник фигуры `Rectangle`. Врисовывает эллипс в ректанг, описанный точками `from` и `to` 5 | 6 | ```js 7 | var ellipse = new Ellipse({ from: [10, 10], to: [100, 50] }); 8 | ``` 9 | 10 | [Пример использования »](http://libcanvas.github.com/shapes/ellipse.html) 11 | 12 | #### Global 13 | 14 | После вызова LibCanvas.extract() можно использовать короткий алиас "Ellipse" 15 | 16 | ## Свойства 17 | 18 | ### angle 19 | Угол поворота эллипса. Суть - эллипс вписан в прямоугольник, а потом развёрнут вокруг центра на свойство `angle` 20 | 21 | ## Метод rotate 22 | 23 | ```js 24 | LibCanvas.Shapes.Ellipse rotate(int degree) 25 | ``` 26 | 27 | Поворачивает эллипс на `degree` градусов вокруг центра. 28 | 29 | #### Возвращает `this` 30 | -------------------------------------------------------------------------------- /Docs/Ru/Shapes/Line.md: -------------------------------------------------------------------------------- 1 | LibCanvas.Shapes.Line 2 | ===================== 3 | 4 | #### Global 5 | 6 | После вызова LibCanvas.extract() можно использовать короткий алиас "Line" 7 | 8 | ## Создание экземпляра LibCanvas.Shapes.Line 9 | 10 | ```js 11 | // две точки LibCanvas.Point - откуда и куда 12 | var line = new LibCanvas.Shapes.Line( fromPoint, toPoint ); 13 | 14 | // объект параметров 15 | var line = new LibCanvas.Shapes.Line({ 16 | from : fromPoint, 17 | to : toPoint 18 | }); 19 | ``` 20 | 21 | Не забывайте, что точки передаются по ссылке, потому если вы объявили line через две точки то при изменении точки внутри линии будут менятся и оригинальные точки. 22 | 23 | ```js 24 | var line = new Line( fromPoint, toPoint ); 25 | line.from.x = 100; 26 | alert(fromPoint.x); // 100 27 | ``` 28 | 29 | Если необходимо, такого поведения можно избежать, передавая клоны точек 30 | 31 | ```js 32 | var line = new LibCanvas.Shapes.Line( fromPoint.clone(), toPoint.clone() ); 33 | ``` 34 | 35 | Или клонируя линию: 36 | 37 | ```js 38 | var line = new LibCanvas.Shapes.Line( fromPoint, toPoint ).clone(); 39 | ``` 40 | 41 | ## Свойства 42 | 43 | ### length (get) 44 | Получить длину линии 45 | 46 | ### center (get) 47 | Создает новую точку с координатами, которые соотвутствуют центру линии 48 | 49 | ```js 50 | var line = new Line({ 51 | from : [10, 10], 52 | to : [20, 20] 53 | }); 54 | line.center; // Point(15, 15) 55 | ``` 56 | 57 | ## Метод hasPoint 58 | 59 | ```js 60 | bool hasPoint(LibCanvas.Point point); 61 | ``` 62 | 63 | Возвращает true если точка находится на линии 64 | 65 | #### Пример 66 | 67 | ```js 68 | var line = new LibCanvas.Shapes.Line({ 69 | from : [4, 4], 70 | to : [8, 8] 71 | }); 72 | line.hasPoint( [6, 6] ); // true 73 | line.hasPoint( [2, 5] ); // false 74 | ``` 75 | 76 | ## Метод move 77 | 78 | ```js 79 | LibCanvas.Shapes.Line move(LibCanvas.Point distance, bool reverse); 80 | ``` 81 | 82 | Вызывает метод move у обоих точек 83 | 84 | #### Пример 85 | 86 | ```js 87 | var line = new LibCanvas.Shapes.Line({ 88 | from : [4, 4], 89 | to : [8, 8] 90 | }); 91 | line.move({ x : 2, y : 3 }); 92 | // line.from == Point( 6, 7) 93 | // line.to == Point(10, 11) 94 | ``` 95 | 96 | #### Возвращает `this` 97 | 98 | ## Метод processPath 99 | 100 | ```js 101 | LibCanvas.Context2D processPath(LibCanvas.Context2D ctx, bool noWrap = false) 102 | ``` 103 | 104 | Прокладывает путь с помощью с точки `from` с помощью `ctx.moveTo` в точку `to` с помощью `ctx.lineTo` 105 | 106 | #### аргумент `noWrap` 107 | если указан в false(по умолчанию), то обрамляет с помощью beginPath, endPath 108 | 109 | #### Пример 110 | 111 | ```js 112 | LibCanvas.Shapes.Line({ 113 | from : [4, 4], 114 | to : [8, 8] 115 | }).processPath(ctx); 116 | 117 | // равносильно c: 118 | ctx.beginPath() 119 | .moveTo(4, 4) // line.from 120 | .lineTo(8, 8) // line.to 121 | .closePath(); 122 | ``` 123 | 124 | #### Возвращает `this` 125 | 126 | ## Метод perpendicular 127 | 128 | ```js 129 | LibCanvas.Point perpendicular(LibCanvas.Point point) 130 | ``` 131 | 132 | Возвращает перпендикуляр точки `point` на текущую прямую 133 | 134 | #### Пример 135 | 136 | ```js 137 | var line = new LibCanvas.Shapes.Line( [0,3], [4,0] ); 138 | var point = new LibCanvas.Point( 0, 0 ); 139 | 140 | line.perpendicular( point ); // Point(1.44, 1.92) 141 | ``` 142 | 143 | ## Метод intersect 144 | 145 | ```js 146 | bool intersect(LibCanvas.Shapes.Line line) 147 | LibCanvas.Point intersect(LibCanvas.Shapes.Line line, true) 148 | ``` 149 | 150 | Определяет пересечение линии с другой линией. Если параметр `point` равен `true`, то вернётся точка пересечения или `null`, если отсутствует - вернётся `true` или `false`. 151 | 152 | ```js 153 | var first = new Line([10, 10], [20, 20]); 154 | var second = new Line([10, 20], [20, 10]); 155 | 156 | trace( first.intersect(second ) ); // true 157 | trace( first.intersect(second, true) ); // Point(15, 15) 158 | ``` 159 | 160 | ## Метод distanceTo 161 | 162 | ```js 163 | Number distanceTo(LibCanvas.Point point, boolean asInfinitiveLine) 164 | ``` 165 | 166 | Определяет расстояние между линией и точкой `point`. Если `asInfinitiveLine=true`, то линия будет считатьтся бесконечной прямой, иначе - отрезком. 167 | 168 | ```js 169 | var line = new Line (10, 10, 20, 20), 170 | point = new Point(41, 40); 171 | 172 | line.distanceTo(point ); // 29 173 | line.distanceTo(point, true); // 0.7071 174 | ``` 175 | 176 | ## Метод equals 177 | 178 | ```js 179 | bool equals(LibCanvas.Shapes.Line line, int accuracy) 180 | ``` 181 | 182 | Сравнивает точки линий методом LibCanvas.Point.equals 183 | 184 | 185 | ```js 186 | var foo = new LibCanvas.Shapes.Line(15, 20, 10, 5); 187 | var bar = new LibCanvas.Shapes.Line(15, 20, 10, 5); 188 | 189 | trace(bar == foo); // false 190 | trace(bar.equals(foo)); // true 191 | ``` 192 | 193 | ## Метод clone 194 | 195 | ```js 196 | LibCanvas.Shapes.Line clone() 197 | ``` 198 | 199 | Возвращает линию с такими же координатами 200 | 201 | ```js 202 | var line = new LibCanvas.Shapes.Line(15, 20, 10, 5); 203 | var clone = line.clone(); 204 | 205 | trace(line == clone); // false 206 | trace(line.equals(clone)); // true 207 | ``` -------------------------------------------------------------------------------- /Docs/Ru/Shapes/Path.md: -------------------------------------------------------------------------------- 1 | Path 2 | ==== 3 | 4 | `LibCanvas.Shapes.Path` - используется для создания фигур на базе кривых Безье. 5 | 6 | #### Global 7 | 8 | После вызова LibCanvas.extract() можно использовать короткий алиас "Path" 9 | 10 | ## Создание экземпляра LibCanvas.Shapes.Path 11 | 12 | ```js 13 | var polygon = new LibCanvas.Shapes.Path(); 14 | ``` 15 | 16 | #### Пример 17 | 18 | ```js 19 | var path = new Path(); 20 | ``` 21 | 22 | ## get length 23 | 24 | Возвращает количество шагов в пути: 25 | 26 | #### Пример 27 | 28 | ```js 29 | pathFrom = new Point(150, 200); 30 | 31 | path = new Path() 32 | .moveTo ( pathFrom ) 33 | .curveTo([300, 200], [250, 150]) 34 | .curveTo([200, 280], [290, 250]) 35 | .curveTo( pathFrom , [220, 220]); 36 | 37 | console.log( path.length ); // 4 38 | ``` 39 | 40 | ## Описание шагов пути 41 | 42 | ### moveTo 43 | 44 | ```js 45 | Path moveTo(LibCanvas.Point point); 46 | ``` 47 | 48 | Добавить шаг перемещения к точке `point` 49 | 50 | #### Пример 51 | 52 | ```js 53 | path.moveTo( new Point(100, 150) ); 54 | ``` 55 | 56 | ### lineTo 57 | 58 | ```js 59 | Path lineTo(LibCanvas.Point point); 60 | ``` 61 | 62 | Провести линию к точке `point` 63 | 64 | #### Пример 65 | 66 | ```js 67 | path.lineTo( new Point(100, 150) ); 68 | ``` 69 | 70 | ### curveTo 71 | 72 | ```js 73 | Path curveTo(LibCanvas.Point point, LibCanvas.Point cp1, LibCanvas.Point cp2 = null); 74 | ``` 75 | 76 | Провести кривую линию к точке `point` 77 | 78 | #### Пример 79 | 80 | ```js 81 | // Квадратическая кривая 82 | path.curveTo( new Point(100, 150), new Point(50, 50) ); 83 | // Кубическая кривая 84 | path.curveTo( new Point(100, 150), new Point(50, 50), new Point(80, 40) ); 85 | ``` 86 | 87 | ## Метод hasPoint 88 | 89 | ```js 90 | bool hasPoint(LibCanvas.Point point); 91 | ``` 92 | 93 | Возвращает true если точка находится внутри пути 94 | 95 | #### Пример 96 | 97 | ```js 98 | pathFrom = new Point(150, 200); 99 | 100 | path = new Path() 101 | .moveTo ( pathFrom ) 102 | .curveTo([300, 200], [250, 150]) 103 | .curveTo([200, 280], [290, 250]) 104 | .curveTo( pathFrom , [220, 220]); 105 | 106 | path.hasPoint( new Point(160, 200) ); // true 107 | path.hasPoint( new Point(666, 200) ); // false 108 | ``` 109 | 110 | ## Метод move 111 | 112 | ```js 113 | Path move(LibCanvas.Point distance, bool reverse); 114 | ``` 115 | 116 | Вызывает метод move у всех точек пути. Если точка повторяется несколько раз - сдвигается лишь однажды 117 | 118 | #### Пример 119 | 120 | ```js 121 | pathFrom = new Point(150, 200); 122 | 123 | path = new Path() 124 | .moveTo ( pathFrom ) 125 | .curveTo([300, 200], [250, 150]) 126 | .curveTo([200, 280], [290, 250]) 127 | .curveTo( pathFrom , [220, 220]); 128 | 129 | path.move( new Point(42, 13) ); 130 | ``` 131 | 132 | #### Возвращает `this` 133 | 134 | ## Метод rotate 135 | 136 | ```js 137 | Path rotate(number angle, LibCanvas.Point pivot); 138 | ``` 139 | Вращает путь вокруг точки `pivot` на `angle` радиан. 140 | 141 | #### Пример 142 | 143 | ```js 144 | pathFrom = new Point(150, 200); 145 | 146 | path = new Path() 147 | .moveTo ( pathFrom ) 148 | .curveTo([300, 200], [250, 150]) 149 | .curveTo([200, 280], [290, 250]) 150 | .curveTo( pathFrom , [220, 220]); 151 | 152 | // вращаем путь вокруг центра 153 | path.rotate( (6).degree(), path.center ); 154 | ``` 155 | 156 | #### Возвращает `this` 157 | 158 | ## Метод scale 159 | 160 | ```js 161 | Path scale(number power, LibCanvas.Point pivot); 162 | ``` 163 | 164 | Увеличивает путь в `power` раз относительно точки `pivot` 165 | 166 | #### Пример 167 | 168 | pathFrom = new Point(150, 200); 169 | 170 | path = new Path() 171 | .moveTo ( pathFrom ) 172 | .curveTo([300, 200], [250, 150]) 173 | .curveTo([200, 280], [290, 250]) 174 | .curveTo( pathFrom , [220, 220]); 175 | 176 | // Скукожили путь в два раза: 177 | path.scale( 0.5, path.center ); 178 | ``` 179 | 180 | #### Возвращает `this` 181 | 182 | 183 | ## Метод draw 184 | 185 | ```js 186 | Path draw(LibCanvas.Context2D ctx, String type); 187 | ``` 188 | 189 | Отрисовывает путь в контекст, используя текущие настройки 190 | 191 | #### аргумент `type` 192 | Способ отрисовки. Может принимать значения `fill`, `stroke`, `clear` 193 | 194 | #### Пример 195 | 196 | ```js 197 | pathFrom = new Point(150, 200); 198 | 199 | path = new Path() 200 | .moveTo ( pathFrom ) 201 | .curveTo([300, 200], [250, 150]) 202 | .curveTo([200, 280], [290, 250]) 203 | .curveTo( pathFrom , [220, 220]); 204 | 205 | var ctx = canvasElem 206 | .getContext('2d-libcanvas') 207 | .set({ 208 | 'fillStyle': 'red', 209 | 'strokeStyle': 'black' 210 | }); 211 | // Зальем красным многоугольник в контексте 212 | path.draw(ctx, 'fill'); 213 | // Обведем черным многоугольник в контексте 214 | path.draw(ctx, 'stroke'); 215 | ``` 216 | 217 | Но такой способ рекомендуется использовать только если по какой либо причине не доступен следующий: 218 | 219 | ```js 220 | var ctx = canvasElem 221 | .getContext('2d-libcanvas') 222 | .fill (path, 'red') 223 | .stroke(path, 'black'); 224 | ``` 225 | 226 | #### Возвращает `this` 227 | 228 | ## Метод processPath 229 | 230 | ```js 231 | Path processPath(LibCanvas.Context2D ctx, bool noWrap = false) 232 | ``` 233 | 234 | Проходит путь с помощью `ctx.moveTo`, `ctx.lineTo`, `ctx.curveTo` 235 | 236 | #### аргумент `noWrap` 237 | если указан в false(по умолчанию), то обрамляет с помощью `beginPath`, `endPath` 238 | 239 | #### Пример 240 | 241 | ```js 242 | path = new Path() 243 | .moveTo ([150, 200]) 244 | .curveTo([300, 200], [250, 150]) 245 | .curveTo([200, 280], [290, 250]) 246 | .curveTo([150, 200], [220, 220]) 247 | .processPath(ctx); 248 | 249 | // равносильно: 250 | 251 | ctx 252 | .beginPath() 253 | .moveTo ([150, 200]) 254 | .curveTo([300, 200], [250, 150]) 255 | .curveTo([200, 280], [290, 250]) 256 | .curveTo([150, 200], [220, 220]) 257 | .closePath(); 258 | ``` 259 | 260 | ## Метод clone 261 | 262 | ```js 263 | Path clone() 264 | ``` 265 | 266 | Возвращает новый путь с склонированными точками 267 | 268 | #### Пример 269 | 270 | ```js 271 | path = new Path() 272 | .moveTo ([150, 200]) 273 | .curveTo([300, 200], [250, 150]) 274 | .curveTo([200, 280], [290, 250]) 275 | .curveTo([150, 200], [220, 220]); 276 | 277 | var pathClone = path.clone(); 278 | ``` 279 | 280 | Внимание! Если в оригинальном пути несколько точек ссылались на один объект, то в новом пути, клоне, это будут разные, не связанные объекты точек. -------------------------------------------------------------------------------- /Docs/Ru/Shapes/RoundedRectangle.md: -------------------------------------------------------------------------------- 1 | LibCanvas.Shapes.RoundedRectangle 2 | ================================= 3 | 4 | `RoundedRectangle` - наследник фигуры `Rectangle`, отличается закруглёнными уголками. Полностью повторяет интерфейс родителя. 5 | 6 | #### Global 7 | 8 | После вызова LibCanvas.extract() можно использовать короткий алиас "RoundedRectangle" 9 | 10 | ## Свойства 11 | 12 | ### radius (set/get) 13 | радиус закругления уголков 14 | 15 | ## Метод setRadius 16 | 17 | ```js 18 | LibCanvas.Shapes.RoundedRectangle setRadius(int radius); 19 | ``` 20 | 21 | Устанавливает радиус фигуры. Всего-лишь удобный алиас для `shape.radius = radius`. 22 | 23 | ```js 24 | var roundedRectangle = new RoundedRectangle( 20, 20, 50, 60 ).setRadius( 5 ); 25 | ``` 26 | 27 | #### Возвращает `this` 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## LibCanvas Javascript Framework 2 | 3 | *LibCanvas* is a free javascript library, based on [AtomJS framework](https://github.com/theshock/atomjs) and available under [LGPL](http://www.gnu.org/copyleft/lgpl.html)/[MIT](http://opensource.org/licenses/mit-license.php) License. 4 | 5 | #### [Examples](http://libcanvas.github.com/) 6 | 7 | Current objectives of the project: 8 | 9 | * Full documentation 10 | * Translation to English 11 | 12 | For consultation, write to shocksilien@gmail.com 13 | 14 | ## Возможности LibCanvas 15 | 16 | LibCanvas - библиотека для создания интерактивных приложений и игр на html5. Основные возможности: 17 | 18 | * [Расширенный 2D Context](https://github.com/theshock/libcanvas/blob/master/Docs/Ru/Core/Context2D.md): 19 | * Method chaining 20 | * Фигуры в качестве аргументов 21 | * Дополнительные методы 22 | * Именованные аргументы 23 | 24 | * [Геометрия](https://github.com/theshock/libcanvas/tree/master/Docs/Ru/Shapes) 25 | * [Действия с точками](https://github.com/theshock/libcanvas/blob/master/Docs/Ru/Core/Point.md) 26 | * Изменения фигуры 27 | * Пересечения 28 | * Базовые математические операции 29 | 30 | 31 | * [Фреймворк LibCanvas.App](https://github.com/theshock/libcanvas/tree/master/Docs/Ru/App) 32 | * Отрисовка только изменившихся частей холста 33 | * События мыши 34 | * Draggable/Droppable 35 | * Слои, внутренний zIndex 36 | * Быстрое смещение слоёв 37 | 38 | 39 | * Игровые движки 40 | * [Тайловый](https://github.com/theshock/libcanvas/blob/master/Docs/Ru/Engines/Tile/) 41 | * [Изометрический](https://github.com/theshock/libcanvas/blob/master/Docs/Ru/Engines/Isometric/Projection.md) 42 | * [Гексагональный](https://github.com/theshock/libcanvas/blob/master/Docs/Ru/Engines/Hex/Projection.md) 43 | 44 | 45 | * Дополнительные возможности (плагины) 46 | * [Спрайтовые анимации](https://github.com/theshock/libcanvas/tree/master/Docs/Ru/Plugins/Animation) 47 | * [Математическая модель кривых Безье](https://github.com/theshock/libcanvas/blob/master/Docs/Ru/Plugins/Curve.md) (для построения путей) 48 | * [Кривые с динамической шириной и цветом](https://github.com/theshock/libcanvas/blob/master/Docs/Ru/Plugins/Curves.md) 49 | * Спрайтовые шрифты 50 | * Рендеринг текстуры в проекции 51 | 52 | ## Интеграция 53 | 54 | * [Gem для Ruby on Rails](https://github.com/tanraya/libcanvas-rails) 55 | 56 | ## Переход на новую версию 57 | 58 | 21 декабря 2012-ого года была публично переведена в "master" главной ветка "declare". 59 | 60 | Предыдущая версия всё ещё доступна [в ветке previous](https://github.com/theshock/libcanvas/tree/previous), но больше не разрабатывается. 61 | 62 | Основные изменения в новой версии: 63 | 64 | * Основательно переписан код, убраны основные баги архитектуры и неочевидные вещи 65 | * Повышена производительность основных компонентов библиотеки 66 | * Более не требуется расширение прототипов. Оно всё ещё поддерживается, но теперь полностью на совести пользователя - библиотека не требует расширенных при помощи atom прототипов 67 | * Используется atom.declare вместо atom.Class в целях повышения производительности и облечения дебага 68 | 69 | -------------------------------------------------------------------------------- /Source/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /Source/App/App.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App" 5 | 6 | description: "LibCanvas.App" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | 18 | provides: App 19 | 20 | ... 21 | */ 22 | 23 | /** @class App */ 24 | LibCanvas.declare( 'LibCanvas.App', 'App', { 25 | initialize: function (settings) { 26 | this.bindMethods( 'tick' ); 27 | 28 | this.layers = []; 29 | this.settings = new Settings({ appendTo: 'body' }).set(settings); 30 | this.container = new App.Container( 31 | this.settings.subset(['simple', 'size', 'appendTo']) 32 | ); 33 | this.resources = new Registry(); 34 | 35 | atom.frame.add( this.tick ); 36 | }, 37 | 38 | destroy: function () { 39 | atom.array.invoke( this.layers, 'destroy' ); 40 | atom.frame.remove( this.tick ); 41 | this.container.destroy(); 42 | }, 43 | 44 | get rectangle () { 45 | return this.container.rectangle; 46 | }, 47 | 48 | /** 49 | * return "-1" if left is higher, "+1" if right is higher & 0 is they are equals 50 | * @param {App.Element} left 51 | * @param {App.Element} right 52 | * @returns {number} 53 | */ 54 | zIndexCompare: function (left, right, inverted) { 55 | var leftZ, rightZ, factor = inverted ? -1 : +1; 56 | 57 | if (!left || !left .layer) throw new TypeError( 'Wrong left element' ); 58 | if (!right || !right.layer) throw new TypeError( 'Wrong right element' ); 59 | 60 | 61 | leftZ = left.layer.dom.zIndex; 62 | rightZ = right.layer.dom.zIndex; 63 | 64 | if (leftZ > rightZ) return -1 * factor; 65 | if (leftZ < rightZ) return +1 * factor; 66 | 67 | leftZ = left.zIndex; 68 | rightZ = right.zIndex; 69 | 70 | if (leftZ > rightZ) return -1 * factor; 71 | if (leftZ < rightZ) return +1 * factor; 72 | 73 | return 0; 74 | }, 75 | 76 | createLayer: function (settings) { 77 | if (this.settings.get('simple') && this.layers.length) { 78 | throw new Error('You can create only one layer in "Simple" mode'); 79 | } 80 | 81 | var layer = new App.Layer(this, settings); 82 | this.layers.push(layer); 83 | return layer; 84 | }, 85 | 86 | tick: function (time) { 87 | atom.array.invoke(this.layers, 'tick', time); 88 | } 89 | }); 90 | 91 | var App = LibCanvas.App; -------------------------------------------------------------------------------- /Source/App/Behavior.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Behavior" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | 18 | provides: App.Behavior 19 | 20 | ... 21 | */ 22 | 23 | /** @class App.Behavior */ 24 | var Behavior = declare( 'LibCanvas.App.Behavior', { 25 | 26 | eventName: null, 27 | 28 | initialize: function (element, callback) { 29 | this.element = element; 30 | this.events = element.events; 31 | this.eventArgs(callback); 32 | }, 33 | 34 | started: false, 35 | 36 | /** @private */ 37 | changeStatus: function (status){ 38 | if (this.started == status) { 39 | return false; 40 | } else { 41 | this.started = status; 42 | return true; 43 | } 44 | }, 45 | 46 | /** @private */ 47 | eventArgs: function (callback) { 48 | if (this.eventName && atom.core.isFunction(callback)) { 49 | this.events.add( this.eventName, callback ); 50 | } 51 | return this; 52 | }, 53 | 54 | /** @private */ 55 | getMouse: function (handler, strict) { 56 | var mouse = this.element.layer.app.resources.get( 57 | handler ? 'mouseHandler' : 'mouse' 58 | ); 59 | 60 | if (strict && !mouse) { 61 | throw new Error('No mouse in element'); 62 | } 63 | 64 | return mouse; 65 | } 66 | 67 | }); -------------------------------------------------------------------------------- /Source/App/Behaviors/Behaviors.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Behaviors" 5 | 6 | description: "DEPRECATED" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | 18 | provides: App.Behaviors 19 | 20 | ... 21 | */ 22 | 23 | /** @class App.Behaviors */ 24 | var Behaviors = declare( 'LibCanvas.App.Behaviors', { 25 | behaviors: {}, 26 | 27 | initialize: function (element) { 28 | this.element = element; 29 | this.behaviors = {}; 30 | }, 31 | 32 | /** @param [handler=false] */ 33 | getMouse: function (handler) { 34 | return this.element.layer.app.resources.get( 35 | handler ? 'mouseHandler' : 'mouse' 36 | ); 37 | }, 38 | 39 | add: function (Behaviour, args) { 40 | if (typeof Behaviour == 'string') { 41 | Behaviour = this.constructor[Behaviour]; 42 | } 43 | 44 | return this.behaviors[Behaviour.index] = new Behaviour(this, slice.call( arguments, 1 )); 45 | }, 46 | 47 | get: function (name) { 48 | return this.behaviors[name] || null; 49 | }, 50 | 51 | startAll: function (arg) { 52 | this.invoke('start', arguments); 53 | return this; 54 | }, 55 | 56 | stopAll: function () { 57 | this.invoke('stop', arguments); 58 | return this; 59 | }, 60 | 61 | /** @private */ 62 | invoke: function (method, args) { 63 | var i, b = this.behaviors; 64 | for (i in b) if (b.hasOwnProperty(i)) { 65 | b[i][method].apply(b[i], args); 66 | } 67 | return this; 68 | } 69 | 70 | }).own({ 71 | attach: function (target, types, arg) { 72 | target.behaviors = new Behaviors(target); 73 | 74 | types.forEach(function (type) { 75 | target.behaviors.add(type, arg); 76 | }); 77 | 78 | return target.behaviors; 79 | } 80 | }); 81 | 82 | 83 | declare( 'LibCanvas.App.Behaviors.Behavior', { 84 | started: false, 85 | 86 | /** @private */ 87 | eventArgs: function (args, eventName) { 88 | if (atom.core.isFunction(args[0])) { 89 | this.events.add( eventName, args[0] ); 90 | } 91 | }, 92 | 93 | /** @private */ 94 | changeStatus: function (status){ 95 | if (this.started == status) { 96 | return false; 97 | } else { 98 | this.started = status; 99 | return true; 100 | } 101 | } 102 | }); -------------------------------------------------------------------------------- /Source/App/Behaviors/Clickable.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Behaviors.Clickable" 5 | 6 | description: "DEPRECATED" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App.Behaviors 18 | 19 | provides: App.Behaviors.Clickable 20 | 21 | ... 22 | */ 23 | 24 | new function () { 25 | 26 | function setValueFn (name, val) { 27 | var result = [name, val]; 28 | return function () { 29 | if (this[name] != val) { 30 | this[name] = val; 31 | this.events.fire('statusChange', result); 32 | } 33 | }; 34 | } 35 | 36 | return declare( 'LibCanvas.App.Behaviors.Clickable', App.Behaviors.Behavior, { 37 | 38 | callbacks: { 39 | 'mouseover' : setValueFn('hover' , true ), 40 | 'mouseout' : (function () { 41 | var dehover = setValueFn('hover' , false), 42 | deactive = setValueFn('active', false); 43 | 44 | return function (e) { 45 | dehover .call(this, e); 46 | deactive.call(this, e); 47 | }; 48 | })(), 49 | 'mousedown' : setValueFn('active', true ), 50 | 'mouseup' : setValueFn('active', false) 51 | }, 52 | 53 | initialize: function (behaviors, args) { 54 | this.events = behaviors.element.events; 55 | this.eventArgs(args, 'statusChange'); 56 | }, 57 | 58 | start: function () { 59 | if (!this.changeStatus(true)) return this; 60 | 61 | this.eventArgs(arguments, 'statusChange'); 62 | this.events.add(this.callbacks); 63 | }, 64 | 65 | stop: function () { 66 | if (!this.changeStatus(false)) return this; 67 | 68 | this.events.remove(this.callbacks); 69 | } 70 | 71 | }).own({ index: 'clickable' }); 72 | 73 | }; -------------------------------------------------------------------------------- /Source/App/Behaviors/Draggable.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Behaviors.Draggable" 5 | 6 | description: "DEPRECATED" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App.Behaviors 18 | 19 | provides: App.Behaviors.Draggable 20 | 21 | ... 22 | */ 23 | 24 | declare( 'LibCanvas.App.Behaviors.Draggable', App.Behaviors.Behavior, { 25 | stopDrag: [ 'up', 'out' ], 26 | 27 | initialize: function (behaviors, args) { 28 | this.bindMethods([ 'onStop', 'onDrag', 'onStart' ]); 29 | 30 | this.behaviors = behaviors; 31 | this.element = behaviors.element; 32 | if (!atom.core.isFunction(this.element.move)) { 33 | throw new TypeError( 'Element ' + this.element + ' must has «move» method' ); 34 | } 35 | this.events = behaviors.element.events; 36 | this.eventArgs(args, 'moveDrag'); 37 | }, 38 | 39 | bindMouse: function (method) { 40 | var mouse = this.mouse, stop = this.stopDrag; 41 | 42 | mouse.events 43 | [method]( 'move', this.onDrag ) 44 | [method]( stop , this.onStop ); 45 | 46 | return mouse; 47 | }, 48 | 49 | start: function () { 50 | if (!this.changeStatus(true)) return this; 51 | 52 | this.mouse = this.behaviors.getMouse(); 53 | if (!this.mouse) throw new Error('No mouse in element'); 54 | this.eventArgs(arguments, 'moveDrag'); 55 | this.events.add( 'mousedown', this.onStart ); 56 | }, 57 | 58 | stop: function () { 59 | if (!this.changeStatus(false)) return this; 60 | 61 | this.events.remove( 'mousedown', this.onStart ); 62 | }, 63 | 64 | /** @private */ 65 | onStart: function (e) { 66 | if (e.button !== 0) return; 67 | 68 | this.bindMouse('add'); 69 | this.events.fire('startDrag', [ e ]); 70 | }, 71 | 72 | /** @private */ 73 | onDrag: function (e) { 74 | if (!this.element.layer) { 75 | return this.onStop(e, true); 76 | } 77 | 78 | var delta = this.behaviors.getMouse().delta; 79 | this.element.move( delta ); 80 | this.events.fire('moveDrag', [delta, e]); 81 | }, 82 | 83 | /** @private */ 84 | onStop: function (e, forced) { 85 | if (e.button === 0 || forced === true) { 86 | this.bindMouse('remove'); 87 | this.events.fire('stopDrag', [ e ]); 88 | } 89 | } 90 | }).own({ index: 'draggable' }); -------------------------------------------------------------------------------- /Source/App/Clickable.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Clickable" 5 | 6 | description: "Provides interface for clickable canvas objects" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App.Behavior 18 | 19 | provides: App.Clickable 20 | 21 | ... 22 | */ 23 | 24 | /** @class App.Clickable */ 25 | var Clickable = declare( 'LibCanvas.App.Clickable', App.Behavior, { 26 | 27 | eventName: 'statusChange', 28 | 29 | callbacks: { 30 | mousedown: function (e) { 31 | Clickable.setValue(this, 'active', true , e); 32 | }, 33 | mouseup : function (e) { 34 | Clickable.setValue(this, 'active', false, e); 35 | }, 36 | mouseover: function (e) { 37 | Clickable.setValue(this, 'hover' , true , e); 38 | }, 39 | mouseout : function (e) { 40 | Clickable.setValue(this, 'hover' , false, e); 41 | Clickable.setValue(this, 'active', false, e); 42 | } 43 | }, 44 | 45 | start: function (callback) { 46 | if (this.changeStatus(true)) { 47 | this.eventArgs(callback); 48 | this.events.add(this.callbacks); 49 | } 50 | return this; 51 | }, 52 | 53 | stop: function () { 54 | if (this.changeStatus(false)) { 55 | this.events.remove(this.callbacks); 56 | } 57 | return this; 58 | } 59 | 60 | }); 61 | 62 | Clickable.setValue = function (element, name, val, event) { 63 | if (element[name] != val) { 64 | element[name] = val; 65 | element.events.fire( 66 | Clickable.prototype.eventName, 67 | [name, val, event] 68 | ); 69 | } 70 | }; -------------------------------------------------------------------------------- /Source/App/Container.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Container" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App 18 | 19 | provides: App.Container 20 | 21 | ... 22 | */ 23 | 24 | /** 25 | * @class App.Container 26 | * @private */ 27 | declare( 'LibCanvas.App.Container', { 28 | /** @private 29 | * @property {Size} */ 30 | currentSize: null, 31 | 32 | /** @property {App.Dom[]} */ 33 | doms: [], 34 | 35 | wrapper: null, 36 | bounds : null, 37 | 38 | initialize: function (settings) { 39 | this.doms = []; 40 | this.settings = new Settings(settings); 41 | this.currentSize = new Size(this.settings.get('size') || [0,0]); 42 | 43 | this.isSimple = this.settings.get('simple'); 44 | 45 | if (this.isSimple) { 46 | this.createWrappersSimple(); 47 | } else { 48 | this.createWrappers(); 49 | } 50 | }, 51 | 52 | get rectangle () { 53 | var size = this.size; 54 | return new Rectangle(0, 0, size.width, size.height); 55 | }, 56 | 57 | set size(size) { 58 | if (this.isSimple) { 59 | this.doms[0].size = size; 60 | } else { 61 | size = this.currentSize.set(size).toObject(); 62 | this.wrapper.css(size); 63 | this.bounds .css(size); 64 | } 65 | }, 66 | 67 | get size() { 68 | return this.currentSize; 69 | }, 70 | 71 | destroy: function () { 72 | if (!this.isSimple) { 73 | this.wrapper.destroy(); 74 | } 75 | return this; 76 | }, 77 | 78 | createDom: function (settings) { 79 | var dom = new App.Dom( this, settings ); 80 | this.doms.push(dom); 81 | return dom; 82 | }, 83 | 84 | appendTo: function (element) { 85 | if (element) this.wrapper.appendTo( element ); 86 | return this; 87 | }, 88 | 89 | /** @private */ 90 | createWrappersSimple: function () { 91 | var size = this.currentSize.toObject(); 92 | 93 | this.wrapper = atom.dom(LibCanvas.buffer(size,true)); 94 | this.bounds = this.wrapper; 95 | 96 | this.wrapper 97 | .addClass('libcanvas-app-simple') 98 | .appendTo( this.settings.get('appendTo') ) 99 | }, 100 | 101 | /** @private */ 102 | createWrappers: function () { 103 | var size = this.currentSize.toObject(); 104 | 105 | this.wrapper = atom.dom.create('div') 106 | .css(size) 107 | .addClass('libcanvas-app') 108 | .appendTo(this.settings.get( 'appendTo' )); 109 | 110 | this.bounds = atom.dom.create('div') 111 | .css({ 112 | overflow: 'hidden', 113 | position: 'absolute' 114 | }) 115 | .css(size) 116 | .appendTo(this.wrapper); 117 | } 118 | }); -------------------------------------------------------------------------------- /Source/App/Dom.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Dom" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App 18 | 19 | provides: App.Dom 20 | 21 | ... 22 | */ 23 | 24 | 25 | /** @class App.Dom */ 26 | declare( 'LibCanvas.App.Dom', { 27 | /** @private 28 | * @property {Size} */ 29 | currentSize: null, 30 | 31 | /** @private 32 | * @property {App.Container} */ 33 | container: null, 34 | 35 | /** @private 36 | * @property {Point} */ 37 | shift: null, 38 | 39 | /** @private */ 40 | z: 0, 41 | 42 | initialize: function (container, settings) { 43 | this.container = container; 44 | this.settings = new Settings(settings); 45 | this.shift = new Point(0,0); 46 | this.name = this.settings.get('name') || ''; 47 | this.createSize(); 48 | this.createElement(); 49 | }, 50 | 51 | set zIndex (z) { 52 | this.z = z; 53 | if (!this.container.isSimple) { 54 | this.element.css('zIndex', z); 55 | } 56 | }, 57 | 58 | get zIndex () { 59 | return this.z; 60 | }, 61 | 62 | set size(size) { 63 | size = this.currentSize.set(size); 64 | 65 | this.canvas.width = size.width ; 66 | this.canvas.height = size.height; 67 | }, 68 | 69 | get size() { 70 | return this.currentSize; 71 | }, 72 | 73 | destroy: function () { 74 | this.element.destroy(); 75 | this.size = new Size(0,0); 76 | }, 77 | 78 | /** 79 | * @param {Point} shift 80 | * @returns {App.Dom} 81 | */ 82 | addShift: function ( shift ) { 83 | var newShift = this.getShift().move( shift ); 84 | this.element.css({ 85 | marginLeft: newShift.x, 86 | marginTop : newShift.y 87 | }); 88 | return this; 89 | }, 90 | 91 | /** 92 | * @param {Point} shift 93 | * @returns {App.Dom} 94 | */ 95 | setShift: function (shift) { 96 | return this.addShift( this.shift.diff(shift) ); 97 | }, 98 | 99 | /** @returns {Point} */ 100 | getShift: function () { 101 | if (this.container.isSimple) { 102 | throw new Error('Shift not available in Simple mode'); 103 | } 104 | 105 | return this.shift; 106 | }, 107 | 108 | /** @private */ 109 | createSize: function () { 110 | var size = this.settings.get('size'); 111 | 112 | if (this.container.isSimple) { 113 | this.currentSize = this.container.size; 114 | if (size) { 115 | this.currentSize.set(size); 116 | } 117 | } else { 118 | this.currentSize = size || this.container.size.clone(); 119 | } 120 | 121 | 122 | }, 123 | 124 | /** @private */ 125 | createElement: function () { 126 | if (this.container.isSimple) { 127 | this.createElementSimple(); 128 | } else { 129 | this.createElementNormal(); 130 | } 131 | }, 132 | 133 | /** @private */ 134 | createElementNormal: function () { 135 | this.canvas = new LibCanvas.Buffer(this.size, true); 136 | 137 | this.element = atom.dom(this.canvas); 138 | 139 | this.element 140 | .attr({ 'data-name': this.name }) 141 | .css ({ 'position' : 'absolute' }) 142 | .appendTo( this.container.bounds ); 143 | 144 | this.zIndex = this.settings.get('zIndex') || 0; 145 | }, 146 | 147 | /** @private */ 148 | createElementSimple: function () { 149 | this.element = this.container.wrapper; 150 | 151 | this.canvas = this.element.first; 152 | this.canvas.width = this.size.width; 153 | this.canvas.height = this.size.height; 154 | } 155 | }); -------------------------------------------------------------------------------- /Source/App/Draggable.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Draggable" 5 | 6 | description: "When object implements LibCanvas.Draggable interface dragging made possible" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App.Behavior 18 | 19 | provides: App.Draggable 20 | 21 | ... 22 | */ 23 | 24 | /** @class App.Draggable */ 25 | declare( 'LibCanvas.App.Draggable', App.Behavior, { 26 | 27 | eventName: 'moveDrag', 28 | 29 | stopDrag: [ 'up', 'out' ], 30 | 31 | initialize: function method (element, callback) { 32 | this.bindMethods([ 'onStop', 'onDrag', 'onStart' ]); 33 | 34 | method.previous.call( this, element, callback ); 35 | }, 36 | 37 | start: function (callback) { 38 | if (this.changeStatus(true)) { 39 | this.mouse = this.getMouse(false, true); 40 | this.eventArgs(callback); 41 | this.events.add( 'mousedown', this.onStart ) 42 | } 43 | return this; 44 | }, 45 | 46 | stop: function () { 47 | if (this.changeStatus(false)) { 48 | this.events.remove( 'mousedown', this.onStart ); 49 | } 50 | return this; 51 | }, 52 | 53 | /** @private */ 54 | bindMouse: function (method) { 55 | var mouse = this.mouse, stop = this.stopDrag; 56 | 57 | mouse.events 58 | [method]( 'move', this.onDrag ) 59 | [method]( stop , this.onStop ); 60 | 61 | return mouse; 62 | }, 63 | 64 | /** @private */ 65 | onStart: function (e) { 66 | if (e.button !== 0) return; 67 | 68 | this.bindMouse('add'); 69 | this.events.fire('startDrag', [ e ]); 70 | }, 71 | 72 | /** @private */ 73 | onDrag: function (e) { 74 | if (!this.element.layer) { 75 | return this.onStop(e, true); 76 | } 77 | 78 | var delta = this.mouse.delta; 79 | this.element.distanceMove( delta ); 80 | this.events.fire('moveDrag', [delta, e]); 81 | }, 82 | 83 | /** @private */ 84 | onStop: function (e, forced) { 85 | if (e.button === 0 || forced === true) { 86 | this.bindMouse('remove'); 87 | this.events.fire('stopDrag', [ e ]); 88 | } 89 | } 90 | }); -------------------------------------------------------------------------------- /Source/App/Dragger.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Dragger" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App 18 | - App.LayerShift 19 | 20 | provides: App.Dragger 21 | 22 | ... 23 | */ 24 | /** @class App.Dragger */ 25 | declare( 'LibCanvas.App.Dragger', { 26 | initialize: function (mouse) { 27 | this.bindMethods([ 'dragStart', 'dragStop', 'dragMove' ]); 28 | this.events = new Events(this); 29 | 30 | this.mouse = mouse; 31 | this.shifts = []; 32 | 33 | this._events = { 34 | down: this.dragStart, 35 | up : this.dragStop, 36 | out : this.dragStop, 37 | move: this.dragMove 38 | }; 39 | }, 40 | 41 | addLayerShift: function (shift) { 42 | this.shifts.push( shift ); 43 | return this; 44 | }, 45 | 46 | started: false, 47 | 48 | start: function (callback) { 49 | if (callback !== undefined) { 50 | this.callback = callback; 51 | } 52 | this.started = true; 53 | this.mouse.events.add( this._events ); 54 | return this; 55 | }, 56 | 57 | stop: function () { 58 | this.started = false; 59 | this.mouse.events.remove( this._events ); 60 | return this; 61 | }, 62 | 63 | /** @private */ 64 | dragStart: function (e) { 65 | if (!this.shouldStartDrag(e)) return; 66 | 67 | for (var i = this.shifts.length; i--;) { 68 | this.shifts[i].layer.stop(); 69 | } 70 | this.drag = true; 71 | this.events.fire( 'start', [ e ]); 72 | }, 73 | /** @private */ 74 | dragStop: function (e) { 75 | if (!this.drag) return; 76 | 77 | for (var i = this.shifts.length; i--;) { 78 | var shift = this.shifts[i]; 79 | shift.addElementsShift(); 80 | shift.layer.start(); 81 | } 82 | 83 | this.drag = false; 84 | this.events.fire( 'stop', [ e ]); 85 | }, 86 | /** @private */ 87 | dragMove: function (e) { 88 | if (!this.drag) return; 89 | for (var i = this.shifts.length; i--;) { 90 | this.shifts[i].addShift(this.mouse.delta); 91 | } 92 | }, 93 | /** @private */ 94 | shouldStartDrag: function (e) { 95 | if (!this.started) return false; 96 | 97 | return this.callback ? this.callback(e) : true; 98 | } 99 | }); -------------------------------------------------------------------------------- /Source/App/Element.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Element" 5 | 6 | description: "LibCanvas.Layer" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App 18 | 19 | provides: App.Element 20 | 21 | ... 22 | */ 23 | 24 | /** @class App.Element */ 25 | declare( 'LibCanvas.App.Element', { 26 | 27 | layer : null, 28 | zIndex : 0, 29 | renderer: null, 30 | settings: {}, 31 | opacity : 1, 32 | opacityThreshold: 0.01, 33 | 34 | /** @constructs */ 35 | initialize: function (layer, settings) { 36 | this.bindMethods([ 'redraw', 'destroy' ]); 37 | 38 | this.events = new Events(this); 39 | this.settings = new Settings({ hidden: false }) 40 | .set(this.settings) 41 | .set(settings) 42 | .addEvents(this.events); 43 | layer.addElement( this ); 44 | 45 | var ownShape = this.shape && this.shape != this.constructor.prototype.shape; 46 | 47 | if (ownShape || this.settings.get('shape')) { 48 | if (!ownShape) this.shape = this.settings.get('shape'); 49 | this.saveCurrentBoundingShape(); 50 | } 51 | if (this.settings.get('zIndex') != null) { 52 | this.zIndex = Number( this.settings.get('zIndex') ); 53 | } 54 | 55 | this.configure(); 56 | }, 57 | 58 | configure: function () { 59 | return this; 60 | }, 61 | 62 | previousBoundingShape: null, 63 | 64 | get currentBoundingShape () { 65 | return this.shape.getBoundingRectangle().fillToPixel(); 66 | }, 67 | 68 | redraw: function () { 69 | if (this.layer) { 70 | this.layer.redrawElement( this ); 71 | } 72 | return this; 73 | }, 74 | 75 | destroy: function () { 76 | if (this.layer) { 77 | this.layer.rmElement( this ); 78 | } 79 | return this; 80 | }, 81 | 82 | distanceMove: function (point) { 83 | this.shape.move(point); 84 | return this; 85 | }, 86 | 87 | hasPoint: function (point) { 88 | return this.shape.hasPoint( point ); 89 | }, 90 | 91 | isTriggerPoint: function (point) { 92 | if (this.hasMousePoint) { 93 | return this.hasMousePoint(point); 94 | } else { 95 | return this.hasPoint(point); 96 | } 97 | }, 98 | 99 | addShift: function (shift) { 100 | this.shape.move( shift ); 101 | if (this.previousBoundingShape) 102 | this.previousBoundingShape.move( shift ); 103 | return this; 104 | }, 105 | 106 | isVisible: function () { 107 | return !this.settings.get('hidden') && this.opacity > this.opacityThreshold; 108 | }, 109 | 110 | onUpdate: function (time) { 111 | return this; 112 | }, 113 | 114 | clearPrevious: function ( ctx ) { 115 | if (this.previousBoundingShape) ctx.clear( this.previousBoundingShape ); 116 | return this; 117 | }, 118 | 119 | saveCurrentBoundingShape: function () { 120 | var shape = this.currentBoundingShape; 121 | this.previousBoundingShape = shape.fillToPixel ? 122 | shape.clone().fillToPixel() : shape.clone().grow( 2 ); 123 | return this; 124 | }, 125 | 126 | renderToWrapper: function (ctx, resources) { 127 | if (this.opacity < this.opacityThreshold) { 128 | return; 129 | } 130 | ctx.save(); 131 | if (this.opacity + this.opacityThreshold < 1) { 132 | ctx.set({ globalAlpha: this.opacity }); 133 | } 134 | this.renderTo(ctx, resources); 135 | ctx.restore(); 136 | return this; 137 | }, 138 | 139 | renderTo: function (ctx, resources) { 140 | if (this.renderer) { 141 | this.renderer.renderTo(ctx, resources); 142 | } 143 | return this; 144 | } 145 | }); -------------------------------------------------------------------------------- /Source/App/Layer.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Layer" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App 18 | - App.Dom 19 | 20 | provides: App.Layer 21 | 22 | ... 23 | */ 24 | 25 | 26 | /** @class App.Layer */ 27 | declare( 'LibCanvas.App.Layer', { 28 | 29 | initialize: function (app, settings) { 30 | this.settings = new Settings({ 31 | invoke : app.settings.get('invoke'), 32 | intersection: 'auto' // auto|manual|all 33 | }).set(settings); 34 | 35 | this.intersection = this.settings.get('intersection'); 36 | this.redrawAllMode = this.intersection === 'all' || this.intersection === 'full'; 37 | 38 | this.app = app; 39 | this.elements = []; 40 | this.redraw = this.redrawAllMode ? this.elements : []; 41 | this.clear = []; 42 | this.createDom(); 43 | }, 44 | 45 | get ctx () { 46 | return this.dom.canvas.ctx; 47 | }, 48 | 49 | /** @private */ 50 | stopped: false, 51 | 52 | destroy: function () { 53 | atom.array.invoke( this.elements, 'destroy' ); 54 | this.dom.destroy(); 55 | }, 56 | 57 | hide: function () { 58 | this.dom.element.css({ display: 'none' }); 59 | return this.stop(); 60 | }, 61 | 62 | show: function () { 63 | this.dom.element.css({ display: '' }); 64 | return this.stop(); 65 | }, 66 | 67 | start: function () { 68 | this.stopped = false; 69 | return this; 70 | }, 71 | 72 | stop: function () { 73 | this.stopped = true; 74 | return this; 75 | }, 76 | 77 | redrawAll: function () { 78 | atom.array.invoke( this.elements, 'redraw' ); 79 | return this; 80 | }, 81 | 82 | /** @private */ 83 | tick: function (time) { 84 | if (this.stopped) return this; 85 | 86 | if (this.settings.get( 'invoke' )) { 87 | this.sortElements(); 88 | this.updateAll(time); 89 | } 90 | 91 | if (this.needUpdate) { 92 | this.draw(); 93 | this.needUpdate = false; 94 | } 95 | 96 | return this; 97 | }, 98 | 99 | 100 | /** @private */ 101 | draw: function () { 102 | var 103 | intersection = this.intersection, 104 | ctx = this.dom.canvas.ctx, 105 | resources = this.app.resources; 106 | 107 | if (intersection === 'full') { 108 | ctx.clearAll(); 109 | } else { 110 | if (intersection === 'auto') { 111 | this.addIntersections(); 112 | } else if (intersection === 'all') { 113 | atom.array.invoke(this.clear, 'clearPrevious', ctx, resources); 114 | } 115 | 116 | atom.array.invoke(this.redraw, 'clearPrevious', ctx, resources); 117 | } 118 | 119 | this.drawElements(this.redraw, ctx, resources); 120 | 121 | if (intersection === 'all') { 122 | this.clear.length = 0; 123 | } else if (intersection !== 'full') { 124 | this.redraw.length = 0; 125 | } 126 | }, 127 | 128 | /** @private */ 129 | drawElements: function (elements, ctx, resources) { 130 | // draw elements with the lower zIndex first 131 | atom.array.sortBy( elements, 'zIndex' ); 132 | 133 | for (var i = elements.length; i--;) { 134 | this.drawElement(elements[i], ctx, resources); 135 | } 136 | }, 137 | 138 | /** @private */ 139 | drawElement: function (elem, ctx, resources) { 140 | if (elem.layer == this) { 141 | elem.redrawRequested = false; 142 | if (elem.isVisible()) { 143 | elem.renderToWrapper( ctx, resources ); 144 | if (this.intersection !== 'full') { 145 | elem.saveCurrentBoundingShape(); 146 | } 147 | } 148 | } 149 | }, 150 | 151 | /** @private */ 152 | sortElements: function () { 153 | atom.array.sortBy( this.elements, 'zIndex' ); 154 | }, 155 | 156 | /** @private */ 157 | updateAll: function (time) { 158 | atom.array.invoke( this.elements, 'onUpdate', time, this.app.resources ); 159 | }, 160 | 161 | /** @private */ 162 | needUpdate: false, 163 | 164 | /** @private */ 165 | createDom: function () { 166 | this.dom = this.app.container.createDom( 167 | this.settings.subset([ 'name', 'zIndex', 'size' ]) 168 | ); 169 | }, 170 | 171 | /** @private */ 172 | addElement: function (element) { 173 | if (element.layer != this) { 174 | if (element.layer) { 175 | element.layer.rmElement( element ); 176 | } 177 | 178 | element.layer = this; 179 | this.elements.push( element ); 180 | this.redrawElement( element ); 181 | } 182 | return this; 183 | }, 184 | 185 | /** @private */ 186 | rmElement: function (element) { 187 | if (element.layer == this) { 188 | if (this.intersection === 'all') { 189 | this.needUpdate = true; 190 | this.clear.push(element); 191 | } else { 192 | this.redrawElement( element ); 193 | } 194 | atom.core.eraseOne( this.elements, element ); 195 | element.layer = null; 196 | } 197 | return this; 198 | }, 199 | 200 | /** @private */ 201 | redrawElement: function (element) { 202 | if (element.layer == this && !element.redrawRequested) { 203 | this.needUpdate = true; 204 | element.redrawRequested = true; 205 | if (!this.redrawAllMode) { 206 | this.redraw.push( element ); 207 | } 208 | } 209 | return this; 210 | }, 211 | 212 | /** @private */ 213 | addIntersections: function () { 214 | var i, elem, layer = this; 215 | 216 | for (i = 0; i < this.redraw.length; i++) { 217 | elem = this.redraw[i]; 218 | 219 | this.findIntersections(elem.previousBoundingShape, elem, this.redrawElement); 220 | this.findIntersections(elem. currentBoundingShape, elem, function (e) { 221 | // we need to redraw it, only if it will be over our element 222 | if (e.zIndex > elem.zIndex) { 223 | layer.redrawElement( e ); 224 | } 225 | }); 226 | } 227 | }, 228 | 229 | /** @private */ 230 | findIntersections: function (shape, elem, fn) { 231 | if (!shape) return; 232 | 233 | var i = this.elements.length, e; 234 | while (i--) { 235 | e = this.elements[i]; 236 | // check if we need also `e.currentBoundingShape.intersect( shape )` 237 | if (e != elem && e.isVisible() && 238 | e.previousBoundingShape && 239 | e.previousBoundingShape.intersect( shape ) 240 | ) fn.call( this, e ); 241 | } 242 | } 243 | 244 | }); 245 | -------------------------------------------------------------------------------- /Source/App/LayerShift.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.LayerShift" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App 18 | 19 | provides: App.LayerShift 20 | 21 | ... 22 | */ 23 | 24 | /** @class App.LayerShift */ 25 | declare( 'LibCanvas.App.LayerShift', { 26 | 27 | initialize: function (layer) { 28 | this.layer = layer; 29 | this.shift = new Point(0, 0); 30 | this.elementsShift = new Point(0, 0); 31 | }, 32 | 33 | /** 34 | * @private 35 | * @property {Point} 36 | */ 37 | shift: null, 38 | 39 | /** 40 | * @private 41 | * @property {Point} 42 | */ 43 | elementsShift: null, 44 | 45 | /** 46 | * @param {Point} shift 47 | */ 48 | addElementsShift: function (shift) { 49 | if (!shift) { 50 | shift = this.elementsShift.diff(this.shift); 51 | } else { 52 | shift = Point(shift); 53 | } 54 | var e = this.layer.elements, i = e.length; 55 | while (i--) e[i].addShift(shift); 56 | this.elementsShift.move(shift); 57 | return this; 58 | }, 59 | 60 | /** 61 | * @private 62 | * @property {LibCanvas.Shapes.Rectangle} 63 | */ 64 | limitShift: null, 65 | 66 | /** 67 | * @param {Rectangle} limitShift 68 | */ 69 | setLimitShift: function (limitShift) { 70 | this.limitShift = limitShift ? Rectangle(limitShift) : null; 71 | return this; 72 | }, 73 | 74 | /** 75 | * @param {Point} shift 76 | */ 77 | addShift: function ( shift, withElements ) { 78 | shift = new Point( shift ); 79 | 80 | var limit = this.limitShift, current = this.shift; 81 | if (limit) { 82 | shift.x = atom.number.limit(shift.x, limit.from.x - current.x, limit.to.x - current.x); 83 | shift.y = atom.number.limit(shift.y, limit.from.y - current.y, limit.to.y - current.y); 84 | } 85 | 86 | current.move( shift ); 87 | this.layer.dom.addShift( shift ); 88 | this.layer.dom.canvas.ctx.translate( shift, true ); 89 | if (withElements) this.addElementsShift( shift ); 90 | return this; 91 | }, 92 | 93 | /** 94 | * @param {Point} shift 95 | */ 96 | setShift: function (shift, withElements) { 97 | return this.addShift( this.shift.diff(shift), withElements ); 98 | }, 99 | 100 | /** 101 | * @returns {Point} 102 | */ 103 | getShift: function () { 104 | return this.shift; 105 | } 106 | }); -------------------------------------------------------------------------------- /Source/App/Light/Element.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Light.Element" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App 18 | - App.Light 19 | 20 | provides: App.Light.Element 21 | 22 | ... 23 | */ 24 | 25 | /** @class App.Light.Vector */ 26 | App.Light.Element = atom.declare( 'LibCanvas.App.Light.Element', App.Element, { 27 | 28 | get behaviors () { 29 | throw new Error( 'Please, use `element.clickable` & `element.draggable` instead' ); 30 | }, 31 | 32 | clickable : null, 33 | draggable : null, 34 | animatable: null, 35 | 36 | configure: function () { 37 | this.clickable = new App.Clickable(this, this.redraw); 38 | this.draggable = new App.Draggable(this, this.redraw); 39 | this.animatable = new atom.Animatable(this); 40 | this.animate = this.animatable.animate; 41 | 42 | if (this.settings.get('mouse') !== false) { 43 | this.listenMouse(); 44 | } 45 | }, 46 | 47 | /** 48 | * Override by Animatable method 49 | */ 50 | animate: function(){}, 51 | 52 | listenMouse: function (unsubscribe) { 53 | var method = unsubscribe ? 'unsubscribe' : 'subscribe'; 54 | return this.layer.app.resources.get('mouseHandler')[method](this); 55 | }, 56 | 57 | destroy: function method () { 58 | this.listenMouse(true); 59 | return method.previous.call(this); 60 | } 61 | }); -------------------------------------------------------------------------------- /Source/App/Light/Image.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Light.Image" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App.Light.Element 18 | 19 | provides: App.Light.Image 20 | 21 | ... 22 | */ 23 | 24 | /** @class App.Light.Image */ 25 | App.Light.Image = atom.declare( 'LibCanvas.App.Light.Image', App.Light.Element, { 26 | get currentBoundingShape () { 27 | return this.shape.clone().fillToPixel(); 28 | }, 29 | 30 | renderTo: function (ctx) { 31 | ctx.drawImage({ 32 | image: this.settings.get('image'), 33 | draw : this.shape 34 | }) 35 | } 36 | }); -------------------------------------------------------------------------------- /Source/App/Light/Light.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Light" 5 | 6 | description: "LibCanvas.App.Light" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App 18 | 19 | provides: App.Light 20 | 21 | ... 22 | */ 23 | 24 | /** @class App.Light */ 25 | declare( 'LibCanvas.App.Light', { 26 | 27 | initialize: function (size, settings) { 28 | var mouse, mouseHandler; 29 | 30 | this.settings = new Settings({ 31 | size : Size.from(size), 32 | name : 'main', 33 | mouse : true, 34 | invoke : false, 35 | simple : true, 36 | appendTo: 'body', 37 | intersection: 'auto' 38 | }).set(settings || {}); 39 | 40 | this.app = new App( this.settings.subset(['size', 'appendTo', 'simple']) ); 41 | this.layer = this.app.createLayer(this.settings.subset(['name','invoke','intersection'])); 42 | if (this.settings.get('mouse') === true) { 43 | mouse = new Mouse(this.app.container.bounds); 44 | mouseHandler = new App.MouseHandler({ mouse: mouse, app: this.app }); 45 | 46 | this.app.resources.set({ mouse: mouse, mouseHandler: mouseHandler }); 47 | } 48 | }, 49 | 50 | createVector: function (shape, settings) { 51 | settings = atom.core.append({ shape:shape }, settings || {}); 52 | return new App.Light.Vector(this.layer, settings); 53 | }, 54 | 55 | createText: function (shape, style, settings) { 56 | settings = atom.core.append({ shape: shape, style: style }, settings); 57 | return new App.Light.Text(this.layer, settings); 58 | }, 59 | 60 | createImage: function (shape, image, settings) { 61 | return new App.Light.Image(this.layer, atom.core.append({ 62 | shape: shape, image: image 63 | }, settings)); 64 | }, 65 | 66 | get mouse () { 67 | return this.app.resources.get( 'mouse' ); 68 | }, 69 | 70 | get mouseHandler () { 71 | return this.app.resources.get( 'mouseHandler' ); 72 | } 73 | 74 | }); -------------------------------------------------------------------------------- /Source/App/Light/Text.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Light.Text" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App 18 | - App.Light 19 | 20 | provides: App.Light.Text 21 | 22 | ... 23 | */ 24 | 25 | /** @class App.Light.Text */ 26 | atom.declare( 'LibCanvas.App.Light.Text', App.Element, { 27 | get style () { 28 | return this.settings.get('style') || {}; 29 | }, 30 | 31 | get content () { 32 | return this.style.text || ''; 33 | }, 34 | 35 | set content (c) { 36 | if (Array.isArray(c)) c = c.join('\n'); 37 | 38 | if (c != this.content) { 39 | this.redraw(); 40 | this.style.text = String(c) || ''; 41 | } 42 | }, 43 | 44 | renderTo: function (ctx) { 45 | var bg = this.settings.get('background'); 46 | 47 | if (bg) ctx.fill( this.shape, bg ); 48 | ctx.text(atom.core.append({ 49 | to : this.shape 50 | }, this.style)); 51 | } 52 | }); -------------------------------------------------------------------------------- /Source/App/Light/Vector.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.Light.Vector" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - App.Light.Element 17 | 18 | provides: App.Light.Vector 19 | 20 | ... 21 | */ 22 | 23 | /** @class App.Light.Vector */ 24 | App.Light.Vector = atom.declare( 'LibCanvas.App.Light.Vector', App.Light.Element, { 25 | active: false, 26 | hover : false, 27 | 28 | configure: function method () { 29 | method.previous.call(this); 30 | 31 | this.style = {}; 32 | this.styleActive = {}; 33 | this.styleHover = {}; 34 | }, 35 | 36 | setStyle: function (key, values) { 37 | if (typeof key == 'object') { 38 | values = key; 39 | key = ''; 40 | } 41 | key = 'style' + atom.string.ucfirst(key); 42 | 43 | atom.core.append( this[key], values ); 44 | return this.redraw(); 45 | }, 46 | 47 | getStyle: function (type) { 48 | if (!this.style) return null; 49 | 50 | var 51 | active = (this.active || null) && this.styleActive[type], 52 | hover = (this.hover || null) && this.styleHover [type], 53 | plain = this.style[type]; 54 | 55 | return active != null ? active : 56 | hover != null ? hover : 57 | plain != null ? plain : null; 58 | }, 59 | 60 | get currentBoundingShape () { 61 | var 62 | br = this.shape.getBoundingRectangle(), 63 | lw = this.getStyle('stroke') && (this.getStyle('lineWidth') || 1); 64 | 65 | return lw ? br.fillToPixel().grow(2 * Math.ceil(lw)) : br; 66 | }, 67 | 68 | renderTo: function (ctx) { 69 | var fill = this.getStyle('fill'), 70 | stroke = this.getStyle('stroke'), 71 | lineW = this.getStyle('lineWidth'), 72 | opacity = this.getStyle('opacity'); 73 | 74 | if (opacity === 0) return this; 75 | 76 | ctx.save(); 77 | if (opacity) ctx.globalAlpha = atom.number.round(opacity, 3); 78 | if (fill) ctx.fill(this.shape, fill); 79 | if (stroke) { 80 | ctx.lineWidth = lineW || 1; 81 | ctx.stroke(this.shape, stroke); 82 | } 83 | ctx.restore(); 84 | return this; 85 | } 86 | }); -------------------------------------------------------------------------------- /Source/App/MouseHandler.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.MouseHandler" 5 | 6 | description: "LibCanvas.App.MouseHandler" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Mouse 18 | - App 19 | - App.PointSearch 20 | 21 | provides: App.MouseHandler 22 | 23 | ... 24 | */ 25 | 26 | /** @class App.MouseHandler */ 27 | declare( 'LibCanvas.App.MouseHandler', { 28 | 29 | events: 'down up move out dblclick contextmenu wheel'.split(' '), 30 | 31 | /** @private */ 32 | mouse: null, 33 | 34 | /** @constructs */ 35 | initialize: function (settings) { 36 | var handler = this; 37 | 38 | handler.settings = new Settings(settings); 39 | handler.lastMouseMove = []; 40 | handler.lastMouseDown = []; 41 | handler.subscribers = []; 42 | 43 | handler.app = handler.settings.get('app'); 44 | handler.mouse = handler.settings.get('mouse'); 45 | handler.compareFunction = function (left, right) { 46 | return handler.app.zIndexCompare(left, right, true); 47 | }; 48 | handler.search = 49 | handler.settings.get('search') || 50 | new App.PointSearch(); 51 | 52 | this.events.forEach(function (type) { 53 | handler.mouse.events.add( type, function (e) { 54 | handler.event(type, e); 55 | }); 56 | }); 57 | }, 58 | 59 | stop: function () { 60 | this.stopped = true; 61 | return this; 62 | }, 63 | 64 | start: function () { 65 | this.stopped = false; 66 | return this; 67 | }, 68 | 69 | subscribe : function (elem) { 70 | if (this.subscribers.indexOf(elem) == -1) { 71 | this.subscribers.push(elem); 72 | this.search.add(elem); 73 | } 74 | return this; 75 | }, 76 | 77 | unsubscribe : function (elem) { 78 | var index = this.subscribers.indexOf(elem); 79 | if (index != -1) { 80 | this.subscribers.splice(index, 1); 81 | atom.core.eraseOne(this.lastMouseDown, elem); 82 | atom.core.eraseOne(this.lastMouseMove, elem); 83 | this.search.remove(elem); 84 | } 85 | return this; 86 | }, 87 | 88 | unsubscribeAll: function () { 89 | this.subscribers.length = 0; 90 | this.search.removeAll(); 91 | return this; 92 | }, 93 | 94 | fall: function () { 95 | this.falling = true; 96 | return this; 97 | }, 98 | 99 | getOverElements: function () { 100 | if (!this.mouse.inside) return []; 101 | 102 | var 103 | elements = this.search.findByPoint( this.mouse.point ), 104 | i = elements.length; 105 | 106 | while (i--) if (!elements[i].layer) { 107 | this.unsubscribe(elements[i]); 108 | elements.splice(i, 1); 109 | } 110 | 111 | return elements.sort( this.compareFunction ); 112 | }, 113 | 114 | /** @private */ 115 | stopped: false, 116 | 117 | /** @private */ 118 | falling: false, 119 | 120 | /** @private */ 121 | checkFalling: function () { 122 | var value = this.falling; 123 | this.falling = false; 124 | return value; 125 | }, 126 | 127 | /** @private */ 128 | event: function (type, e) { 129 | if (this.stopped) return; 130 | 131 | var method = ['dblclick', 'contextmenu', 'wheel'].indexOf( type ) >= 0 132 | ? 'forceEvent' : 'parseEvent'; 133 | 134 | return this[method]( type, e ); 135 | }, 136 | 137 | /** @private */ 138 | parseEvent: function (type, event) { 139 | if (type == 'down') this.lastMouseDown.length = 0; 140 | 141 | var i, elem, 142 | elements = this.getOverElements(), 143 | stopped = false, 144 | eventArgs = [event], 145 | isChangeCoordEvent = (type == 'move' || type == 'out'); 146 | 147 | // В первую очередь - обрабатываем реальный mouseout с элементов 148 | if (isChangeCoordEvent) { 149 | this.informOut(eventArgs, elements); 150 | } 151 | 152 | for (i = elements.length; i--;) { 153 | elem = elements[i]; 154 | // мышь над элементом, сообщаем о mousemove 155 | // о mouseover, mousedown, click, если необходимо 156 | if (!stopped) { 157 | if (this.fireElem( type, elem, eventArgs )) { 158 | stopped = true; 159 | if (!isChangeCoordEvent) break; 160 | } 161 | // предыдущий элемент принял событие на себя 162 | // необходимо сообщить остальным элементам под ним о mouseout 163 | // Но только если это событие передвижения или выхода за границы холста 164 | // а не активационные, как маусдаун или маусап 165 | } else { 166 | this.stoppedElem(elem, eventArgs); 167 | } 168 | } 169 | 170 | return stopped; 171 | }, 172 | 173 | /** @private */ 174 | informOut: function (eventArgs, elements) { 175 | var 176 | elem, 177 | lastMove = this.lastMouseMove, 178 | i = lastMove.length; 179 | while (i--) { 180 | elem = lastMove[i]; 181 | if (elements.indexOf(elem) < 0) { 182 | elem.events.fire( 'mouseout', eventArgs ); 183 | lastMove.splice(i, 1); 184 | } 185 | } 186 | }, 187 | 188 | /** @private */ 189 | stoppedElem: function (elem, eventArgs) { 190 | var 191 | lastMove = this.lastMouseMove, 192 | index = lastMove.indexOf(elem); 193 | if (index > -1) { 194 | elem.events.fire( 'mouseout', eventArgs ); 195 | lastMove.splice(index, 1); 196 | } 197 | }, 198 | 199 | /** @private */ 200 | fireElem: function (type, elem, eventArgs) { 201 | var 202 | lastDown = this.lastMouseDown, 203 | lastMove = this.lastMouseMove; 204 | 205 | if (type == 'move') { 206 | if (lastMove.indexOf(elem) < 0) { 207 | elem.events.fire( 'mouseover', eventArgs ); 208 | lastMove.push( elem ); 209 | } 210 | } else if (type == 'down') { 211 | lastDown.push(elem); 212 | // If mouseup on this elem and last mousedown was on this elem - click 213 | } else if (type == 'up' && lastDown.indexOf(elem) > -1) { 214 | elem.events.fire( 'click', eventArgs ); 215 | } 216 | elem.events.fire( 'mouse' + type, eventArgs ); 217 | 218 | return !this.checkFalling(); 219 | }, 220 | 221 | /** @private */ 222 | forceEvent: function (type, event) { 223 | var 224 | elements = this.getOverElements(), 225 | i = elements.length; 226 | while (i--) { 227 | elements[i].events.fire( type, [ event ]); 228 | if (!this.checkFalling()) { 229 | break; 230 | } 231 | } 232 | } 233 | 234 | }); -------------------------------------------------------------------------------- /Source/App/PointSearch.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "App.PointSearch" 5 | 6 | description: "LibCanvas.App.ElementsMouseSearch" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - App 18 | 19 | provides: App.PointSearch 20 | 21 | ... 22 | */ 23 | 24 | /** @class App.PointSearch */ 25 | declare( 'LibCanvas.App.PointSearch', { 26 | 27 | initialize: function () { 28 | this.elements = []; 29 | }, 30 | 31 | add: function (elem) { 32 | this.elements.push( elem ); 33 | return this; 34 | }, 35 | 36 | remove: function (elem) { 37 | atom.core.eraseOne( this.elements, elem ); 38 | return this; 39 | }, 40 | 41 | removeAll: function () { 42 | this.elements.length = 0; 43 | return this; 44 | }, 45 | 46 | findByPoint: function (point) { 47 | var e = this.elements, i = e.length, result = []; 48 | while (i--) if (e[i].isTriggerPoint( point )) { 49 | result.push(e[i]); 50 | } 51 | return result; 52 | } 53 | 54 | }); 55 | 56 | /** @deprecated */ 57 | App.ElementsMouseSearch = App.PointSearch; -------------------------------------------------------------------------------- /Source/Context/DrawImage.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Context.DrawImage" 5 | 6 | description: "LibCanvas.Context2D adds new canvas context '2d-libcanvas'" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Point 18 | - Size 19 | - Shapes.Rectangle 20 | - Shapes.Circle 21 | 22 | provides: Context.DrawImage 23 | 24 | ... 25 | */ 26 | 27 | new function () { 28 | 29 | var toPoint = Point.from, toRectangle = Rectangle.from; 30 | 31 | /** @class LibCanvas.Context.DrawImage */ 32 | LibCanvas.declare( 'LibCanvas.Context.DrawImage', { 33 | initialize: function (context) { 34 | this.context = context; 35 | this.ctx2d = context.ctx2d; 36 | }, 37 | 38 | drawImage: function (args) { 39 | var a, center, from, draw, crop, scale, image, pivot, angle; 40 | 41 | if (this.checkNonObject(args)) return; 42 | 43 | a = args[0]; 44 | 45 | image = a.image; 46 | angle = a.angle; 47 | scale = a.scale && toPoint(a.scale); 48 | center = a.center && toPoint(a.center); 49 | from = a.from && toPoint(a.from); 50 | draw = a.draw && toRectangle(a.draw); 51 | crop = a.crop && toRectangle(a.crop); 52 | 53 | if (! atom.dom.isElement(image) ) throw new TypeError('Wrong image in Context.DrawImage'); 54 | if (! (center || from || draw) ) throw new TypeError('Wrong arguments in Context.DrawImage'); 55 | 56 | pivot = this.getTransformPivot( 57 | angle, scale, image, 58 | center, from, draw 59 | ); 60 | 61 | if (pivot) this.transform(pivot, angle, scale); 62 | draw ? 63 | this.drawRect (image, draw, crop , a.optimize) : 64 | this.drawPoint(image, from, center, a.optimize); 65 | if (pivot) this.ctx2d.restore(); 66 | 67 | return this.context; 68 | }, 69 | 70 | /** @private */ 71 | run: function (array) { 72 | this.ctx2d.drawImage.apply( this.ctx2d, array ); 73 | }, 74 | /** @private */ 75 | transform: function (center, angle, scale) { 76 | this.ctx2d.save(); 77 | if (angle) this.context.rotate(angle, center); 78 | if (scale) this.context.scale (scale, center); 79 | }, 80 | /** @private */ 81 | checkNonObject: function (args) { 82 | var image = args[0], length = args.length, target; 83 | if (length > 2) { 84 | this.run(args); 85 | return true; 86 | } 87 | if (length == 2) { 88 | target = args[1]; 89 | 90 | if (target instanceof Point) { 91 | this.drawPoint(image, target); 92 | return true; 93 | } 94 | if (target instanceof Rectangle) { 95 | this.drawRect(image, target); 96 | return true; 97 | } 98 | 99 | throw new Error('Unknown second argument in Context.DrawImage'); 100 | } 101 | 102 | if (length == 0) { 103 | throw new Error('Empty arguments in Context.DrawImage'); 104 | } 105 | 106 | if (atom.dom.isElement(image)) { 107 | this.ctx2d.drawImage(image, 0, 0); 108 | return true; 109 | } 110 | 111 | return false; 112 | }, 113 | /** @private */ 114 | drawPoint: function (image, from, center, optimize) { 115 | var 116 | point = center || from, 117 | fromX = point.x, 118 | fromY = point.y; 119 | 120 | if (center) { 121 | fromX -= image.width / 2; 122 | fromY -= image.height / 2; 123 | } 124 | 125 | if (optimize) { 126 | fromX = Math.round(fromX); 127 | fromY = Math.round(fromY); 128 | } 129 | 130 | this.ctx2d.drawImage(image, fromX, fromY); 131 | }, 132 | /** @private */ 133 | drawRect: function (image, rect, crop, optimize) { 134 | var deltaX, deltaY, fromX, fromY; 135 | 136 | if (crop) { 137 | this.ctx2d.drawImage( image, 138 | crop.from.x, crop.from.y, crop.width, crop.height, 139 | rect.from.x, rect.from.y, rect.width, rect.height 140 | ); 141 | return; 142 | } 143 | 144 | if (optimize) { 145 | fromX = Math.round(rect.from.x); 146 | fromY = Math.round(rect.from.y); 147 | deltaX = Math.abs(rect.width - image.width ); 148 | deltaY = Math.abs(rect.height - image.width ); 149 | 150 | if (deltaX < 1.1 && deltaY < 1.1) { 151 | this.ctx2d.drawImage(image, fromX, fromY); 152 | } else { 153 | this.ctx2d.drawImage(image, fromX, fromY, 154 | Math.round(rect.width), 155 | Math.round(rect.height) 156 | ); 157 | } 158 | return; 159 | } 160 | 161 | this.ctx2d.drawImage( image, 162 | rect.from.x, rect.from.y, 163 | rect.width , rect.height 164 | ); 165 | }, 166 | /** @private */ 167 | getTransformPivot: function (angle, scale, image, center, from, draw) { 168 | if ( !angle && (!scale || (scale.x == 1 && scale.y == 1)) ) return null; 169 | 170 | if (center) return center; 171 | if ( draw ) return draw.center; 172 | 173 | return new Point(from.x + image.width/2, from.y + image.height/2); 174 | } 175 | }); 176 | 177 | }; 178 | -------------------------------------------------------------------------------- /Source/Context/Gradients.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Context.Gradients" 5 | 6 | description: "LibCanvas.Context2D adds new canvas context '2d-libcanvas'" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Point 18 | - Size 19 | - Shapes.Rectangle 20 | - Shapes.Circle 21 | 22 | provides: Context.Gradients 23 | 24 | ... 25 | */ 26 | 27 | new function () { 28 | 29 | var toPoint = Point.from, toRectangle = Rectangle.from, toCircle = Circle.from; 30 | 31 | 32 | var addColorStopSource = document 33 | .createElement('canvas') 34 | .getContext('2d') 35 | .createLinearGradient(0,0,1,1) 36 | .addColorStop; 37 | 38 | var addColorStop = function (colors) { 39 | if (typeof colors == 'object') { 40 | for (var position in colors) if (colors.hasOwnProperty(position)) { 41 | addColorStopSource.call( this, parseFloat(position), colors[position] ); 42 | } 43 | } else { 44 | addColorStopSource.apply( this, arguments ); 45 | } 46 | return this; 47 | }; 48 | 49 | 50 | var fixGradient = function (grad) { 51 | grad.addColorStop = addColorStop; 52 | return grad; 53 | }; 54 | 55 | /** @class LibCanvas.Context.Gradients */ 56 | LibCanvas.declare( 'LibCanvas.Context.Gradients', { 57 | initialize: function (context) { 58 | this.context = context; 59 | this.ctx2d = context.ctx2d; 60 | }, 61 | 62 | /** @returns {CanvasGradient} */ 63 | createGradient: function (from, to, colors) { 64 | var gradient; 65 | if ( from instanceof Rectangle ) { 66 | colors = to; 67 | gradient = this.createLinearGradient([ from ]); 68 | } else if (from instanceof Circle) { 69 | gradient = this.createRadialGradient([ from, to ]); 70 | } else if (from instanceof Point) { 71 | gradient = this.createLinearGradient([ from, to ]); 72 | } else { 73 | throw new Error('Unknown arguments in Context.Gradients.createGradient'); 74 | } 75 | if (typeof colors == 'object') gradient.addColorStop( colors ); 76 | return gradient; 77 | }, 78 | /** @returns {CanvasGradient} */ 79 | createRectangleGradient: function (rectangle, colors) { 80 | rectangle = toRectangle( rectangle ); 81 | 82 | var from = rectangle.from, line = new Line( rectangle.bottomLeft, rectangle.topRight ); 83 | 84 | return this.createGradient( from, line.perpendicular(from).scale(2, from), colors ); 85 | }, 86 | /** @returns {CanvasGradient} */ 87 | createLinearGradient : function (a) { 88 | var from, to; 89 | if (a.length != 4) { 90 | if (a.length == 2) { 91 | to = toPoint(a[0]); 92 | from = toPoint(a[1]); 93 | } else if (a.length == 1) { 94 | to = toPoint(a[0].to); 95 | from = toPoint(a[0].from); 96 | } else { 97 | throw new TypeError('Wrong arguments.length in the Context.createLinearGradient'); 98 | } 99 | a = [from.x, from.y, to.x, to.y]; 100 | } 101 | return fixGradient( this.ctx2d.createLinearGradient.apply(this.ctx2d, a) ); 102 | }, 103 | /** @returns {CanvasGradient} */ 104 | createRadialGradient: function (a) { 105 | var points, c1, c2, length = a.length; 106 | if (length == 1 || length == 2) { 107 | if (length == 2) { 108 | c1 = toCircle( a[0] ); 109 | c2 = toCircle( a[1] ); 110 | } else { 111 | c1 = toCircle( a.start ); 112 | c2 = toCircle( a.end ); 113 | } 114 | points = [c1.center.x, c1.center.y, c1.radius, c2.center.x, c2.center.y, c2.radius]; 115 | } else if (length == 6) { 116 | points = a; 117 | } else { 118 | throw new TypeError('Wrong arguments.length in the Context.createRadialGradient'); 119 | } 120 | 121 | return fixGradient( this.ctx2d.createRadialGradient.apply(this.ctx2d, points) ); 122 | } 123 | }); 124 | 125 | }; 126 | -------------------------------------------------------------------------------- /Source/Context/Path.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Context.Path" 5 | 6 | description: "LibCanvas.Context2D adds new canvas context '2d-libcanvas'" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Point 18 | - Size 19 | - Shapes.Circle 20 | 21 | provides: Context.Path 22 | 23 | ... 24 | */ 25 | 26 | new function () { 27 | 28 | var toPoint = Point.from, toCircle = Circle.from; 29 | 30 | 31 | /** @class LibCanvas.Context.Path */ 32 | LibCanvas.declare( 'LibCanvas.Context.Path', { 33 | initialize: function (context) { 34 | this.context = context; 35 | this.ctx2d = context.ctx2d; 36 | }, 37 | 38 | 39 | /** @returns {Context2D} */ 40 | arc : function (a) { 41 | 42 | if (a.length > 1) { 43 | return this.ctx2d.arc.apply(this.ctx2d, a); 44 | } else if (!a[0].circle) { 45 | throw new TypeError('Wrong arguments in CanvasContext.arc'); 46 | } 47 | 48 | var f = a[0], circle, angle, angleStart, angleEnd, angleSize; 49 | circle = toCircle(f.circle); 50 | angle = f.angle; 51 | 52 | if (Array.isArray(angle)) { 53 | angleStart = angle[0]; 54 | angleEnd = angle[1]; 55 | } else { 56 | angleStart = angle.start; 57 | angleEnd = angle.end; 58 | angleSize = angle.size; 59 | 60 | if (angleSize == null) { 61 | // do nothing 62 | } else if (angleEnd == null) { 63 | angleEnd = angleSize + angleStart; 64 | } else if (angleStart == null) { 65 | angleStart = angleEnd - angleSize; 66 | } 67 | } 68 | this.ctx2d.arc( 69 | circle.center.x, circle.center.y, circle.radius, 70 | angleStart, angleEnd, !!(f.anticlockwise || f.acw) 71 | ); 72 | return this.context; 73 | }, 74 | 75 | /** @returns {Context2D} */ 76 | arcTo : function () { 77 | // @todo Beauty arguments 78 | this.ctx2d.arcTo.apply(this.ctx2d, arguments); 79 | return this.context; 80 | }, 81 | /** @returns {Context2D} */ 82 | curveTo: function (a) { 83 | var p, l, to, curve = a[0]; 84 | 85 | if (typeof curve == 'number') { 86 | if (a.length === 4) { 87 | return this.quadraticCurveTo(a); 88 | } else if (a.length === 6) { 89 | return this.bezierCurveTo(a); 90 | } 91 | } else if (a.length > 1) { 92 | p = atom.array.from( a ).map(toPoint); 93 | to = p.shift() 94 | } else { 95 | p = atom.array.from( curve.points ).map(toPoint); 96 | to = toPoint(curve.to); 97 | } 98 | 99 | l = p.length; 100 | 101 | if (l == 2) { 102 | this.ctx2d.bezierCurveTo( 103 | p[0].x, p[0].y, p[1].x, p[1].y, to.x, to.y 104 | ); 105 | } else if (l == 1) { 106 | this.ctx2d.quadraticCurveTo( 107 | p[0].x, p[0].y, to.x, to.y 108 | ); 109 | } else { 110 | this.ctx2d.lineTo(to.x, to.y); 111 | } 112 | return this.context; 113 | }, 114 | /** @returns {Context2D} */ 115 | quadraticCurveTo : function (a) { 116 | if (a.length == 4) { 117 | this.ctx2d.quadraticCurveTo.apply(this.ctx2d, a); 118 | return this.context; 119 | } else { 120 | a = a.length == 2 ? {p:a[0], to:a[1]} : a[0]; 121 | return this.curveTo([{ 122 | to: a.to, 123 | points: [a.p] 124 | }]); 125 | } 126 | }, 127 | /** @returns {Context2D} */ 128 | bezierCurveTo : function (a) { 129 | if (a.length == 6) { 130 | this.ctx2d.bezierCurveTo.apply(this.ctx2d, a); 131 | return this.context; 132 | } else { 133 | a = a.length == 3 ? {p1:a[0], p2:a[1], to:a[2]} : a[0]; 134 | return this.curveTo([{ 135 | to: a.to, 136 | points: [a.p1, a.p2] 137 | }]); 138 | } 139 | }, 140 | isPointInPath: function (x, y) { 141 | if (y == null) { 142 | x = toPoint(x); 143 | y = x.y; 144 | x = x.x; 145 | } 146 | return this.ctx2d.isPointInPath(x, y); 147 | } 148 | 149 | }); 150 | 151 | }; 152 | -------------------------------------------------------------------------------- /Source/Context/Pixels.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Context.Pixels" 5 | 6 | description: "LibCanvas.Context2D adds new canvas context '2d-libcanvas'" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Point 18 | - Size 19 | - Shapes.Rectangle 20 | - Shapes.Circle 21 | 22 | provides: Context.Pixels 23 | 24 | ... 25 | */ 26 | 27 | new function () { 28 | 29 | var toPoint = Point.from, toRectangle = Rectangle.from, size1x1 = new Size(1,1); 30 | 31 | 32 | /** @class LibCanvas.Context.Pixels */ 33 | LibCanvas.declare( 'LibCanvas.Context.Pixels', { 34 | initialize: function (context) { 35 | this.context = context; 36 | this.ctx2d = context.ctx2d; 37 | }, 38 | 39 | // image data 40 | /** @returns {CanvasPixelArray} */ 41 | createImageData : function (w, h) { 42 | if (w == null) { 43 | w = this.context.width; 44 | h = this.context.height; 45 | } else if (h == null) { 46 | if (w.width == null && w.height == null) { 47 | throw new TypeError('Wrong argument in the Context.createImageData'); 48 | } else { 49 | h = w.height; 50 | w = w.width; 51 | } 52 | } 53 | 54 | return this.ctx2d.createImageData(w, h); 55 | }, 56 | 57 | /** @returns {Context2D} */ 58 | putImageData : function (a) { 59 | var put = {}, args, rect; 60 | 61 | switch (a.length) { 62 | case 1: { 63 | if (typeof a != 'object') { 64 | throw new TypeError('Wrong argument in the Context.putImageData'); 65 | } 66 | 67 | a = a[0]; 68 | put.image = a.image; 69 | put.from = toPoint(a.from); 70 | 71 | if (a.crop) put.crop = toRectangle(a.crop); 72 | } break; 73 | 74 | case 3: { 75 | put.image = a[0]; 76 | put.from = new Point(a[1], a[2]); 77 | } break; 78 | 79 | case 7: { 80 | put.image = a[0]; 81 | put.from = new Point(a[1], a[2]); 82 | put.crop = new Rectangle(a[3], a[4], a[5], a[6]); 83 | } break; 84 | 85 | default : throw new TypeError('Wrong args number in the Context.putImageData'); 86 | } 87 | 88 | args = [put.image, put.from.x, put.from.y]; 89 | 90 | if (put.crop) { 91 | rect = put.crop; 92 | atom.array.append(args, [rect.from.x, rect.from.y, rect.width, rect.height]) 93 | } 94 | 95 | this.ctx2d.putImageData.apply(this.ctx2d, args); 96 | return this.context; 97 | }, 98 | /** @returns {CanvasPixelArray} */ 99 | getImageData : function (args) { 100 | var rect = toRectangle(args.length > 1 ? args : args[0]); 101 | 102 | return this.ctx2d.getImageData(rect.from.x, rect.from.y, rect.width, rect.height); 103 | }, 104 | getPixels : function (args) { 105 | var 106 | rect = toRectangle(args.length > 1 ? args : args[0]), 107 | data = this.getImageData(rect).data, 108 | result = [], 109 | line = []; 110 | for (var i = 0, L = data.length; i < L; i+=4) { 111 | line.push({ 112 | r : data[i], 113 | g : data[i+1], 114 | b : data[i+2], 115 | a : data[i+3] / 255 116 | }); 117 | if (line.length == rect.width) { 118 | result.push(line); 119 | line = []; 120 | } 121 | } 122 | return result; 123 | }, 124 | 125 | getPixel: function (point) { 126 | var 127 | rect = new Rectangle(toPoint( point ), size1x1), 128 | data = slice.call(this.getImageData([rect]).data); 129 | 130 | data[3] /= 255; 131 | 132 | return new atom.Color(data); 133 | } 134 | 135 | }); 136 | 137 | }; 138 | -------------------------------------------------------------------------------- /Source/Context/Text.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Context.Text" 5 | 6 | description: "LibCanvas.Context2D adds new canvas context '2d-libcanvas'" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Point 18 | - Size 19 | - Shapes.Rectangle 20 | - Shapes.Circle 21 | 22 | provides: Context.Text 23 | 24 | ... 25 | */ 26 | 27 | new function () { 28 | 29 | var toPoint = Point.from, toRectangle = Rectangle.from, size1x1 = new Size(1,1); 30 | 31 | 32 | /** @class LibCanvas.Context.Text */ 33 | LibCanvas.declare( 'LibCanvas.Context.Text', { 34 | initialize: function (context) { 35 | this.context = context; 36 | this.ctx2d = context.ctx2d; 37 | }, 38 | // text 39 | /** @returns {Context2D} */ 40 | method: function (method, text, x, y, maxWidth) { 41 | var type = typeof x; 42 | if (type != 'number' && type != 'string') { 43 | maxWidth = y; 44 | x = toPoint( x ); 45 | y = x.y; 46 | x = x.x; 47 | } 48 | var args = [text, x, y]; 49 | if (maxWidth) args.push( maxWidth ); 50 | this.ctx2d[method].apply( this.ctx2d, args ); 51 | return this.context; 52 | }, 53 | fillText : function (text, x, y, maxWidth) { 54 | return this.method( 'fillText', text, x, y, maxWidth); 55 | }, 56 | strokeText : function (text, x, y, maxWidth) { 57 | return this.method('strokeText', text, x, y, maxWidth); 58 | }, 59 | /** @returns {object} */ 60 | measureText : function (args) { 61 | return this.ctx2d.measureText.apply(this.ctx2d, args) 62 | }, 63 | /** @returns {Context2D} */ 64 | text : function (cfg) { 65 | if (!this.ctx2d.fillText) return this; 66 | 67 | var ctx = this.ctx2d; 68 | 69 | cfg = atom.core.append({ 70 | text : '', 71 | color : null, /* @color */ 72 | wrap : 'normal', /* no|normal */ 73 | to : null, 74 | align : 'left', /* center|left|right */ 75 | size : 16, 76 | weight : 'normal', /* bold|normal */ 77 | style : 'normal', /* italic|normal */ 78 | family : 'arial,sans-serif', /* @fontFamily */ 79 | lineHeight : null, 80 | overflow : 'visible', /* hidden|visible */ 81 | padding : [0,0], 82 | shadow : null, 83 | stroke : null, 84 | lineWidth : null 85 | }, cfg); 86 | 87 | ctx.save(); 88 | if (typeof cfg.padding == 'number') { 89 | cfg.padding = [cfg.padding, cfg.padding]; 90 | } 91 | var method = cfg.stroke ? 'strokeText' : 'fillText'; 92 | var to = cfg.to ? toRectangle(cfg.to) : this.context.rectangle; 93 | var lh = Math.round(cfg.lineHeight || (cfg.size * 1.15)); 94 | this.context.set('font', atom.string.substitute( 95 | '{style}{weight}{size}px {family}', { 96 | style : cfg.style == 'italic' ? 'italic ' : '', 97 | weight : cfg.weight == 'bold' ? 'bold ' : '', 98 | size : cfg.size, 99 | family : cfg.family 100 | }) 101 | ); 102 | 103 | if (cfg.color) { 104 | this.context.set(cfg.stroke ? 'strokeStyle' : 'fillStyle', cfg.color); 105 | } 106 | 107 | if (cfg.shadow) this.context.shadow = cfg.shadow; 108 | if (cfg.overflow == 'hidden') this.context.clip(to); 109 | if (cfg.lineWidth) this.context.set({ lineWidth: cfg.lineWidth }); 110 | 111 | function xGet (lineWidth) { 112 | var al = cfg.align, pad = cfg.padding[1]; 113 | return Math.round( 114 | al == 'left' ? to.from.x + pad : 115 | al == 'right' ? to.to.x - lineWidth - pad : 116 | to.from.x + (to.width - lineWidth)/2 117 | ); 118 | } 119 | function measure (text) { 120 | return Number(ctx.measureText(text).width); 121 | } 122 | var lines = String(cfg.text).split('\n'); 123 | 124 | if (cfg.wrap == 'no') { 125 | lines.forEach(function (line, i) { 126 | if (!line) return; 127 | 128 | ctx[method](line, xGet(cfg.align == 'left' ? 0 : measure(line)), to.from.y + (i+1)*lh); 129 | }); 130 | } else { 131 | var lNum = 0; 132 | lines.forEach(function (line) { 133 | if (!line) { 134 | lNum++; 135 | return; 136 | } 137 | 138 | var words = (line || ' ').match(/.+?(\s|$)/g); 139 | if (!words) { 140 | lNum++; 141 | return; 142 | } 143 | var L = ''; 144 | var Lw = 0; 145 | for (var i = 0; i <= words.length; i++) { 146 | var last = i == words.length; 147 | if (!last) { 148 | var text = words[i]; 149 | // @todo too slow. 2-4ms for 50words 150 | var wordWidth = measure(text); 151 | if (!Lw || Lw + wordWidth < to.width) { 152 | Lw += wordWidth; 153 | L += text; 154 | continue; 155 | } 156 | } 157 | if (Lw) { 158 | ctx[method](L, xGet(Lw), to.from.y + (++lNum)*lh + cfg.padding[0]); 159 | if (last) { 160 | L = ''; 161 | Lw = 0; 162 | } else { 163 | L = text; 164 | Lw = wordWidth; 165 | } 166 | } 167 | } 168 | if (Lw) { 169 | ctx[method](L, xGet(Lw), to.from.y + (++lNum)*lh + cfg.padding[0]); 170 | } 171 | }); 172 | 173 | } 174 | ctx.restore(); 175 | return this.context; 176 | } 177 | 178 | }); 179 | 180 | }; 181 | -------------------------------------------------------------------------------- /Source/Core/Canvas.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Core.Canvas" 5 | 6 | description: "Provides some Canvas extensions" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | 18 | provides: Core.Canvas 19 | 20 | ... 21 | */ 22 | 23 | atom.core.append(HTMLCanvasElement, 24 | /** @lends HTMLCanvasElement */ 25 | { 26 | /** @private */ 27 | _newContexts: {}, 28 | /** @returns {HTMLCanvasElement} */ 29 | addContext: function (name, ctx) { 30 | this._newContexts[name] = ctx; 31 | return this; 32 | }, 33 | /** @returns {Context2D} */ 34 | getContext: function (name) { 35 | return this._newContexts[name] || null; 36 | } 37 | }); 38 | 39 | atom.core.append(HTMLCanvasElement.prototype, 40 | /** @lends HTMLCanvasElement.prototype */ 41 | { 42 | getOriginalContext: HTMLCanvasElement.prototype.getContext, 43 | /** @returns {Context2D} */ 44 | getContext: function (type) { 45 | if (!this.contextsList) { 46 | this.contextsList = {}; 47 | } 48 | 49 | if (!this.contextsList[type]) { 50 | var ctx = HTMLCanvasElement.getContext(type); 51 | if (ctx) { 52 | ctx = new ctx(this); 53 | } else try { 54 | ctx = this.getOriginalContext.apply(this, arguments); 55 | } catch (e) { 56 | throw (!e.toString().match(/NS_ERROR_ILLEGAL_VALUE/)) ? e : 57 | new TypeError('Wrong Context Type: «' + type + '»'); 58 | } 59 | this.contextsList[type] = ctx; 60 | } 61 | return this.contextsList[type]; 62 | } 63 | }); -------------------------------------------------------------------------------- /Source/Core/Geometry.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Geometry" 5 | 6 | description: "Base for such things as Point and Shape" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | 18 | provides: Geometry 19 | 20 | ... 21 | */ 22 | 23 | /** @class Geometry */ 24 | var Geometry = declare( 'LibCanvas.Geometry', { 25 | initialize : function () { 26 | if (arguments.length) this.set.apply(this, arguments); 27 | }, 28 | cast: function (args) { 29 | return this.constructor.castArguments(args); 30 | } 31 | }).own({ 32 | invoke: declare.castArguments, 33 | from : function (obj) { 34 | return this(obj); 35 | } 36 | }); -------------------------------------------------------------------------------- /Source/Core/Mouse.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Mouse" 5 | 6 | description: "A mouse control abstraction class" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Point 18 | 19 | provides: Mouse 20 | 21 | ... 22 | */ 23 | 24 | /** @class Mouse */ 25 | var Mouse = LibCanvas.declare( 'LibCanvas.Mouse', 'Mouse', { 26 | /** @private */ 27 | elem: null, 28 | 29 | /** @property {boolean} */ 30 | inside: false, 31 | /** @property {Point} */ 32 | point: null, 33 | /** @property {Point} */ 34 | previous: null, 35 | /** @property {Point} */ 36 | delta: null, 37 | /** @property {Events} */ 38 | events: null, 39 | 40 | /** @private */ 41 | mapping: { 42 | click : 'click', 43 | dblclick : 'dblclick', 44 | contextmenu: 'contextmenu', 45 | 46 | mouseover : 'over', 47 | mouseout : 'out', 48 | mousedown : 'down', 49 | mouseup : 'up', 50 | mousemove : 'move', 51 | 52 | DOMMouseScroll: 'wheel', 53 | mousewheel : 'wheel' 54 | }, 55 | 56 | initialize : function (elem, offsetElem) { 57 | this.bindMethods( 'onEvent' ); 58 | 59 | if (elem == null) { 60 | throw new TypeError('`elem` is undefined'); 61 | } 62 | 63 | this.elem = atom.dom(elem); 64 | this.offsetElem = offsetElem ? atom.dom(offsetElem) : this.elem; 65 | 66 | this.point = new Point(0, 0); 67 | this.previous = new Point(0, 0); 68 | this.delta = new Point(0, 0); 69 | this.events = new Events(this); 70 | 71 | this.listen(this.onEvent); 72 | }, 73 | /** @private */ 74 | fire: function (name, e) { 75 | this.events.fire(name, [e, this]); 76 | return this; 77 | }, 78 | /** @private */ 79 | onEvent: function (e) { 80 | var 81 | name = this.mapping[e.type], 82 | fn = this.eventActions[name]; 83 | 84 | if (fn) fn.call(this, e); 85 | 86 | this.fire(name, e); 87 | }, 88 | /** @private */ 89 | getOffset: function (e) { 90 | return this.constructor.getOffset(e, this.offsetElem); 91 | }, 92 | /** @private */ 93 | set: function (e, inside) { 94 | var point = this.getOffset(e); 95 | 96 | this.previous.set( this.point ); 97 | this.delta .set( this.previous.diff( point ) ); 98 | this.point .set( point ); 99 | this.inside = inside; 100 | }, 101 | /** @private */ 102 | eventActions: { 103 | wheel: function (e) { 104 | this.constructor.addWheelDelta(e); 105 | }, 106 | 107 | move: function (e) { 108 | this.set(e, true); 109 | }, 110 | 111 | down: function (e) { 112 | this.set(e, true); 113 | }, 114 | 115 | over: function (e) { 116 | if (this.checkEvent(e)) { 117 | this.fire('enter', e); 118 | } 119 | }, 120 | 121 | out: function (e) { 122 | if (this.checkEvent(e)) { 123 | this.set(e, false); 124 | this.fire('leave', e); 125 | } 126 | } 127 | }, 128 | /** @private */ 129 | checkEvent: function (e) { 130 | var related = e.relatedTarget, elem = this.elem; 131 | 132 | return related == null || ( 133 | related && related != elem.first && !elem.contains(related) 134 | ); 135 | }, 136 | /** @private */ 137 | listen : function (callback) { 138 | this.elem 139 | .bind({ selectstart: false }) 140 | .bind(atom.object.map( 141 | this.mapping, atom.fn.lambda(callback) 142 | )); 143 | } 144 | }).own({ 145 | prevent: function (e) {e.preventDefault()}, 146 | addWheelDelta: function (e) { 147 | e.delta = 148 | // IE, Opera, Chrome 149 | e.wheelDelta ? e.wheelDelta > 0 ? 1 : -1 : 150 | // Fx 151 | e.detail ? e.detail < 0 ? 1 : -1 : null; 152 | 153 | return e; 154 | }, 155 | eventSource: function (e) { 156 | return e.changedTouches ? e.changedTouches[0] : e; 157 | }, 158 | expandEvent: function (e) { 159 | var source = this.eventSource(e); 160 | 161 | if (e.pageX == null) { 162 | e.pageX = source.pageX != null ? source.pageX : source.clientX + document.scrollLeft; 163 | e.pageY = source.pageY != null ? source.pageY : source.clientY + document.scrollTop ; 164 | } 165 | 166 | return e; 167 | }, 168 | getOffset : function (e, element) { 169 | var elementOffset = atom.dom(element || this.eventSource(e).target).offset(); 170 | 171 | this.expandEvent(e); 172 | 173 | return new Point( 174 | e.pageX - elementOffset.x, 175 | e.pageY - elementOffset.y 176 | ); 177 | } 178 | }); -------------------------------------------------------------------------------- /Source/Core/Point3D.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Point3D" 5 | 6 | description: "A X/Y/Z point coordinates encapsulating class" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Geometry 18 | 19 | provides: Point3D 20 | 21 | ... 22 | */ 23 | 24 | /** @class Point3D */ 25 | var Point3D = LibCanvas.declare( 'LibCanvas.Point3D', 'Point3D', Geometry, { 26 | x: 0, 27 | y: 0, 28 | z: 0, 29 | 30 | /** @private */ 31 | coordinatesArray: ['x', 'y', 'z'], 32 | 33 | /** 34 | * @constructs 35 | * @param {Number} x 36 | * @param {Number} y 37 | * @param {Number} z 38 | * @returns {Point3D} 39 | */ 40 | set: function (x, y, z) { 41 | if ( arguments.length > 1 ) { 42 | this.x = Number(x) || 0; 43 | this.y = Number(y) || 0; 44 | this.z = Number(z) || 0; 45 | } else if ( x && typeof x.x === 'number' ) { 46 | this.set( x.x, x.y, x.z ); 47 | } else if ( x && typeof x[0] === 'number' ) { 48 | this.set( x[0], x[1], x[2] ); 49 | } else { 50 | throw new Error( 'Wrong arguments in Isometric.Point3D' ); 51 | } 52 | return this; 53 | }, 54 | 55 | /** 56 | * You can pass callback (function( value, axis, point ){}) 57 | * @param {function} fn 58 | * @param {object} [context=null] 59 | * @returns {Point3D} 60 | */ 61 | map: function (fn, context) { 62 | var point = this; 63 | point.coordinatesArray.forEach(function (axis) { 64 | point[axis] = fn.call( context || point, point[axis], axis, point ); 65 | }); 66 | return this; 67 | }, 68 | 69 | /** 70 | * @param {Number} factor 71 | * @returns {Point3D} 72 | */ 73 | add: function (factor) { 74 | return this.map(function (c) { return c+factor }); 75 | }, 76 | 77 | /** 78 | * @param {Number} factor 79 | * @returns {Point3D} 80 | */ 81 | mul: function (factor) { 82 | return this.map(function (c) { return c*factor }); 83 | }, 84 | 85 | /** 86 | * @param {Point3D} point3d 87 | * @returns {Point3D} 88 | */ 89 | diff: function (point3d) { 90 | point3d = this.cast( point3d ); 91 | return new this.constructor( 92 | point3d.x - this.x, 93 | point3d.y - this.y, 94 | point3d.z - this.z 95 | ); 96 | }, 97 | 98 | /** 99 | * @param {Point3D} point3d 100 | * @returns {Point3D} 101 | */ 102 | move: function (point3d) { 103 | point3d = this.cast( arguments ); 104 | this.x += point3d.x; 105 | this.y += point3d.y; 106 | this.z += point3d.z; 107 | return this; 108 | }, 109 | 110 | /** 111 | * @param {Point3D} point3d 112 | * @param {Number} accuracy 113 | * @returns {boolean} 114 | */ 115 | equals: function (point3d, accuracy) { 116 | return point3d.x.equals( this.x, accuracy ) && 117 | point3d.y.equals( this.y, accuracy ) && 118 | point3d.z.equals( this.z, accuracy ); 119 | }, 120 | 121 | /** @returns {Point3D} */ 122 | clone: function () { 123 | return new this.constructor( this ); 124 | }, 125 | 126 | /** @returns Array */ 127 | toArray: function () { 128 | return [this.x, this.y, this.z]; 129 | }, 130 | 131 | /** @returns String */ 132 | dump: function () { 133 | return '[LibCanvas.Point3D(' + this.toArray() + ')]'; 134 | } 135 | }); -------------------------------------------------------------------------------- /Source/Core/Shape.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Shape" 5 | 6 | description: "Abstract class LibCanvas.Shape defines interface for drawable canvas objects" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Geometry 18 | - Point 19 | 20 | provides: Shape 21 | 22 | ... 23 | */ 24 | 25 | var shapeTestBuffer = function () { 26 | if (!shapeTestBuffer.buffer) { 27 | return shapeTestBuffer.buffer = LibCanvas.buffer(1, 1, true); 28 | } 29 | return shapeTestBuffer.buffer; 30 | }; 31 | 32 | /** @class Shape */ 33 | var Shape = declare( 'LibCanvas.Shape', Geometry, { 34 | set : 'abstract', 35 | hasPoint : 'abstract', 36 | processPath: 'abstract', 37 | draw : function (ctx, type) { 38 | this.processPath(ctx)[type](); 39 | return this; 40 | }, 41 | // Методы ниже рассчитывают на то, что в фигуре есть точки from и to 42 | getCoords : function () { 43 | return this.from; 44 | }, 45 | /** @returns {LibCanvas.Shape} */ 46 | grow: function (size) { 47 | if (typeof size == 'number') { 48 | size = new Point(size/2, size/2); 49 | } else { 50 | size = new Point(size.x/2, size.y/2); 51 | } 52 | 53 | this.from.move(size, true); 54 | this. to .move(size); 55 | return this; 56 | }, 57 | get x () { return this.from.x }, 58 | get y () { return this.from.y }, 59 | set x (x) { 60 | return this.move(new Point(x - this.x, 0)); 61 | }, 62 | set y (y) { 63 | return this.move(new Point(0, y - this.y)); 64 | }, 65 | get bottomLeft () { 66 | return new Point(this.from.x, this.to.y); 67 | }, 68 | get topRight() { 69 | return new Point(this.to.x, this.from.y); 70 | }, 71 | get center() { 72 | var from = this.from, to = this.to; 73 | return new Point( (from.x + to.x) / 2, (from.y + to.y) / 2 ); 74 | }, 75 | getBoundingRectangle: function () { 76 | return new Rectangle( this.from, this.to ); 77 | }, 78 | getCenter : function () { 79 | return this.center; 80 | }, 81 | move : function (distance, reverse) { 82 | this.from.move(distance, reverse); 83 | this. to .move(distance, reverse); 84 | return this; 85 | }, 86 | equals : function (shape, accuracy) { 87 | return shape instanceof this.constructor && 88 | shape.from.equals(this.from, accuracy) && 89 | shape.to .equals(this.to , accuracy); 90 | }, 91 | clone : function () { 92 | return new this.constructor(this.from.clone(), this.to.clone()); 93 | }, 94 | dumpPoint: function (point) { 95 | return '[' + point.x + ', ' + point.y + ']'; 96 | }, 97 | dump: function (shape) { 98 | if (!shape) return this.toString(); 99 | return '[shape '+shape+'(from'+this.dumpPoint(this.from)+', to'+this.dumpPoint(this.to)+')]'; 100 | } 101 | }); -------------------------------------------------------------------------------- /Source/Core/Size.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Size" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Point 18 | 19 | provides: Size 20 | 21 | ... 22 | */ 23 | 24 | /** @class Size */ 25 | var Size = LibCanvas.declare( 'LibCanvas.Size', 'Size', Point, { 26 | set: function method (size) { 27 | if (typeof size == 'object' && size.width != null) { 28 | this.x = Number(size.width); 29 | this.y = Number(size.height); 30 | 31 | return this; 32 | } 33 | return method.previous.apply( this, arguments ); 34 | }, 35 | 36 | get width ( ) { return this.x }, 37 | get height ( ) { return this.y }, 38 | set width (w) { this.x = w }, 39 | set height (h) { this.y = h }, 40 | 41 | /** @returns {object} */ 42 | toObject: function () { 43 | return { width: this.x, height: this.y }; 44 | } 45 | }); 46 | 47 | /** @private */ 48 | Size.from = function (object) { 49 | if (object == null) return null; 50 | 51 | return object instanceof Size ? object : new Size(object); 52 | }; -------------------------------------------------------------------------------- /Source/Engines/Hex/Engine.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Engines.Hex" 5 | 6 | license: 7 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 8 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 9 | 10 | authors: 11 | - "Shock " 12 | 13 | requires: 14 | - LibCanvas 15 | 16 | provides: Engines.Hex 17 | 18 | ... 19 | */ 20 | 21 | /** @class HexEngine */ 22 | LibCanvas.declare( 'LibCanvas.Engines.Hex', 'HexEngine', { }); -------------------------------------------------------------------------------- /Source/Engines/Isometric/Engine.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Engines.Isometric" 5 | 6 | license: 7 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 8 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 9 | 10 | authors: 11 | - "Shock " 12 | 13 | requires: 14 | - LibCanvas 15 | 16 | provides: Engines.Isometric 17 | 18 | ... 19 | */ 20 | 21 | /** @class HexEngine */ 22 | LibCanvas.declare( 'LibCanvas.Engines.Isometric', 'IsometricEngine', { }); -------------------------------------------------------------------------------- /Source/Engines/Isometric/Projection.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Engines.Isometric.Projection" 5 | 6 | license: 7 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 8 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 9 | 10 | authors: 11 | - "Shock " 12 | 13 | requires: 14 | - LibCanvas 15 | - Point3D 16 | - Engines.Isometric 17 | 18 | provides: Engines.Isometric.Projection 19 | 20 | ... 21 | */ 22 | 23 | /** @class IsometricProjection */ 24 | atom.declare( 'LibCanvas.Engines.Isometric.Projection', { 25 | 26 | /** 27 | * factor (and default factor in proto) 28 | * @property {Point3D} 29 | */ 30 | factor: [0.866, 0.5, 0.866], 31 | 32 | /** 33 | * size (and default size in proto) 34 | * @property int 35 | */ 36 | size: 1, 37 | 38 | /** 39 | * start (and default start in proto) 40 | * @property {Point} 41 | */ 42 | start: [0, 0], 43 | 44 | /** 45 | * @constructs 46 | * @param {object} settings 47 | * @param {Point3D} settings.factor 48 | * @param {Point3D} settings.size 49 | * @param {Point} settings.start - position of [0,0] coordinate 50 | */ 51 | initialize: function (settings) { 52 | this.bindMethods([ 'toIsometric', 'to3D' ]); 53 | this.settings = new Settings(settings); 54 | 55 | this.factor = Point3D( this.settings.get('factor') || this.factor ); 56 | this.size = Number ( this.settings.get('size') || this.size ); 57 | this.start = Point ( this.settings.get('start') || this.start ); 58 | }, 59 | 60 | /** 61 | * @param {Point3D} point3d 62 | * @returns {Point} 63 | */ 64 | toIsometric: function (point3d) { 65 | point3d = Point3D( point3d ); 66 | return new Point( 67 | (point3d.y + point3d.x) * this.factor.x, 68 | (point3d.y - point3d.x) * this.factor.y - point3d.z * this.factor.z 69 | ) 70 | .mul(this.size) 71 | .move(this.start); 72 | }, 73 | 74 | /** 75 | * @param {Point} point 76 | * @param {int} [z=0] 77 | * @returns {Point3D} 78 | */ 79 | to3D: function (point, z) { 80 | point = Point(point); 81 | z = Number(z) || 0; 82 | 83 | var 84 | size = this.size, 85 | start = this.start, 86 | dXY = ((point.y - start.y) / size + z * this.factor.z) / this.factor.y, 87 | pX = ((point.x - start.x) / size / this.factor.x - dXY) / 2; 88 | 89 | return new Point3D( pX, pX + dXY, z ); 90 | } 91 | }); -------------------------------------------------------------------------------- /Source/Engines/Tile/Cell.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "TileEngine.Cell" 5 | 6 | license: 7 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 8 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 9 | 10 | authors: 11 | - "Shock " 12 | 13 | requires: 14 | - Engines.Tile 15 | 16 | provides: Engines.Tile.Cell 17 | 18 | ... 19 | */ 20 | /** @class TileEngine.Cell */ 21 | declare( 'LibCanvas.Engines.Tile.Cell', { 22 | 23 | initialize: function (engine, point, rectangle, value) { 24 | this.engine = engine; 25 | this.point = point; 26 | this.value = value; 27 | this.rectangle = rectangle; 28 | }, 29 | 30 | /** @private */ 31 | _value: null, 32 | 33 | get value () { 34 | return this._value; 35 | }, 36 | 37 | set value (value) { 38 | this._value = value; 39 | this.engine.updateCell(this); 40 | }, 41 | 42 | renderTo: function (ctx) { 43 | var method, value = this.value, rectangle = this.rectangle; 44 | 45 | ctx.clear( rectangle ); 46 | 47 | if (value == null) return this; 48 | 49 | method = this.engine.methods[ value ]; 50 | 51 | if (method == null) { 52 | throw new Error( 'No method in tile engine: «' + this.value + '»') 53 | } 54 | 55 | if (atom.dom.isElement(method)) { 56 | ctx.drawImage( method, rectangle ); 57 | } else if (typeof method == 'function') { 58 | method.call( this, ctx, this ); 59 | } else { 60 | ctx.fill( rectangle, method ); 61 | } 62 | return this; 63 | } 64 | 65 | }); -------------------------------------------------------------------------------- /Source/Engines/Tile/Element.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "TileEngine.Element" 5 | 6 | license: 7 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 8 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 9 | 10 | authors: 11 | - "Shock " 12 | 13 | requires: 14 | - Engines.Tile 15 | - App.Element 16 | 17 | provides: Engines.Tile.Element 18 | 19 | ... 20 | */ 21 | /** @class TileEngine.Element */ 22 | declare( 'LibCanvas.Engines.Tile.Element', App.Element, { 23 | configure: function () { 24 | this.shape = new Rectangle( 25 | this.settings.get('from') || new Point(0, 0), 26 | this.engine.countSize() 27 | ); 28 | this.engine.events.add( 'update', this.redraw ); 29 | }, 30 | 31 | get engine () { 32 | return this.settings.get('engine'); 33 | }, 34 | 35 | clearPrevious: function () {}, 36 | 37 | renderTo: function (ctx) { 38 | this.engine.refresh(ctx, this.shape.from); 39 | } 40 | }).own({ 41 | app: function (app, engine, from) { 42 | return new this( app.createLayer({ 43 | name: 'tile-engine', 44 | intersection: 'manual', 45 | invoke: false 46 | }), { 47 | engine: engine, 48 | from: from 49 | }); 50 | } 51 | }); 52 | 53 | -------------------------------------------------------------------------------- /Source/Engines/Tile/Mouse.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "TileEngine.Mouse" 5 | 6 | license: 7 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 8 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 9 | 10 | authors: 11 | - "Shock " 12 | 13 | requires: 14 | - Engines.Tile.Element 15 | 16 | provides: Engines.Tile.Mouse 17 | 18 | ... 19 | */ 20 | /** @class TileEngine.Mouse */ 21 | declare( 'LibCanvas.Engines.Tile.Mouse', { 22 | eventsList: 'mousemove mouseout mousedown mouseup contextmenu' 23 | .split(' '), 24 | 25 | initialize: function (element, mouse) { 26 | this.bindMethods(this.eventsList); 27 | 28 | this.events = new Events(this); 29 | 30 | /** @private */ 31 | this.mouse = mouse; 32 | /** @private */ 33 | this.element = element; 34 | /** @private */ 35 | this.previous = null; 36 | /** @private */ 37 | this.lastDown = null; 38 | this.subscribe(false); 39 | }, 40 | 41 | /** @private */ 42 | subscribe: function (un) { 43 | var events = atom.object.collect(this, this.eventsList, null); 44 | 45 | this.element.events 46 | [ un ? 'remove' : 'add' ] 47 | (events); 48 | }, 49 | /** @private */ 50 | mousemove: function (e) { 51 | var cell = this.get(); 52 | if (this.previous != cell) { 53 | this.outCell(e); 54 | this.fire( 'over', cell, e ); 55 | this.previous = cell; 56 | } 57 | }, 58 | /** @private */ 59 | mouseout: function (e) { 60 | this.outCell(e); 61 | }, 62 | /** @private */ 63 | mousedown: function (e) { 64 | var cell = this.get(); 65 | this.fire( 'down', cell, e ); 66 | this.lastDown = cell; 67 | }, 68 | /** @private */ 69 | mouseup: function (e) { 70 | var cell = this.get(); 71 | this.fire( 'up', cell, e ); 72 | if (cell != null && cell == this.lastDown) { 73 | this.fire( 'click', cell, e ); 74 | } 75 | this.lastDown = null; 76 | }, 77 | /** @private */ 78 | contextmenu: function (e) { 79 | var cell = this.get(); 80 | if (cell != null) { 81 | this.fire( 'contextmenu', cell, e ); 82 | } 83 | }, 84 | /** @private */ 85 | get: function () { 86 | return this.element.engine.getCellByPoint( 87 | this.mouse.point.clone().move(this.element.shape.from, true) 88 | ); 89 | }, 90 | 91 | /** @private */ 92 | fire: function (event, cell, e) { 93 | return this.events.fire( event, [ cell, e ]); 94 | }, 95 | 96 | /** @private */ 97 | outCell: function (e) { 98 | if (this.previous) { 99 | this.fire( 'out', this.previous, e ); 100 | this.previous = null; 101 | } 102 | } 103 | }); -------------------------------------------------------------------------------- /Source/Engines/Tile/Tile.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "TileEngine" 5 | 6 | license: 7 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 8 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 9 | 10 | authors: 11 | - "Shock " 12 | 13 | requires: 14 | - LibCanvas 15 | - Point 16 | - Size 17 | - Rectangle 18 | 19 | provides: Engines.Tile 20 | 21 | ... 22 | */ 23 | 24 | /** @class TileEngine */ 25 | var TileEngine = LibCanvas.declare( 'LibCanvas.Engines.Tile', 'TileEngine', { 26 | 27 | /** 28 | * @param {Object} settings 29 | * @param {*} settings.defaultValue 30 | * @param {Size} settings.size 31 | * @param {Size} settings.cellSize 32 | * @param {Size} settings.cellMargin 33 | */ 34 | initialize: function (settings) { 35 | this.cells = []; 36 | this.methods = {}; 37 | this.cellsUpdate = []; 38 | 39 | this.events = new Events(this); 40 | this.settings = new Settings(settings).addEvents(this.events); 41 | this.createMatrix(); 42 | }, 43 | 44 | setMethod: atom.core.overloadSetter(function (name, method) { 45 | if (this.isValidMethod(method)) { 46 | this.methods[ name ] = method; 47 | } else { 48 | throw new TypeError( 'Unknown method: «' + name + '»' ); 49 | } 50 | }), 51 | 52 | countSize: function () { 53 | var 54 | settings = this.settings, 55 | cellSize = settings.get('cellSize'), 56 | cellMargin = settings.get('cellMargin'); 57 | 58 | return new Size( 59 | (cellSize.x + cellMargin.x) * this.width - cellMargin.x, 60 | (cellSize.y + cellMargin.y) * this.height - cellMargin.y 61 | ); 62 | }, 63 | 64 | getCellByIndex: function (point) { 65 | point = Point.from(point); 66 | return this.isIndexOutOfBounds(point) ? null: 67 | this.cells[ this.width * point.y + point.x ]; 68 | }, 69 | 70 | getCellByPoint: function (point) { 71 | var 72 | settings = this.settings, 73 | cellSize = settings.get('cellSize'), 74 | cellMargin = settings.get('cellMargin'); 75 | 76 | point = Point.from(point); 77 | 78 | return this.getCellByIndex(new Point( 79 | parseInt(point.x / (cellSize.width + cellMargin.x)), 80 | parseInt(point.y / (cellSize.height + cellMargin.y)) 81 | )); 82 | }, 83 | 84 | refresh: function (ctx, translate) { 85 | if (this.requireUpdate) { 86 | ctx.save(); 87 | if (translate) ctx.translate(translate); 88 | atom.array.invoke( this.cellsUpdate, 'renderTo', ctx ); 89 | ctx.restore(); 90 | this.cellsUpdate.length = 0; 91 | } 92 | return this; 93 | }, 94 | 95 | get width () { 96 | return this.settings.get('size').width; 97 | }, 98 | 99 | get height () { 100 | return this.settings.get('size').height; 101 | }, 102 | 103 | get requireUpdate () { 104 | return !!this.cellsUpdate.length; 105 | }, 106 | 107 | /** @private */ 108 | isValidMethod: function (method) { 109 | var type = typeof method; 110 | 111 | return type == 'function' 112 | || type == 'string' 113 | || atom.dom.isElement(method); 114 | }, 115 | 116 | /** @private */ 117 | createMatrix : function () { 118 | var x, y, 119 | settings = this.settings, 120 | size = settings.get('size'), 121 | value = settings.get('defaultValue'), 122 | cellSize = settings.get('cellSize'), 123 | cellMargin = settings.get('cellMargin'); 124 | 125 | for (y = 0; y < size.height; y++) for (x = 0; x < size.width; x++) { 126 | this.createMatrixCell(new Point(x, y), cellSize, cellMargin, value); 127 | } 128 | return this; 129 | }, 130 | 131 | /** @private */ 132 | createMatrixCell: function (point, size, margin, value) { 133 | var shape = this.createCellRectangle(point, size, margin); 134 | 135 | this.cells.push(this.createCell(point, shape, value)); 136 | }, 137 | 138 | /** @private */ 139 | createCell: function (point, shape, value) { 140 | return new TileEngine.Cell( this, point, shape, value ); 141 | }, 142 | 143 | /** @private */ 144 | createCellRectangle: function (point, cellSize, cellMargin) { 145 | return new Rectangle({ 146 | from: new Point( 147 | (cellSize.x + cellMargin.x) * point.x, 148 | (cellSize.y + cellMargin.y) * point.y 149 | ), 150 | size: cellSize 151 | }); 152 | }, 153 | 154 | /** @private */ 155 | isIndexOutOfBounds: function (point) { 156 | return point.x < 0 || point.y < 0 || point.x >= this.width || point.y >= this.height; 157 | }, 158 | 159 | /** @private */ 160 | updateCell: function (cell) { 161 | if (!this.requireUpdate) { 162 | this.events.fire('update', [ this ]); 163 | } 164 | atom.array.include( this.cellsUpdate, cell ); 165 | return this; 166 | } 167 | 168 | }); -------------------------------------------------------------------------------- /Source/LibCanvas.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "LibCanvas" 5 | 6 | description: "LibCanvas initialization" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - Pavel Ponomarenko aka Shock 14 | 15 | provides: LibCanvas 16 | 17 | ... 18 | */ 19 | 20 | var LibCanvas = this.LibCanvas = declare({ name: 'LibCanvas', prototype: {} }) 21 | .own({ 22 | Buffer: function () { 23 | return LibCanvas.buffer.apply( LibCanvas, arguments ); 24 | }, 25 | buffer: function (width, height, withCtx) { 26 | var canvas, size, a = slice.call(arguments), last = a[a.length-1]; 27 | 28 | withCtx = (typeof last === 'boolean' ? a.pop() : false); 29 | 30 | size = Size(a.length == 1 ? a[0] : a); 31 | 32 | canvas = atom.dom.create("canvas", { 33 | width : size.width, 34 | height : size.height 35 | }).first; 36 | 37 | if (withCtx) canvas.ctx = new Context2D(canvas); 38 | return canvas; 39 | }, 40 | 'declare.classes': {}, 41 | declare: function (declareName, shortName, Parent, object) { 42 | if (typeof shortName == 'object') { 43 | object = Parent; 44 | Parent = shortName; 45 | shortName = null; 46 | } 47 | if (object == null) { 48 | object = Parent; 49 | Parent = null; 50 | } 51 | var Class = declare( declareName, Parent, object ); 52 | if (shortName) { 53 | if (shortName in this['declare.classes']) { 54 | throw new Error( 'Duplicate declaration: ' + shortName ); 55 | } 56 | this['declare.classes'][shortName] = Class; 57 | } 58 | return Class; 59 | }, 60 | extract: function (to) { 61 | to = to || global; 62 | for (var k in this['declare.classes']) { 63 | to[k] = this['declare.classes'][k]; 64 | } 65 | return to; 66 | } 67 | }); -------------------------------------------------------------------------------- /Source/Plugins/Animation/Animation.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Plugins.Animation.Core" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - Pavel Ponomarenko aka Shock 14 | 15 | provides: 16 | - Plugins.Animation 17 | - Plugins.Animation.Core 18 | 19 | requires: 20 | - LibCanvas 21 | 22 | ... 23 | */ 24 | 25 | /** @class Animation */ 26 | var Animation = LibCanvas.declare( 'LibCanvas.Plugins.Animation', 'Animation', { 27 | /** @private */ 28 | ownStartTime: null, 29 | /** @private */ 30 | timeoutId : 0, 31 | /** @private */ 32 | synchronizedWith: null, 33 | 34 | initialize: function (settings) { 35 | this.bindMethods('update'); 36 | 37 | this.events = new atom.Events(this); 38 | this.settings = new atom.Settings(settings).addEvents(this.events); 39 | this.run(); 40 | }, 41 | 42 | stop: function () { 43 | this.startTime = null; 44 | return this.update(); 45 | }, 46 | 47 | run: function () { 48 | this.startTime = Date.now(); 49 | return this.update(); 50 | }, 51 | 52 | synchronize: function (anim) { 53 | this.synchronizedWith = anim; 54 | return this; 55 | }, 56 | 57 | get: function () { 58 | return this.sheet.get(this.startTime); 59 | }, 60 | 61 | /** @private */ 62 | get sheet () { 63 | return this.settings.get('sheet'); 64 | }, 65 | 66 | /** @private */ 67 | set sheet (sheet) { 68 | return this.settings.set('sheet', sheet); 69 | }, 70 | 71 | /** @private */ 72 | set startTime (time) { 73 | this.ownStartTime = time; 74 | }, 75 | 76 | /** @private */ 77 | get startTime () { 78 | if (this.synchronizedWith) { 79 | return this.synchronizedWith.startTime; 80 | } else { 81 | return this.ownStartTime; 82 | } 83 | }, 84 | 85 | /** @private */ 86 | update: function () { 87 | var delay = this.getDelay(); 88 | 89 | clearTimeout(this.timeoutId); 90 | 91 | if (delay == null || this.startTime == null) { 92 | this.events.fire('stop'); 93 | } else { 94 | this.timeoutId = setTimeout( this.update, delay ); 95 | this.events.fire('update', [ this.get() ]); 96 | } 97 | return this; 98 | }, 99 | 100 | /** @private */ 101 | getDelay: function () { 102 | return this.startTime == null ? null : 103 | this.sheet.getCurrentDelay(this.startTime); 104 | } 105 | }); -------------------------------------------------------------------------------- /Source/Plugins/Animation/Frames.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Plugins.Animation.Frames" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - Pavel Ponomarenko aka Shock 14 | 15 | provides: 16 | - Plugins.Animation 17 | - Plugins.Animation.Frames 18 | 19 | requires: 20 | - LibCanvas 21 | - Plugins.Animation.Core 22 | 23 | ... 24 | */ 25 | 26 | /** @class Animation.Frames */ 27 | atom.declare( 'LibCanvas.Plugins.Animation.Frames', { 28 | /** @private */ 29 | sprites: [], 30 | 31 | initialize: function (image, width, height) { 32 | if (image == null) throw new TypeError('`image` cant be null'); 33 | 34 | this.sprites = []; 35 | this.image = image; 36 | this.size = new Size( 37 | Math.round( width == null ? image.width : width ), 38 | Math.round( height == null ? image.height : height ) 39 | ); 40 | 41 | this.prepare(); 42 | }, 43 | 44 | get: function (id) { 45 | var sprite = this.sprites[id]; 46 | 47 | if (!sprite) { 48 | throw new Error('No sprite with such id: ' + id); 49 | } 50 | 51 | return sprite; 52 | }, 53 | 54 | get length () { 55 | return this.sprites.length; 56 | }, 57 | 58 | /** @private */ 59 | prepare: function () { 60 | var x, y, 61 | im = this.image, 62 | w = this.size.width, 63 | h = this.size.height; 64 | 65 | for (y = 0; y <= im.height - h; y += h) { 66 | for (x = 0; x <= im.width - w; x += w) { 67 | this.sprites.push( this.makeSprite(new Point(x, y)) ); 68 | } 69 | } 70 | 71 | if (!this.length) { 72 | throw new TypeError('Animation is empty'); 73 | } 74 | }, 75 | 76 | /** @private */ 77 | makeSprite: function (from) { 78 | var 79 | size = this.size, 80 | buffer = LibCanvas.buffer(size, true); 81 | 82 | buffer.ctx.drawImage({ 83 | image: this.image, 84 | draw : buffer.ctx.rectangle, 85 | crop : new Rectangle(from, size) 86 | }); 87 | 88 | return buffer; 89 | } 90 | }); -------------------------------------------------------------------------------- /Source/Plugins/Animation/Image.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Plugins.Animation.Image" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - Pavel Ponomarenko aka Shock 14 | 15 | provides: 16 | - Plugins.Animation 17 | - Plugins.Animation.Image 18 | 19 | requires: 20 | - LibCanvas 21 | - Plugins.Animation.Core 22 | 23 | ... 24 | */ 25 | 26 | /** @name Animation.Image */ 27 | atom.declare( 'LibCanvas.Plugins.Animation.Image', { 28 | /** @private */ 29 | initialize: function (animation) { 30 | this.bindMethods('update'); 31 | 32 | if (animation instanceof Animation.Sheet) { 33 | animation = { sheet: animation }; 34 | } 35 | if (!(animation instanceof Animation)) { 36 | animation = new Animation(animation); 37 | } 38 | 39 | this.buffer = LibCanvas.buffer(animation.sheet.size, true); 40 | this.element = atom.dom(this.buffer); 41 | this.animation = animation; 42 | this.element.controller = this; 43 | 44 | animation.events.add( 'update', this.update ); 45 | }, 46 | 47 | /** @private */ 48 | update: function (image) { 49 | var ctx = this.buffer.ctx; 50 | 51 | ctx.clearAll(); 52 | if (image) ctx.drawImage(image); 53 | } 54 | }).own({ 55 | element: function (animation) { 56 | return new this(animation).element; 57 | } 58 | }); -------------------------------------------------------------------------------- /Source/Plugins/Animation/Sheet.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Plugins.Animation.Sheet" 5 | 6 | description: "" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - Pavel Ponomarenko aka Shock 14 | 15 | provides: 16 | - Plugins.Animation 17 | - Plugins.Animation.Sheet 18 | 19 | requires: 20 | - LibCanvas 21 | - Plugins.Animation.Core 22 | 23 | ... 24 | */ 25 | 26 | /** @class Animation.Sheet */ 27 | atom.declare( 'LibCanvas.Plugins.Animation.Sheet', { 28 | 29 | initialize: function (options) { 30 | this.frames = options.frames; 31 | this.delay = options.delay; 32 | this.looped = options.looped; 33 | if (options.sequence == null) { 34 | this.sequence = atom.array.range(0, this.frames.length - 1); 35 | } else { 36 | this.sequence = options.sequence; 37 | } 38 | }, 39 | 40 | /** @private */ 41 | get size () { 42 | return this.frames.size; 43 | }, 44 | 45 | /** @private */ 46 | get: function (startTime) { 47 | if (startTime == null) return null; 48 | 49 | var id = this.getFrameId(this.countFrames(startTime)); 50 | return id == null ? id : this.frames.get( id ); 51 | }, 52 | 53 | /** @private */ 54 | getCurrentDelay: function (startTime) { 55 | var frames, switchTime; 56 | 57 | frames = this.countFrames(startTime); 58 | 59 | if (this.getFrameId(frames) == null) { 60 | return null; 61 | } 62 | 63 | // когда был включён текущий кадр 64 | switchTime = frames * this.delay + startTime; 65 | 66 | // до следующего кадра - задержка минус время, которое уже показывается текущий 67 | return this.delay - ( Date.now() - switchTime ); 68 | }, 69 | 70 | /** @private */ 71 | getFrameId: function (framesCount) { 72 | if (this.looped) { 73 | return this.sequence[ framesCount % this.sequence.length ]; 74 | } else if (framesCount >= this.sequence.length) { 75 | return null; 76 | } else { 77 | return this.sequence[framesCount]; 78 | } 79 | }, 80 | 81 | /** @private */ 82 | countFrames: function (startTime) { 83 | return Math.floor( (Date.now() - startTime) / this.delay ); 84 | } 85 | 86 | }); -------------------------------------------------------------------------------- /Source/Plugins/Canvas2DContext.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Canvas2DContext" 5 | 6 | description: "LibCanvas.Canvas2DContext is similar to original 2d context (and chainable). Can be used for creating your own contexts" 7 | 8 | license: "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 9 | 10 | authors: 11 | - "Shock " 12 | 13 | requires: 14 | - LibCanvas 15 | 16 | provides: Canvas2DContext 17 | 18 | ... 19 | */ 20 | 21 | new function () { 22 | 23 | var object = { 24 | initialize: function (canvas) { 25 | if (canvas instanceof CanvasRenderingContext2D) { 26 | this.ctx2d = canvas; 27 | this.canvas = this.ctx2d.canvas; 28 | } else { 29 | this.canvas = canvas; 30 | this.ctx2d = canvas.getOriginalContext('2d'); 31 | } 32 | }, 33 | get width () { return this.canvas.width }, 34 | get height() { return this.canvas.height }, 35 | set width (width) { this.canvas.width = width }, 36 | set height(height) { this.canvas.height = height } 37 | }, 38 | 39 | methods = ( 40 | 'arc arcTo beginPath bezierCurveTo clearRect clip ' + 41 | 'closePath drawImage fill fillRect fillText lineTo moveTo ' + 42 | 'quadraticCurveTo rect restore rotate save scale setTransform ' + 43 | 'stroke strokeRect strokeText transform translate' 44 | ).split(' '), 45 | 46 | getterMethods = ( 47 | 'createPattern drawFocusRing isPointInPath measureText ' + 48 | 'createImageData createLinearGradient ' + 49 | 'createRadialGradient getImageData putImageData' 50 | ).split(' '), 51 | 52 | properties = ( 53 | 'fillStyle font globalAlpha globalCompositeOperation lineCap ' + 54 | 'lineJoin lineWidth miterLimit shadowOffsetX shadowOffsetY ' + 55 | 'shadowBlur shadowColor strokeStyle textAlign textBaseline' 56 | ).split(' '); 57 | 58 | properties.forEach(function (property) { 59 | atom.accessors.define(object, property, { 60 | set: function (value) { 61 | try { 62 | this.ctx2d[property] = value; 63 | } catch (e) { 64 | throw TypeError('Exception while setting «' + property + '» to «' + value + '»: ' + e.message); 65 | } 66 | }, 67 | get: function () { 68 | return this.ctx2d[property]; 69 | } 70 | }) 71 | }); 72 | 73 | methods.forEach(function (method) { 74 | object[method] = function () { 75 | this.ctx2d[method].apply(this.ctx, arguments); 76 | return this; 77 | }; 78 | }); 79 | 80 | getterMethods.forEach(function (method) { 81 | object[method] = function () { 82 | return this.ctx2d[method].apply(this.ctx, arguments); 83 | }; 84 | }); 85 | 86 | atom.declare( 'LibCanvas.Canvas2DContext', object ); 87 | 88 | }; -------------------------------------------------------------------------------- /Source/Plugins/Curve/Curve.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Plugins.Curve" 5 | 6 | description: "Provides math base for bezier curves" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - Pavel Ponomarenko aka Shock 14 | 15 | provides: Plugins.Curve.Core 16 | 17 | requires: 18 | - LibCanvas 19 | - Point 20 | 21 | ... 22 | */ 23 | 24 | /** @name LibCanvas.Plugins.Curve */ 25 | atom.declare( 'LibCanvas.Plugins.Curve', { 26 | 27 | step: 0.0001, 28 | 29 | initialize: function (data) { 30 | var Class = this.constructor.classes[data.points.length]; 31 | 32 | if (Class) return new Class(data); 33 | 34 | this.setData(data); 35 | }, 36 | 37 | setData: function (data) { 38 | this.from = data.from; 39 | this.to = data.to; 40 | this.cp = data.points; 41 | }, 42 | 43 | getAngle: function (t) { 44 | var f; 45 | 46 | if (t < this.step) { 47 | f = t - this.step; 48 | } else { 49 | f = t; 50 | t += this.step; 51 | } 52 | 53 | return this.getPoint(t).angleTo(this.getPoint(f)); 54 | } 55 | 56 | }).own({ 57 | classes: {}, 58 | 59 | addClass: function (points, Class) { 60 | this.classes[points] = Class; 61 | } 62 | }); -------------------------------------------------------------------------------- /Source/Plugins/Curve/Quadratic.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Plugins.Curve.Quadratic" 5 | 6 | description: "Provides math base for bezier curves" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - Pavel Ponomarenko aka Shock 14 | 15 | provides: 16 | - Plugins.Curve 17 | - Plugins.Curve.Quadratic 18 | 19 | requires: 20 | - Plugins.Curve.Core 21 | 22 | ... 23 | */ 24 | 25 | /** @name LibCanvas.Plugins.Curve.Quadratic */ 26 | atom.declare( 'LibCanvas.Plugins.Curve.Quadratic', LibCanvas.Plugins.Curve, { 27 | 28 | initialize: function (data) { 29 | this.setData(data); 30 | }, 31 | 32 | getPoint: function (t) { 33 | var 34 | from = this.from, 35 | to = this.to, 36 | point= this.cp[0], 37 | i = 1 - t; 38 | 39 | return new Point( 40 | i*i*from.x + 2*t*i*point.x + t*t*to.x, 41 | i*i*from.y + 2*t*i*point.y + t*t*to.y 42 | ); 43 | } 44 | 45 | }); 46 | 47 | LibCanvas.Plugins.Curve.addClass(1, LibCanvas.Plugins.Curve.Quadratic); -------------------------------------------------------------------------------- /Source/Plugins/Curve/Qubic.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Plugins.Curve.Qubic" 5 | 6 | description: "Provides math base for bezier curves" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - Pavel Ponomarenko aka Shock 14 | 15 | provides: 16 | - Plugins.Curve 17 | - Plugins.Curve.Qubic 18 | 19 | requires: 20 | - Plugins.Curve.Core 21 | 22 | ... 23 | */ 24 | 25 | /** @name LibCanvas.Plugins.Curve.Qubic */ 26 | atom.declare( 'LibCanvas.Plugins.Curve.Qubic', LibCanvas.Plugins.Curve, { 27 | 28 | initialize: function (data) { 29 | this.setData(data); 30 | }, 31 | 32 | getPoint: function (t) { 33 | var 34 | from = this.from, 35 | to = this.to, 36 | cp = this.cp, 37 | i = 1 - t; 38 | 39 | return new Point( 40 | i*i*i*from.x + 3*t*i*i*cp[0].x + 3*t*t*i*cp[1].x + t*t*t*to.x, 41 | i*i*i*from.y + 3*t*i*i*cp[0].y + 3*t*t*i*cp[1].y + t*t*t*to.y 42 | ); 43 | } 44 | 45 | }); 46 | 47 | LibCanvas.Plugins.Curve.addClass(2, LibCanvas.Plugins.Curve.Qubic); -------------------------------------------------------------------------------- /Source/Plugins/Curves.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Plugins.ExtendedCurves" 5 | 6 | description: "Curves with dynamic width and color" 7 | 8 | license: "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 9 | 10 | authors: 11 | - "Artem Smirnov " 12 | - "Shock " 13 | 14 | requires: 15 | - LibCanvas 16 | - Context2D 17 | 18 | provides: Plugins.ExtendedCurves 19 | 20 | ... 21 | */ 22 | 23 | new function () { 24 | 25 | // The following text contains bad code and due to it's code it should not be readed by ANYONE! 26 | 27 | var 28 | Transition = atom.Transition, 29 | Color = atom.Color, 30 | EC = {}; 31 | 32 | /** @returns {atom.Color} */ 33 | EC.getColor = function (color) { 34 | return new Color(color || [0,0,0,1]); 35 | }; 36 | 37 | EC.getPoints = function (prevPos, pos, width, inverted) { 38 | var 39 | w = pos.x-prevPos.x, 40 | h = pos.y-prevPos.y, 41 | dist = atom.math.hypotenuse(w, h), 42 | 43 | sin = h / dist, 44 | cos = w / dist, 45 | 46 | dx = sin * width, 47 | dy = cos * width; 48 | 49 | return [ 50 | new Point(pos.x + dx, pos.y + dy*inverted), 51 | new Point(pos.x - dx, pos.y - dy*inverted) 52 | ]; 53 | }; 54 | 55 | EC.getGradientFunction = function (attr) { 56 | switch (typeof attr.gradient) { 57 | case 'undefined' : 58 | return atom.fn.lambda( EC.getColor(attr.color) ); 59 | 60 | case 'function' : 61 | return attr.gradient; 62 | 63 | default : 64 | var gradient = { fn: attr.gradient.fn || 'linear' }; 65 | 66 | if (typeof gradient.fn != 'string') { 67 | throw new Error('LibCanvas.Context2D.drawCurve -- unexpected type of gradient function'); 68 | } 69 | 70 | gradient.from = EC.getColor(attr.gradient.from); 71 | gradient.to = EC.getColor(attr.gradient.to ); 72 | 73 | var diff = gradient.from.diff( gradient.to ); 74 | 75 | return function (t) { 76 | var factor = Transition.get(gradient.fn)(t); 77 | return gradient.from.shift( diff.clone().mul(factor) ).toString(); 78 | }; 79 | } 80 | }; 81 | 82 | EC.getWidthFunction = function (attr) { 83 | attr.width = attr.width || 1; 84 | switch (typeof attr.width) { 85 | case 'number' : return atom.fn.lambda(attr.width); 86 | case 'function': return attr.width; 87 | case 'object' : return EC.getWidthFunction.range( attr.width ); 88 | default: throw new TypeError('LibCanvas.Context2D.drawCurve -- unexpected type of width'); 89 | } 90 | }; 91 | 92 | EC.getWidthFunction.range = function (width) { 93 | if(!width.from || !width.to){ 94 | throw new Error('LibCanvas.Context2D.drawCurve -- width.from or width.to undefined'); 95 | } 96 | var diff = width.to - width.from; 97 | return function(t){ 98 | return width.from + diff * Transition.get(width.fn || 'linear')(t); 99 | } 100 | }; 101 | 102 | EC.curvesFunctions = [ 103 | function (p, t) { // linear 104 | return { 105 | x:p[0].x + (p[1].x - p[0].x) * t, 106 | y:p[0].y + (p[1].y - p[0].y) * t 107 | }; 108 | }, 109 | function (p,t) { // quadratic 110 | var i = 1-t; 111 | return { 112 | x:i*i*p[0].x + 2*t*i*p[1].x + t*t*p[2].x, 113 | y:i*i*p[0].y + 2*t*i*p[1].y + t*t*p[2].y 114 | }; 115 | }, 116 | function (p, t) { // qubic 117 | var i = 1-t; 118 | return { 119 | x:i*i*i*p[0].x + 3*t*i*i*p[1].x + 3*t*t*i*p[2].x + t*t*t*p[3].x, 120 | y:i*i*i*p[0].y + 3*t*i*i*p[1].y + 3*t*t*i*p[2].y + t*t*t*p[3].y 121 | }; 122 | } 123 | ]; 124 | 125 | Context2D.prototype.drawCurve = function (obj) { 126 | var points = atom.array.append( [Point(obj.from)], obj.points.map(Point), [Point(obj.to)] ); 127 | 128 | var gradientFunction = EC.getGradientFunction(obj), //Getting gradient function 129 | widthFunction = EC.getWidthFunction(obj), //Getting width function 130 | curveFunction = EC.curvesFunctions[ obj.points.length ]; //Getting curve function 131 | 132 | if (!curveFunction) throw new Error('LibCanvas.Context2D.drawCurve -- unexpected number of points'); 133 | 134 | var step = obj.step || 0.02; 135 | 136 | var invertedMultipler = obj.inverted ? 1 : -1; 137 | 138 | var controlPoint, prevContorolPoint, 139 | drawPoints , prevDrawPoints , 140 | width , color, prevColor, style; 141 | 142 | prevContorolPoint = curveFunction(points, -step); 143 | 144 | for (var t=-step ; t<1.02 ; t += step) { 145 | controlPoint = curveFunction(points, t); 146 | color = gradientFunction(t); 147 | width = widthFunction(t) / 2; 148 | 149 | drawPoints = EC.getPoints(prevContorolPoint, controlPoint, width, invertedMultipler); 150 | 151 | if (t >= step) { 152 | // #todo: reduce is part of array, not color 153 | var diff = EC.getColor(prevColor).diff(color); 154 | 155 | if ( (diff.red + diff.green + diff.blue) > 150 ) { 156 | style = this.createLinearGradient(prevContorolPoint, controlPoint); 157 | style.addColorStop(0, prevColor); 158 | style.addColorStop(1, color); 159 | } else { 160 | style = color; 161 | } 162 | 163 | this 164 | .set("lineWidth",1) 165 | .beginPath(prevDrawPoints[0]) 166 | .lineTo (prevDrawPoints[1]) 167 | .lineTo (drawPoints[1]) 168 | .lineTo (drawPoints[0]) 169 | .fill (style) 170 | .stroke(style); 171 | } 172 | prevDrawPoints = drawPoints; 173 | prevContorolPoint = controlPoint; 174 | prevColor = color; 175 | } 176 | return this; 177 | }; 178 | 179 | }; -------------------------------------------------------------------------------- /Source/Plugins/Image.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Plugins.Image" 5 | 6 | description: "Provides some Image extensions" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Shapes.Rectangle 18 | 19 | provides: Plugins.Image 20 | 21 | ... 22 | */ 23 | 24 | var UtilsImage = atom.declare( 'LibCanvas.Utils.Image', { 25 | canvasCache: null, 26 | 27 | initialize: function (image) { 28 | this.image = image; 29 | this.cache = {}; 30 | }, 31 | 32 | /** 33 | * @param {Rectangle} rect 34 | * #todo: use createPattern 35 | */ 36 | createSprite: function (rect) { 37 | var image, buf, xShift, yShift, x, y, xMax, yMax, crop, size, current, from, to; 38 | 39 | if (rect.width <= 0 || rect.height <= 0) { 40 | throw new TypeError('Wrong rectangle size'); 41 | } 42 | 43 | image = this.image; 44 | buf = LibCanvas.buffer(rect.width, rect.height, true); 45 | 46 | // если координаты выходят за левый/верхний край картинки 47 | if (rect.from.x < 0) xShift = Math.ceil(Math.abs(rect.from.x) / rect.width ); 48 | if (rect.from.y < 0) yShift = Math.ceil(Math.abs(rect.from.y) / rect.height); 49 | if (xShift || yShift) { 50 | rect = rect.clone().move(new Point( 51 | xShift * image.width, 52 | yShift * image.height 53 | )); 54 | } 55 | 56 | // для того, чтобы была возможность указывать ректангл, выходящий 57 | // за пределы картинки. текущая картинка повторяется как паттерн 58 | xMax = Math.ceil(rect.to.x / image.width ); 59 | yMax = Math.ceil(rect.to.y / image.height); 60 | for (y = yMax; y-- > 0;) for (x = xMax; x-- > 0;) { 61 | current = new Point(x * image.width, y * image.height); 62 | from = current.clone(); 63 | to = from.clone().move([image.width, image.height]); 64 | 65 | if (from.x < rect.from.x) from.x = rect.from.x; 66 | if (from.y < rect.from.y) from.y = rect.from.y; 67 | if ( to.x > rect. to .x) to.x = rect. to .x; 68 | if ( to.y > rect. to .y) to.y = rect. to .y; 69 | 70 | crop = new Rectangle(from, to); 71 | size = crop.size; 72 | crop.from.x %= image.width; 73 | crop.from.y %= image.height; 74 | crop.size = size; 75 | 76 | if (x) current.x -= rect.from.x; 77 | if (y) current.y -= rect.from.y; 78 | 79 | if (size.width && size.height) buf.ctx.drawImage({ 80 | image : image, 81 | crop : crop, 82 | draw : new Rectangle( current, size ) 83 | }); 84 | } 85 | 86 | return buf; 87 | }, 88 | 89 | toCanvas: function () { 90 | var cache = this.canvasCache; 91 | 92 | if (!cache) { 93 | cache = this.canvasCache = LibCanvas.buffer(this, true); 94 | cache.ctx.drawImage(this); 95 | } 96 | return cache; 97 | }, 98 | 99 | isLoaded: function () { 100 | return this.constructor.isLoaded( this.image ); 101 | }, 102 | 103 | sprite: function () { 104 | if (!this.isLoaded()) throw new Error('Not loaded in Image.sprite, logged'); 105 | 106 | if (arguments.length) { 107 | var 108 | rect = Rectangle(arguments), 109 | index = rect.dump(), 110 | cache = this.cache[index]; 111 | 112 | if (!cache) { 113 | cache = this.cache[index] = this.createSprite(rect); 114 | } 115 | return cache; 116 | } else { 117 | return this.toCanvas(); 118 | } 119 | } 120 | }); 121 | 122 | UtilsImage.own({ 123 | isLoaded : function (image) { 124 | return image.complete && ( (image.naturalWidth == null) || !!image.naturalWidth ); 125 | }, 126 | 127 | sprite: function (image, rectangle) { 128 | return this.mix(image).sprite(rectangle); 129 | }, 130 | 131 | toCanvas: function (image) { 132 | return this.mix(image).toCanvas(); 133 | }, 134 | 135 | mix: function (image) { 136 | var key = 'libcanvas.image'; 137 | 138 | if (!image[key]) { 139 | image[key] = new this(image); 140 | } 141 | 142 | return image[key]; 143 | } 144 | }); -------------------------------------------------------------------------------- /Source/Plugins/ImagePrototype.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Plugins.ImagePrototype" 5 | 6 | description: "Provides some Image extensions" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Utils.Image 18 | 19 | provides: Plugins.ImagePrototype 20 | 21 | ... 22 | */ 23 | 24 | // tag 25 | atom.core.append(HTMLImageElement.prototype, { 26 | createSprite: function (rect) { 27 | return UtilsImage.mix(this).createSprite(rect); 28 | }, 29 | toCanvas: function () { 30 | return UtilsImage.mix(this).toCanvas(); 31 | }, 32 | sprite : function () { 33 | var utils = UtilsImage.mix(this); 34 | 35 | return utils.sprite.apply( utils, arguments ); 36 | }, 37 | isLoaded : function () { 38 | return UtilsImage.isLoaded(this); 39 | } 40 | }); 41 | 42 | // mixin from image 43 | atom.core.append(HTMLCanvasElement.prototype, { 44 | createSprite : HTMLImageElement.prototype.createSprite, 45 | sprite : HTMLImageElement.prototype.sprite, 46 | isLoaded : atom.fn.lambda(true), 47 | toCanvas : atom.fn.lambda() 48 | }); -------------------------------------------------------------------------------- /Source/Shapes/Circle.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Shapes.Circle" 5 | 6 | description: "Provides circle as canvas object" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Point 18 | - Shape 19 | 20 | provides: Shapes.Circle 21 | 22 | ... 23 | */ 24 | 25 | /** @class Circle */ 26 | var Circle = LibCanvas.declare( 'LibCanvas.Shapes.Circle', 'Circle', Shape, { 27 | set : function () { 28 | var 29 | center, radius, 30 | a = atom.array.pickFrom(arguments); 31 | 32 | if (a.length >= 3) { 33 | center = new Point(a[0], a[1]); 34 | radius = a[2]; 35 | } else if (a.length == 2) { 36 | center = Point.from(a[0]); 37 | radius = a[1]; 38 | } else { 39 | a = a[0]; 40 | radius = a.r == null ? a.radius : a.r; 41 | if ('x' in a && 'y' in a) { 42 | center = new Point(a.x, a.y); 43 | } else if ('center' in a) { 44 | center = Point.from(a.center); 45 | } else if ('from' in a) { 46 | center = new Point(a.from).move({ 47 | x: this.radius, 48 | y: this.radius 49 | }); 50 | } 51 | } 52 | 53 | this.center = center; 54 | this.radius = radius; 55 | 56 | if (center == null) throw new TypeError('center is null'); 57 | if (radius == null) throw new TypeError('radius is null'); 58 | }, 59 | // we need accessors to redefine parent "get center" 60 | get center ( ) { return this._center; }, 61 | set center (c) { this._center = c; }, 62 | grow: function (size) { 63 | this.radius += size/2; 64 | return this; 65 | }, 66 | getCoords : function () { 67 | return this.center; 68 | }, 69 | hasPoint : function (point) { 70 | return this.center.checkDistanceTo(point, this.radius, true); 71 | }, 72 | scale : function (factor, pivot) { 73 | if (pivot) this.center.scale(factor, pivot); 74 | this.radius *= factor; 75 | return this; 76 | }, 77 | getCenter: function () { 78 | return this.center; 79 | }, 80 | intersect : function (obj) { 81 | if (obj instanceof this.constructor) { 82 | return this.center.checkDistanceTo(obj.center, this.radius + obj.radius, true); 83 | } else { 84 | return this.getBoundingRectangle().intersect( obj ); 85 | } 86 | }, 87 | move : function (distance, reverse) { 88 | this.center.move(distance, reverse); 89 | return this; 90 | }, 91 | processPath : function (ctx, noWrap) { 92 | if (!noWrap) ctx.beginPath(); 93 | if (this.radius) { 94 | ctx.arc({ 95 | circle : this, 96 | angle : [0, Math.PI * 2] 97 | }); 98 | } 99 | if (!noWrap) ctx.closePath(); 100 | return ctx; 101 | }, 102 | getBoundingRectangle: function () { 103 | var r = this.radius, center = this.center; 104 | return new Rectangle( 105 | new Point(center.x - r, center.y - r), 106 | new Point(center.x + r, center.y + r) 107 | ); 108 | }, 109 | clone : function () { 110 | return new this.constructor(this.center.clone(), this.radius); 111 | }, 112 | getPoints : function () { 113 | return { center : this.center }; 114 | }, 115 | equals : function (shape, accuracy) { 116 | return shape instanceof this.shape && 117 | shape.radius == this.radius && 118 | shape.center.equals(this.center, accuracy); 119 | }, 120 | dump: function () { 121 | return '[shape Circle(center['+this.center.x+', '+this.center.y+'], '+this.radius+')]'; 122 | } 123 | }); 124 | 125 | /** @private */ 126 | Circle.from = function (object) { 127 | if (object == null) return null; 128 | 129 | return object instanceof Circle ? object : new Circle(object); 130 | }; 131 | -------------------------------------------------------------------------------- /Source/Shapes/Ellipse.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Shapes.Ellipse" 5 | 6 | description: "Provides ellipse as canvas object" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Point 18 | - Shapes.Rectangle 19 | 20 | provides: Shapes.Ellipse 21 | 22 | ... 23 | */ 24 | 25 | /** @class Ellipse */ 26 | var Ellipse = LibCanvas.declare( 'LibCanvas.Shapes.Ellipse', 'Ellipse', Rectangle, { 27 | set: function () { 28 | this.bindMethods( 'update' ); 29 | Rectangle.prototype.set.apply(this, arguments); 30 | }, 31 | _angle : 0, 32 | get angle () { 33 | return this._angle; 34 | }, 35 | set angle (a) { 36 | if (this._angle == a) return; 37 | this._angle = atom.math.normalizeAngle(a); 38 | this.updateCache = true; 39 | }, 40 | update: function () { 41 | this.updateCache = true; 42 | }, 43 | rotate : function (degree) { 44 | this.angle += degree; 45 | return this; 46 | }, 47 | hasPoint : function () { 48 | var ctx = this.processPath( shapeTestBuffer().ctx ); 49 | return ctx.isPointInPath(Point(arguments)); 50 | }, 51 | cache : null, 52 | updateCache : true, 53 | countCache : function () { 54 | if (this.cache && !this.updateCache) { 55 | return this.cache; 56 | } 57 | 58 | if (this.cache === null) { 59 | this.cache = []; 60 | for (var i = 12; i--;) this.cache.push(new Point()); 61 | } 62 | var c = this.cache, 63 | angle = this._angle, 64 | kappa = .5522848, 65 | x = this.from.x, 66 | y = this.from.y, 67 | xe = this.to.x, 68 | ye = this.to.y, 69 | xm = (xe + x) / 2, 70 | ym = (ye + y) / 2, 71 | ox = (xe - x) / 2 * kappa, 72 | oy = (ye - y) / 2 * kappa; 73 | c[0].set(x, ym - oy); c[ 1].set(xm - ox, y); c[ 2].set(xm, y); 74 | c[3].set(xm + ox, y); c[ 4].set(xe, ym -oy); c[ 5].set(xe, ym); 75 | c[6].set(xe, ym +oy); c[ 7].set(xm +ox, ye); c[ 8].set(xm, ye); 76 | c[9].set(xm -ox, ye); c[10].set(x, ym + oy); c[11].set(x, ym); 77 | 78 | if (angle) { 79 | var center = new Point(xm, ym); 80 | for (i = c.length; i--;) c[i].rotate(angle, center); 81 | } 82 | 83 | return c; 84 | }, 85 | processPath : function (ctx, noWrap) { 86 | if (!noWrap) ctx.beginPath(); 87 | var c = this.countCache(); 88 | ctx.beginPath(c[11]) 89 | .bezierCurveTo(c[0], c[1], c[2]) 90 | .bezierCurveTo(c[3], c[4], c[5]) 91 | .bezierCurveTo(c[6], c[7], c[8]) 92 | .bezierCurveTo(c[9], c[10],c[11]); 93 | if (!noWrap) ctx.closePath(); 94 | return ctx; 95 | }, 96 | equals : function (shape, accuracy) { 97 | return Rectangle.prototype.equals.call( this, shape, accuracy ) && shape.angle == this.angle; 98 | }, 99 | draw : function (ctx, type) { 100 | this.processPath(ctx)[type](); 101 | return this; 102 | }, 103 | dump: function (name) { 104 | return Rectangle.prototype.dump.call(this, name || 'Ellipse'); 105 | } 106 | }); -------------------------------------------------------------------------------- /Source/Shapes/Line.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Shapes.Line" 5 | 6 | description: "Provides line as canvas object" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Point 18 | - Shape 19 | 20 | provides: Shapes.Line 21 | 22 | ... 23 | */ 24 | 25 | var Line = function () { 26 | 27 | var between = function (x, a, b, accuracy) { 28 | return atom.number.equals(x, a, accuracy) 29 | || atom.number.equals(x, b, accuracy) 30 | || (a < x && x < b) 31 | || (b < x && x < a); 32 | }; 33 | 34 | var halfPi = Math.PI/2; 35 | 36 | /** @class Line */ 37 | return LibCanvas.declare( 'LibCanvas.Shapes.Line', 'Line', Shape, { 38 | set : function (from, to) { 39 | var a = atom.array.pickFrom(arguments); 40 | 41 | if (a.length === 4) { 42 | this.from = new Point( a[0], a[1] ); 43 | this.to = new Point( a[2], a[3] ); 44 | } else { 45 | this.from = Point.from(a[0] || a.from); 46 | this.to = Point.from(a[1] || a.to); 47 | } 48 | 49 | return this; 50 | }, 51 | hasPoint : function (point) { 52 | var fx = this.from.x, 53 | fy = this.from.y, 54 | tx = this.to.x, 55 | ty = this.to.y, 56 | px = point.x, 57 | py = point.y; 58 | 59 | if (!( atom.number.between(point.x, Math.min(fx, tx), Math.max(fx, tx)) 60 | && atom.number.between(point.y, Math.min(fy, ty), Math.max(fy, ty)) 61 | )) return false; 62 | 63 | // if triangle square is zero - points are on one line 64 | return atom.number.round(((fx-px)*(ty-py)-(tx-px)*(fy-py)), 6) == 0; 65 | }, 66 | getBoundingRectangle: function () { 67 | return new Rectangle(this.from, this.to).fillToPixel().grow(2); 68 | }, 69 | intersect: function (line, point, accuracy) { 70 | if (line.constructor != this.constructor) { 71 | return this.getBoundingRectangle().intersect( line ); 72 | } 73 | var a = this.from, b = this.to, c = line.from, d = line.to, x, y, FALSE = point ? null : false; 74 | if (atom.number.equals(d.x, c.x, accuracy)) { // DC == vertical line 75 | if (atom.number.equals(b.x, a.x, accuracy)) { 76 | if (atom.number.equals(a.x, d.x, accuracy)) { 77 | if (atom.number.between(a.y, c.y, d.y)) { 78 | return a.clone(); 79 | } else if (atom.number.between(b.y, c.y, d.y)) { 80 | return b.clone(); 81 | } else { 82 | return FALSE; 83 | } 84 | } else { 85 | return FALSE; 86 | } 87 | } 88 | x = d.x; 89 | y = b.y + (x-b.x)*(a.y-b.y)/(a.x-b.x); 90 | } else { 91 | x = ((a.x*b.y - b.x*a.y)*(d.x-c.x)-(c.x*d.y - d.x*c.y)*(b.x-a.x))/((a.y-b.y)*(d.x-c.x)-(c.y-d.y)*(b.x-a.x)); 92 | y = ((c.y-d.y)*x-(c.x*d.y-d.x*c.y))/(d.x-c.x); 93 | x *= -1; 94 | } 95 | 96 | if (!between(x, a.x, b.x, accuracy)) return FALSE; 97 | if (!between(y, a.y, b.y, accuracy)) return FALSE; 98 | if (!between(x, c.x, d.x, accuracy)) return FALSE; 99 | if (!between(y, c.y, d.y, accuracy)) return FALSE; 100 | 101 | return point ? new Point(x, y) : true; 102 | }, 103 | perpendicular: function (point) { 104 | point = Point( point ); 105 | var 106 | fX = this.from.x, 107 | fY = this.from.y, 108 | tX = this.to.x, 109 | tY = this.to.y, 110 | pX = point.x, 111 | pY = point.y, 112 | dX = (tX-fX) * (tX-fX), 113 | dY = (tY-fY) * (tY-fY), 114 | rX = ((tX-fX)*(tY-fY)*(pY-fY)+fX*dY+pX*dX) / (dX+dY), 115 | rY = (tY-fY)*(rX-fX)/(tX-fX)+fY; 116 | 117 | return new Point( rX, rY ); 118 | }, 119 | distanceTo: function (p, asInfiniteLine) { 120 | p = Point(p); 121 | var f = this.from, t = this.to, angle, s, x, y; 122 | 123 | if (!asInfiniteLine) { 124 | angle = Math.atan2(p.x - t.x, p.y - t.y); 125 | if ( atom.number.between(angle, -halfPi, halfPi) ) { 126 | return t.distanceTo( p ); 127 | } 128 | 129 | angle = Math.atan2(f.x - p.x, f.y - p.y); 130 | if ( atom.number.between(angle, -halfPi, halfPi) ) { 131 | return f.distanceTo( p ); 132 | } 133 | } 134 | 135 | s = Math.abs( 136 | f.x * (t.y - p.y) + 137 | t.x * (p.y - f.y) + 138 | p.x * (f.y - t.y) 139 | ) / 2; 140 | 141 | x = f.x - t.x; 142 | y = f.y - t.y; 143 | return 2 * s / Math.sqrt(x*x+y*y); 144 | }, 145 | get length () { 146 | return this.to.distanceTo(this.from); 147 | }, 148 | getLength : function () { 149 | return this.length; 150 | }, 151 | draw : function (ctx, type) { 152 | ctx.beginPath(); 153 | this.processPath(ctx, true)[type](); 154 | ctx.closePath(); 155 | return this; 156 | }, 157 | processPath : function (ctx, noWrap) { 158 | if (!noWrap) ctx.beginPath(); 159 | ctx.moveTo(this.from).lineTo(this.to); 160 | if (!noWrap) ctx.closePath(); 161 | return ctx; 162 | }, 163 | dump: function () { 164 | return Shape.prototype.dump.call(this, 'Line'); 165 | } 166 | }); 167 | 168 | }(); 169 | -------------------------------------------------------------------------------- /Source/Shapes/Path.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Shapes.Path" 5 | 6 | description: "Provides Path as canvas object" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Point 18 | - Shape 19 | - Shapes.Polygon 20 | 21 | provides: Shapes.Path 22 | 23 | ... 24 | */ 25 | 26 | /** 27 | * @class Path 28 | * @extends Polygon: 29 | * get center 30 | * draw() 31 | * move(distance, reverse) 32 | * scale(power, pivot) 33 | * rotate(angle, pivot) 34 | * getBoundingRectangle() 35 | * [empty] grow() 36 | */ 37 | var Path = LibCanvas.declare( 'LibCanvas.Shapes.Path', 'Path', Polygon, { 38 | parts: [], 39 | 40 | initialize : function (parts) { 41 | this.parts = []; 42 | 43 | if (parts) this.set(parts); 44 | }, 45 | 46 | set : function (parts) { 47 | this.parts.length = 0; 48 | 49 | if (Array.isArray(parts)) { 50 | for (var i = 0, l = parts.length; i < l; i++) { 51 | this.push(parts[i]); 52 | } 53 | } 54 | }, 55 | 56 | get length () { 57 | return this.parts.length; 58 | }, 59 | 60 | // methods 61 | moveTo: function (point) { 62 | return this.push('moveTo', [ Point.from(point) ]); 63 | }, 64 | lineTo: function (point) { 65 | return this.push('lineTo', [ Point.from(point) ]); 66 | }, 67 | curveTo: function (to, cp1, cp2) { 68 | var points = atom.array.pickFrom(arguments).map(Point); 69 | return this.push('curveTo', points); 70 | }, 71 | 72 | // queue/stack 73 | push : function (method, points) { 74 | this.parts.push(Path.Part.from(method, points)); 75 | return this; 76 | }, 77 | unshift: function (method, points) { 78 | this.parts.unshift(Path.Part.from(method, points)); 79 | return this; 80 | }, 81 | pop : function () { 82 | return this.parts.pop(); 83 | }, 84 | shift: function () { 85 | return this.parts.shift(); 86 | }, 87 | 88 | processPath : function (ctx, noWrap) { 89 | if (!noWrap) ctx.beginPath(); 90 | this.forEach(function (part) { 91 | ctx[part.method].apply(ctx, part.points); 92 | }); 93 | if (!noWrap) ctx.closePath(); 94 | return ctx; 95 | }, 96 | 97 | intersect: function (obj) { 98 | return this.getBoundingRectangle() 99 | .intersect( obj.getBoundingRectangle() ); 100 | }, 101 | 102 | forEach: function (fn) { 103 | var parts = this.parts, i = 0, l = parts.length; 104 | while (i < l) { 105 | fn.call( this, parts[i++], i, this ); 106 | } 107 | return this; 108 | }, 109 | 110 | get points () { 111 | var points = []; 112 | this.forEach(function (part) { 113 | for (var i = 0, l = part.points.length; i < l; i++) { 114 | atom.array.include(points, part.points[i]); 115 | } 116 | }); 117 | return points; 118 | }, 119 | 120 | hasPoint : function (point) { 121 | var ctx = shapeTestBuffer().ctx; 122 | this.processPath(ctx); 123 | return ctx.isPointInPath(Point.from(point)); 124 | }, 125 | clone: function () { 126 | return new this.constructor( 127 | this.parts.invoke('clone') 128 | ); 129 | } 130 | }); 131 | /** @class Path.Part */ 132 | atom.declare('LibCanvas.Shapes.Path.Part', { 133 | initialize: function (method, points) { 134 | this.method = method; 135 | this.points = points.map(Point); 136 | }, 137 | 138 | clone: function () { 139 | return new this.constructor( 140 | this.method, 141 | this.points.invoke('clone') 142 | ); 143 | } 144 | }).own({ 145 | from: function (method, args) { 146 | if (method == null) { 147 | throw new Error('Empty path method'); 148 | } 149 | 150 | if (typeof method == 'string') { 151 | return new this(method, args) ; 152 | } else if (atom.core.isArrayLike(method)) { 153 | return new this(method[0], args[1]); 154 | } else { 155 | return this; 156 | } 157 | } 158 | }); -------------------------------------------------------------------------------- /Source/Shapes/Polygon.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Shapes.Polygon" 5 | 6 | description: "Provides user-defined concave polygon as canvas object" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Point 18 | - Shape 19 | - Shapes.Line 20 | 21 | provides: Shapes.Polygon 22 | 23 | ... 24 | */ 25 | 26 | /** @class Polygon */ 27 | var Polygon = LibCanvas.declare( 'LibCanvas.Shapes.Polygon', 'Polygon', Shape, { 28 | initialize: function method () { 29 | this.points = []; 30 | this._lines = []; 31 | method.previous.apply(this, arguments); 32 | }, 33 | set : function (poly) { 34 | this.points.length = 0; 35 | 36 | var source = Array.isArray(poly) ? poly : atom.core.toArray(arguments); 37 | 38 | atom.array.append( this.points, 39 | source 40 | .filter(Boolean) 41 | .map(Point) 42 | ); 43 | 44 | this._lines.length = 0; 45 | 46 | return this; 47 | }, 48 | get length () { 49 | return this.points.length; 50 | }, 51 | get lines () { 52 | var 53 | lines = this._lines, 54 | p = this.points, 55 | l = p.length, 56 | i = 0; 57 | 58 | if (lines.length != l) for (;i < l; i++) { 59 | lines.push( new Line( p[i], i+1 == l ? p[0] : p[i+1] ) ); 60 | } 61 | 62 | return this._lines; 63 | }, 64 | get center () { 65 | return new Point().mean(this.points); 66 | }, 67 | get: function (index) { 68 | return this.points[index]; 69 | }, 70 | getCoords : function () { 71 | return this.points[0]; 72 | }, 73 | processPath : function (ctx, noWrap) { 74 | var p = this.points, i = 0, l = p.length; 75 | 76 | if (!noWrap) ctx.beginPath(); 77 | for (; i <= l; i++) { 78 | if (i == 0) { 79 | ctx.moveTo(p[i]); 80 | } else { 81 | ctx.lineTo(p[i == l ? 0 : i]); 82 | } 83 | } 84 | if (!noWrap) ctx.closePath(); 85 | 86 | return ctx; 87 | }, 88 | 89 | grow: function () { return this; }, 90 | 91 | getBoundingRectangle: function () { 92 | var p = this.points, l = p.length, from, to; 93 | 94 | if (l == 0) { 95 | throw new Error('Shape is empty'); 96 | } 97 | 98 | while (l--) { 99 | 100 | if (from) { 101 | from.x = Math.min( from.x, p[l].x ); 102 | from.y = Math.min( from.y, p[l].y ); 103 | to.x = Math.max( to.x, p[l].x ); 104 | to.y = Math.max( to.y, p[l].y ); 105 | } else { 106 | from = p[l].clone(); 107 | to = p[l].clone(); 108 | } 109 | 110 | } 111 | 112 | return new Rectangle( from, to ); 113 | }, 114 | 115 | // points invoking 116 | move : function (distance, reverse) { 117 | return this.invoke('move', distance, reverse) 118 | }, 119 | rotate : function (angle, pivot) { 120 | return this.invoke('rotate', angle, pivot) 121 | }, 122 | scale : function (power, pivot) { 123 | return this.invoke('scale', power, pivot) 124 | }, 125 | invoke: function (method, args) { 126 | args = Array.prototype.slice.call(arguments, 1); 127 | 128 | this.points.map(function (point) { 129 | point[method].apply(point, args); 130 | }); 131 | return this; 132 | }, 133 | forEach : function (fn) { 134 | this.points.forEach(fn); 135 | return this; 136 | }, 137 | each: function (fn, context) { 138 | return this.forEach(context ? fn.bind(context) : fn); 139 | }, 140 | 141 | hasPoint : function (point) { 142 | point = Point.from(point); 143 | 144 | var result = false, points = this.points; 145 | for (var i = 0, l = this.length; i < l; i++) { 146 | var k = (i || l) - 1, I = points[i], K = points[k]; 147 | if ( 148 | (atom.number.between(point.y, I.y , K.y, "L") 149 | || atom.number.between(point.y, K.y , I.y, "L") 150 | ) && point.x < (K.x - I.x) * (point.y -I.y) / (K.y - I.y) + I.x 151 | ) { 152 | result = !result; 153 | } 154 | } 155 | return result; 156 | }, 157 | intersect : function (poly) { 158 | if (poly.constructor != this.constructor) { 159 | return this.getBoundingRectangle().intersect( poly ); 160 | } 161 | 162 | var tL = this.lines, pL = poly.lines, i = tL.length, k = pL.length; 163 | while (i-- > 0) for (k = pL.length; k-- > 0;) { 164 | if (tL[i].intersect(pL[k])) return true; 165 | } 166 | return false; 167 | }, 168 | getPoints : function () { 169 | return atom.array.toHash(this.points); 170 | }, 171 | clone: function () { 172 | return new this.constructor( atom.array.invoke(this.points, 'clone') ); 173 | } 174 | }); -------------------------------------------------------------------------------- /Source/Shapes/RoundedRectangle.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "Shapes.RoundedRectangle" 5 | 6 | description: "Provides rounded rectangle as canvas object" 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - "Shock " 14 | 15 | requires: 16 | - LibCanvas 17 | - Shapes.Rectangle 18 | 19 | provides: Shapes.RoundedRectangle 20 | 21 | ... 22 | */ 23 | 24 | /** @class RoundedRectangle */ 25 | var RoundedRectangle = LibCanvas.declare( 26 | 'LibCanvas.Shapes.RoundedRectangle', 'RoundedRectangle', Rectangle, { 27 | radius: 0, 28 | 29 | setRadius: function (value) { 30 | this.radius = value; 31 | return this; 32 | }, 33 | draw : Shape.prototype.draw, 34 | processPath : function (ctx, noWrap) { 35 | var from = this.from, to = this.to, radius = this.radius; 36 | if (!noWrap) ctx.beginPath(); 37 | ctx 38 | .moveTo (from.x, from.y+radius) 39 | .lineTo (from.x, to.y-radius) 40 | .curveTo(from.x, to.y, from.x + radius, to.y) 41 | .lineTo (to.x-radius, to.y) 42 | .curveTo(to.x,to.y, to.x,to.y-radius) 43 | .lineTo (to.x, from.y+radius) 44 | .curveTo(to.x, from.y, to.x-radius, from.y) 45 | .lineTo (from.x+radius, from.y) 46 | .curveTo(from.x,from.y,from.x,from.y+radius); 47 | if (!noWrap) ctx.closePath(); 48 | return ctx; 49 | }, 50 | 51 | 52 | clone: function method () { 53 | return method.previous 54 | .apply(this, arguments) 55 | .setRadius(this.radius); 56 | }, 57 | 58 | equals: function (shape, accuracy) { 59 | return Rectangle.prototype.equals.call( this, shape, accuracy ) && shape.radius == this.radius; 60 | }, 61 | 62 | dump: function () { 63 | var p = function (p) { return '[' + p.x + ', ' + p.y + ']'; }; 64 | return '[shape RoundedRectangle(from'+p(this.from)+', to'+p(this.to)+', radius='+this.radius+')]'; 65 | } 66 | }); -------------------------------------------------------------------------------- /Source/overall.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | 4 | name: "LibCanvas" 5 | 6 | description: "LibCanvas - free javascript library, based on AtomJS framework." 7 | 8 | license: 9 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 10 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 11 | 12 | authors: 13 | - Pavel Ponomarenko aka Shock 14 | 15 | ... 16 | */ 17 | 18 | (function (atom, Math) { // LibCanvas 19 | 20 | // bug in Safari 5.1 ( 'use strict' + 'set prop' ) 21 | // 'use strict'; 22 | 23 | var undefined, 24 | /** @global {Object} global */ 25 | global = this, 26 | /** @global {Function} slice */ 27 | slice = [].slice, 28 | /** @global {Function} declare */ 29 | declare = atom.declare, 30 | /** @global {Function} Registry */ 31 | Registry = atom.Registry, 32 | /** @global {Function} Events */ 33 | Events = atom.Events, 34 | /** @global {Function} Settings */ 35 | Settings = atom.Settings; 36 | /*** [Code] ***/ 37 | 38 | }).call(typeof window == 'undefined' ? exports : window, atom, Math); -------------------------------------------------------------------------------- /Source/package.yml: -------------------------------------------------------------------------------- 1 | name: "LibCanvas" 2 | 3 | exports: "libcanvas.js" 4 | 5 | web: "[libcanvas.com](http://libcanvas.com/)" 6 | 7 | description: "LibCanvas - html5 canvas framework" 8 | 9 | license: 10 | - "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)" 11 | - "[MIT License](http://opensource.org/licenses/mit-license.php)" 12 | 13 | copyright: "© [LibCanvas](http://libcanvas.com)" 14 | 15 | authors: "[LibCanvas Development Team](http://libcanvas.com)" 16 | 17 | sources: "*.js" 18 | 19 | overall: "overall.js" --------------------------------------------------------------------------------