1 /** 2 * The MIT License (MIT) 3 * 4 * Copyright (c) 2016 DeNA Co., Ltd. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 /// <reference path="base.js"/> 26 /// <reference path="display_object.js"/> 27 /// <reference path="graphics.js"/> 28 /// <reference path="config.js"/> 29 30 /** 31 * A class that represents a vector shape. 32 * @param {createjs.Graphics=} opt_graphics 33 * @extends {createjs.DisplayObject} 34 * @constructor 35 */ 36 createjs.Shape = function(opt_graphics) { 37 createjs.DisplayObject.call(this); 38 39 /** 40 * The createjs.Graphics object that actually represents this shape. 41 * @type {createjs.Graphics} 42 * @private 43 */ 44 this.graphics_ = opt_graphics ? opt_graphics : new createjs.Graphics(); 45 }; 46 createjs.inherits('Shape', createjs.Shape, createjs.DisplayObject); 47 48 /** 49 * Createjs.Graphics objects to be set by a tween attached to this shape. 50 * @type {Array.<createjs.Graphics>} 51 * @private 52 */ 53 createjs.Shape.prototype.masks_ = null; 54 55 /** 56 * The position where this shape was hit-tested last time. 57 * @type {createjs.Point} 58 * @private 59 */ 60 createjs.Shape.prototype.hitTestPoint_ = null; 61 62 /** 63 * The result of the last hit-testing. 64 * @type {createjs.DisplayObject} 65 * @private 66 */ 67 createjs.Shape.prototype.hitTestResult_ = null; 68 69 /** 70 * Returns the createjs.Graphics object owned by this object. 71 * @return {createjs.Graphics} 72 * @const 73 */ 74 createjs.Shape.prototype.getGraphics = function() { 75 /// <returns type="createjs.Graphics"/> 76 return this.graphics_; 77 }; 78 79 /** 80 * Sets the createjs.Graphics object. 81 * @param {createjs.Graphics} graphics 82 * @const 83 */ 84 createjs.Shape.prototype.setGraphics = function(graphics) { 85 /// <param type="createjs.Graphics" name="graphics"/> 86 var owners = this.getOwners(); 87 if (!owners) { 88 return; 89 } 90 var composition = this.getCompositionId(); 91 if (composition) { 92 // A mask uses its bounding box in the global coordinate system to render 93 // its owners. Force its owners to calculate it. (Even though multiple 94 // owners can share one mask, they create copies of the bounding box of the 95 // mask to convert them to the global coordinate system. That is, it is safe 96 // for multiple owners to share one mask.) 97 this.graphics_ = graphics; 98 if (graphics) { 99 var box = graphics.box; 100 this.setBoundingBox(box.minX, box.minY, box.maxX, box.maxY); 101 for (var i = 0; i < owners.length; ++i) { 102 owners[i].setDirty(createjs.DisplayObject.DIRTY_MASK); 103 } 104 } 105 } 106 }; 107 108 /** @override */ 109 createjs.Shape.prototype.handleAttach = function(flag) { 110 /// <param type="number" name="flag"/> 111 var graphics = this.getGraphics(); 112 if (flag) { 113 // Cache the associated Graphics object when this shape is added to an 114 // object tree. (A CreateJS object generated by Flash CC adds child shapes 115 // to its node list in its constructor, i.e. this code caches all child 116 // shapes of a CreateJS object there.) 117 graphics.cache(flag); 118 if ((flag & 2) && this.masks_) { 119 // Cache masks the first time when a display object has a mask attached, 120 // i.e. "object.mask = shape" is executed. 121 for (var i = 0; i < this.masks_.length; ++i) { 122 this.masks_[i].cache(flag); 123 } 124 } 125 } 126 var box = graphics.box; 127 this.setBoundingBox(box.minX, box.minY, box.maxX, box.maxY); 128 }; 129 130 /** @override */ 131 createjs.Shape.prototype.removeAllChildren = function(opt_destroy) { 132 /// <param type="boolean" optional="true" name="opt_destroy"/> 133 this.handleDetach(); 134 }; 135 136 /** @override */ 137 createjs.Shape.prototype.handleDetach = function() { 138 if (this.graphics_) { 139 this.graphics_.uncache(); 140 this.graphics_ = null; 141 } 142 if (this.masks_) { 143 for (var i = 0; i < this.masks_.length; ++i) { 144 this.masks_[i].uncache(); 145 } 146 this.masks_ = null; 147 } 148 }; 149 150 if (createjs.USE_PIXEL_TEST) { 151 /** @override */ 152 createjs.Shape.prototype.hitTestObject = function(point, types, bubble) { 153 var object = createjs.DisplayObject.prototype.hitTestObject.call( 154 this, point, types, bubble); 155 if (object && this.graphics_) { 156 // Return the cached result if the given point is sufficiently close to 157 // the last one. This method is often called twice with the same position 158 // when a user taps on this shape: one is for a 'touchdown' event, and the 159 // other is for a 'touchup' event. This cache avoids reading the pixels of 160 // its createjs.Graphics object twice. 161 if (this.hitTestPoint_) { 162 var dx = this.hitTestPoint_.x - point.x; 163 var dy = this.hitTestPoint_.y - point.y; 164 if (dx * dx + dy * dy <= 4) { 165 return this.hitTestResult_; 166 } 167 this.hitTestPoint_.x = point.x; 168 this.hitTestPoint_.y = point.y; 169 } else { 170 this.hitTestPoint_ = new createjs.Point(point.x, point.y); 171 } 172 // Read the pixel at the specified position. 173 var local = new createjs.Point(point.x, point.y); 174 this.getInverse().transformPoint(local); 175 if (!this.graphics_.hitTestObject(local)) { 176 object = null; 177 } 178 this.hitTestResult_ = object; 179 } 180 return object; 181 }; 182 } 183 184 /** @override */ 185 createjs.Shape.prototype.layout = 186 function(renderer, parent, dirty, time, draw) { 187 /// <param type="createjs.Renderer" name="renderer"/> 188 /// <param type="createjs.DisplayObject" name="parent"/> 189 /// <param type="number" name="dirty"/> 190 /// <param type="number" name="time"/> 191 /// <param type="number" name="draw"/> 192 var graphics = this.graphics_; 193 if (!graphics) { 194 return 0; 195 } 196 if (graphics.isDirty()) { 197 this.setDirty(createjs.DisplayObject.DIRTY_SHAPE); 198 var box = graphics.box; 199 this.setBoundingBox(box.minX, box.minY, box.maxX, box.maxY); 200 } 201 return createjs.Shape.superClass_.layout.call( 202 this, renderer, parent, dirty, time, draw); 203 }; 204 205 /** @override */ 206 createjs.Shape.prototype.paintObject = function(renderer) { 207 /// <returns type="createjs.Renderer"/> 208 this.graphics_.paint(renderer); 209 }; 210 211 /** @override */ 212 createjs.Shape.prototype.isVisible = function() { 213 /// <returns type="boolean"/> 214 return !!this.graphics_ && !this.graphics_.isEmpty() && 215 createjs.Shape.superClass_.isVisible.call(this); 216 }; 217 218 /** @override */ 219 createjs.Shape.prototype.set = function(properties) { 220 createjs.Shape.superClass_.set.call(this, properties); 221 var value = properties['graphics']; 222 if (value) { 223 this.setGraphics(/** @type {createjs.Graphics} */ (value)); 224 } 225 return this; 226 }; 227 228 /** @override */ 229 createjs.Shape.prototype.addGraphics = function(graphics) { 230 /// <param type="createjs.Graphics" name="graphics"/> 231 // Add the given graphics object to a list so this shape can create their 232 // cache bitmaps when it is a mask and it has to clip its owners. 233 if (graphics) { 234 if (!this.masks_) { 235 this.masks_ = []; 236 } 237 this.masks_.push(graphics); 238 } 239 }; 240 241 /** @override */ 242 createjs.Shape.prototype.getSetters = function() { 243 /// <return type="Object" elementType="createjs.TweenTarget.Setter"/> 244 var setters = createjs.Shape.superClass_.getSetters.call(this); 245 setters['graphics'].setGraphics(this.graphics_); 246 return setters; 247 }; 248 249 // Add a setter to allow tweens to change this object. 250 createjs.TweenTarget.Property.addSetters({ 251 'graphics': createjs.Shape.prototype.setGraphics 252 }); 253 254 // Adds a getter and a setter for applications to access internal variables. 255 Object.defineProperties(createjs.Shape.prototype, { 256 'graphics': { 257 get: createjs.Shape.prototype.getGraphics, 258 set: createjs.Shape.prototype.setGraphics 259 } 260 }); 261 262 // Export the createjs.Shape object to the global namespace. 263 createjs.exportObject('createjs.Shape', createjs.Shape, { 264 // createjs.Object methods. 265 }); 266