/*
* Graphics
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
// namespace:
this.createjs = this.createjs||{};
(function() {
/**
* Inner class used by the {{#crossLink "Graphics"}}{{/crossLink}} class. Used to create the instruction lists used in Graphics:
* @class Command
* @protected
* @constructor
**/
function Command(f, params, path) {
this.f = f;
this.params = params;
this.path = path==null ? true : path;
}
/**
* @method exec
* @protected
* @param {Object} scope
**/
Command.prototype.exec = function(scope) { this.f.apply(scope, this.params); }
/**
* The Graphics class exposes an easy to use API for generating vector drawing instructions and drawing them to a
* specified context. Note that you can use Graphics without any dependency on the Easel framework by calling {{#crossLink "DisplayObject/draw"}}{{/crossLink}}
* directly, or it can be used with the {{#crossLink "Shape"}}{{/crossLink}} object to draw vector graphics within the
* context of an Easel display list.
*
*
Example
* var g = new Graphics();
* g.setStrokeStyle(1);
* g.beginStroke(Graphics.getRGB(0,0,0));
* g.beginFill(Graphics.getRGB(255,0,0));
* g.drawCircle(0,0,3);
*
* var s = new Shape(g);
* s.x = 100;
* s.y = 100;
*
* stage.addChild(s);
* stage.update();
*
* Note that all drawing methods in Graphics return the Graphics instance, so they can be chained together. For example,
* the following line of code would generate the instructions to draw a rectangle with a red stroke and blue fill, then
* render it to the specified context2D:
*
* myGraphics.beginStroke("#F00").beginFill("#00F").drawRect(20, 20, 100, 50).draw(myContext2D);
*
* Tiny API
* The Graphics class also includes a "tiny API", which is one or two-letter methods that are shortcuts for all of the
* Graphics methods. These methods are great for creating compact instructions, and is used by the Toolkit for CreateJS
* to generate readable code. All tiny methods are marked as protected, so you can view them by enabling protected
* descriptions in the docs.
*
*
* Tiny | Method | Tiny | Method |
* mt | {{#crossLink "Graphics/moveTo"}}{{/crossLink}} |
* lt | {{#crossLink "Graphics/lineTo"}}{{/crossLink}} |
* at | {{#crossLink "Graphics/arcTo"}}{{/crossLink}} |
* bt | {{#crossLink "Graphics/bezierCurveTo"}}{{/crossLink}} |
* qt | {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} (also curveTo) |
* r | {{#crossLink "Graphics/rect"}}{{/crossLink}} |
* cp | {{#crossLink "Graphics/closePath"}}{{/crossLink}} |
* c | {{#crossLink "Graphics/clear"}}{{/crossLink}} |
* f | {{#crossLink "Graphics/beginFill"}}{{/crossLink}} |
* lf | {{#crossLink "Graphics/beginLinearGradientFill"}}{{/crossLink}} |
* rf | {{#crossLink "Graphics/beginRadialGradientFill"}}{{/crossLink}} |
* bf | {{#crossLink "Graphics/beginBitmapFill"}}{{/crossLink}} |
* ef | {{#crossLink "Graphics/endFill"}}{{/crossLink}} |
* ss | {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} |
* s | {{#crossLink "Graphics/beginStroke"}}{{/crossLink}} |
* ls | {{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}} |
* rs | {{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} |
* bs | {{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}} |
* es | {{#crossLink "Graphics/endStroke"}}{{/crossLink}} |
* dr | {{#crossLink "Graphics/drawRect"}}{{/crossLink}} |
* rr | {{#crossLink "Graphics/drawRoundRect"}}{{/crossLink}} |
* rc | {{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}} |
* dc | {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} |
* de | {{#crossLink "Graphics/drawEllipse"}}{{/crossLink}} |
* dp | {{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} |
* p | {{#crossLink "Graphics/decodePath"}}{{/crossLink}} |
*
*
* Here is the above example, using the tiny API instead.
*
* myGraphics.s("#F00").f("#00F").r(20, 20, 100, 50).draw(myContext2D);
*
* @class Graphics
* @constructor
* @for Graphics
**/
var Graphics = function() {
this.initialize();
};
var p = Graphics.prototype;
// static public methods:
/**
* Returns a CSS compatible color string based on the specified RGB numeric color values in the format
* "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)". For example,
*
* Graphics.getRGB(50, 100, 150, 0.5);
*
* will return "rgba(50,100,150,0.5)". It also supports passing a single hex color value as the first param, and an
* optional alpha value as the second param. For example,
*
* Graphics.getRGB(0xFF00FF, 0.2);
*
* will return "rgba(255,0,255,0.2)".
* @method getRGB
* @static
* @param {Number} r The red component for the color, between 0 and 0xFF (255).
* @param {Number} g The green component for the color, between 0 and 0xFF (255).
* @param {Number} b The blue component for the color, between 0 and 0xFF (255).
* @param {Number} alpha Optional. The alpha component for the color where 0 is fully transparent and 1 is fully opaque.
* @return {String} A CSS compatible color string based on the specified RGB numeric color values in the format
* "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)".
**/
Graphics.getRGB = function(r, g, b, alpha) {
if (r != null && b == null) {
alpha = g;
b = r&0xFF;
g = r>>8&0xFF;
r = r>>16&0xFF;
}
if (alpha == null) {
return "rgb("+r+","+g+","+b+")";
} else {
return "rgba("+r+","+g+","+b+","+alpha+")";
}
};
/**
* Returns a CSS compatible color string based on the specified HSL numeric color values in the format "hsla(360,100,100,1.0)",
* or if alpha is null then in the format "hsl(360,100,100)". For example, this will return "hsl(150,100,70)".
*
* Graphics.getHSL(150, 100, 70);
*
* @method getHSL
* @static
* @param {Number} hue The hue component for the color, between 0 and 360.
* @param {Number} saturation The saturation component for the color, between 0 and 100.
* @param {Number} lightness The lightness component for the color, between 0 and 100.
* @param {Number} alpha Optional. The alpha component for the color where 0 is fully transparent and 1 is fully opaque.
* @return {String} A CSS compatible color string based on the specified HSL numeric color values in the format
* "hsla(360,100,100,1.0)", or if alpha is null then in the format "hsl(360,100,100)".
**/
Graphics.getHSL = function(hue, saturation, lightness, alpha) {
if (alpha == null) {
return "hsl("+(hue%360)+","+saturation+"%,"+lightness+"%)";
} else {
return "hsla("+(hue%360)+","+saturation+"%,"+lightness+"%,"+alpha+")";
}
};
/**
* Map of Base64 characters to values. Used by {{#crossLink "Graphics/decodePath"}}{{/crossLink}}.
* @property BASE_64
* @static
* @final
* @type {Object}
**/
Graphics.BASE_64 = {"A":0,"B":1,"C":2,"D":3,"E":4,"F":5,"G":6,"H":7,"I":8,"J":9,"K":10,"L":11,"M":12,"N":13,"O":14,"P":15,"Q":16,"R":17,"S":18,"T":19,"U":20,"V":21,"W":22,"X":23,"Y":24,"Z":25,"a":26,"b":27,"c":28,"d":29,"e":30,"f":31,"g":32,"h":33,"i":34,"j":35,"k":36,"l":37,"m":38,"n":39,"o":40,"p":41,"q":42,"r":43,"s":44,"t":45,"u":46,"v":47,"w":48,"x":49,"y":50,"z":51,"0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"+":62,"/":63};
/**
* Maps numeric values for the caps parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to
* corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to
* "butt", 1 to "round", and 2 to "square".
* For example, to set the line caps to "square":
*
* myGraphics.ss(16, 2);
*
* @property STROKE_CAPS_MAP
* @static
* @final
* @type {Array}
**/
Graphics.STROKE_CAPS_MAP = ["butt", "round", "square"];
/**
* Maps numeric values for the joints parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to
* corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to
* "miter", 1 to "round", and 2 to "bevel".
* For example, to set the line joints to "bevel":
* myGraphics.ss(16, 0, 2);
*
* @property STROKE_JOINTS_MAP
* @static
* @final
* @type {Array}
**/
Graphics.STROKE_JOINTS_MAP = ["miter", "round", "bevel"];
/**
* @property _ctx
* @static
* @protected
* @type {CanvasRenderingContext2D}
**/
Graphics._ctx = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas")).getContext("2d");
/**
* @property beginCmd
* @static
* @protected
* @type {Command}
**/
Graphics.beginCmd = new Command(Graphics._ctx.beginPath, [], false);
/**
* @property fillCmd
* @static
* @protected
* @type {Command}
**/
Graphics.fillCmd = new Command(Graphics._ctx.fill, [], false);
/**
* @property strokeCmd
* @static
* @protected
* @type {Command}
**/
Graphics.strokeCmd = new Command(Graphics._ctx.stroke, [], false);
// public properties
// private properties
/**
* @property _strokeInstructions
* @protected
* @type {Array}
**/
p._strokeInstructions = null;
/**
* @property _strokeStyleInstructions
* @protected
* @type {Array}
**/
p._strokeStyleInstructions = null;
/**
* @property _ignoreScaleStroke
* @protected
* @type Boolean
**/
p._ignoreScaleStroke = false;
/**
* @property _fillInstructions
* @protected
* @type {Array}
**/
p._fillInstructions = null;
/**
* @property _instructions
* @protected
* @type {Array}
**/
p._instructions = null;
/**
* @property _oldInstructions
* @protected
* @type {Array}
**/
p._oldInstructions = null;
/**
* @property _activeInstructions
* @protected
* @type {Array}
**/
p._activeInstructions = null;
/**
* @property _active
* @protected
* @type {Boolean}
* @default false
**/
p._active = false;
/**
* @property _dirty
* @protected
* @type {Boolean}
* @default false
**/
p._dirty = false;
/**
* Initialization method.
* @method initialize
* @protected
**/
p.initialize = function() {
this.clear();
this._ctx = Graphics._ctx;
};
/**
* Returns true if this Graphics instance has no drawing commands.
* @method isEmpty
* @return {Boolean} Returns true if this Graphics instance has no drawing commands.
**/
p.isEmpty = function() {
return !(this._instructions.length || this._oldInstructions.length || this._activeInstructions.length);
};
/**
* Draws the display object into the specified context ignoring it's visible, alpha, shadow, and transform.
* Returns true if the draw was handled (useful for overriding functionality).
*
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
* @method draw
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
**/
p.draw = function(ctx) {
if (this._dirty) { this._updateInstructions(); }
var instr = this._instructions;
for (var i=0, l=instr.length; i
* whatwg spec.
* @method lineTo
* @param {Number} x The x coordinate the drawing point should draw to.
* @param {Number} y The y coordinate the drawing point should draw to.
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.lineTo = function(x, y) {
this._dirty = this._active = true;
this._activeInstructions.push(new Command(this._ctx.lineTo, [x, y]));
return this;
};
/**
* Draws an arc with the specified control points and radius. For detailed information, read the
*
* whatwg spec.
* @method arcTo
* @param {Number} x1
* @param {Number} y1
* @param {Number} x2
* @param {Number} y2
* @param {Number} radius
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.arcTo = function(x1, y1, x2, y2, radius) {
this._dirty = this._active = true;
this._activeInstructions.push(new Command(this._ctx.arcTo, [x1, y1, x2, y2, radius]));
return this;
};
/**
* Draws an arc defined by the radius, startAngle and endAngle arguments, centered at the position (x, y). For
* example, to draw a full circle with a radius of 20 centered at (100, 100):
*
* arc(100, 100, 20, 0, Math.PI*2);
*
* For detailed information, read the
* whatwg spec.
* @method arc
* @param {Number} x
* @param {Number} y
* @param {Number} radius
* @param {Number} startAngle Measured in radians.
* @param {Number} endAngle Measured in radians.
* @param {Boolean} anticlockwise
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.arc = function(x, y, radius, startAngle, endAngle, anticlockwise) {
this._dirty = this._active = true;
if (anticlockwise == null) { anticlockwise = false; }
this._activeInstructions.push(new Command(this._ctx.arc, [x, y, radius, startAngle, endAngle, anticlockwise]));
return this;
};
/**
* Draws a quadratic curve from the current drawing point to (x, y) using the control point (cpx, cpy). For detailed
* information, read the
* whatwg spec.
* @method quadraticCurveTo
* @param {Number} cpx
* @param {Number} cpy
* @param {Number} x
* @param {Number} y
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.quadraticCurveTo = function(cpx, cpy, x, y) {
this._dirty = this._active = true;
this._activeInstructions.push(new Command(this._ctx.quadraticCurveTo, [cpx, cpy, x, y]));
return this;
};
/**
* Draws a bezier curve from the current drawing point to (x, y) using the control points (cp1x, cp1y) and (cp2x,
* cp2y). For detailed information, read the
*
* whatwg spec.
* @method bezierCurveTo
* @param {Number} cp1x
* @param {Number} cp1y
* @param {Number} cp2x
* @param {Number} cp2y
* @param {Number} x
* @param {Number} y
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
this._dirty = this._active = true;
this._activeInstructions.push(new Command(this._ctx.bezierCurveTo, [cp1x, cp1y, cp2x, cp2y, x, y]));
return this;
};
/**
* Draws a rectangle at (x, y) with the specified width and height using the current fill and/or stroke.
* For detailed information, read the
*
* whatwg spec.
* @method rect
* @param {Number} x
* @param {Number} y
* @param {Number} w Width of the rectangle
* @param {Number} h Height of the rectangle
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.rect = function(x, y, w, h) {
this._dirty = this._active = true;
this._activeInstructions.push(new Command(this._ctx.rect, [x, y, w, h]));
return this;
};
/**
* Closes the current path, effectively drawing a line from the current drawing point to the first drawing point specified
* since the fill or stroke was last set.
* @method closePath
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.closePath = function() {
if (this._active) {
this._dirty = true;
this._activeInstructions.push(new Command(this._ctx.closePath, []));
}
return this;
};
// public methods that roughly map to Flash graphics APIs:
/**
* Clears all drawing instructions, effectively resetting this Graphics instance.
* @method clear
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.clear = function() {
this._instructions = [];
this._oldInstructions = [];
this._activeInstructions = [];
this._strokeStyleInstructions = this._strokeInstructions = this._fillInstructions = null;
this._active = this._dirty = false;
return this;
};
/**
* Begins a fill with the specified color. This ends the current sub-path.
* @method beginFill
* @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to
* null will result in no fill.
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.beginFill = function(color) {
if (this._active) { this._newPath(); }
this._fillInstructions = color ? [new Command(this._setProp, ["fillStyle", color], false), Graphics.fillCmd] : null;
return this;
};
/**
* Begins a linear gradient fill defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
* example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a square to display it:
*
* myGraphics.beginLinearGradientFill(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120);
*
* @method beginLinearGradientFill
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient
* drawing from red to blue.
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw
* the first color to 10% then interpolating to the second color at 90%.
* @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
* @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
* @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
* @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.beginLinearGradientFill = function(colors, ratios, x0, y0, x1, y1) {
if (this._active) { this._newPath(); }
var o = this._ctx.createLinearGradient(x0, y0, x1, y1);
for (var i=0, l=colors.length; ibeginFill(null).
* @method endFill
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.endFill = function() {
return this.beginFill();
};
/**
* Sets the stroke style for the current sub-path. Like all drawing methods, this can be chained, so you can define
* the stroke style and color in a single line of code like so:
*
* myGraphics.setStrokeStyle(8,"round").beginStroke("#F00");
*
* @method setStrokeStyle
* @param {Number} thickness The width of the stroke.
* @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt,
* round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with
* the tiny API.
* @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet.
* One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel)
* for use with the tiny API.
* @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which
* controls at what point a mitered joint will be clipped.
* @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless
* of active transformations.
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.setStrokeStyle = function(thickness, caps, joints, miterLimit, ignoreScale) {
if (this._active) { this._newPath(); }
this._strokeStyleInstructions = [
new Command(this._setProp, ["lineWidth", (thickness == null ? "1" : thickness)], false),
new Command(this._setProp, ["lineCap", (caps == null ? "butt" : (isNaN(caps) ? caps : Graphics.STROKE_CAPS_MAP[caps]))], false),
new Command(this._setProp, ["lineJoin", (joints == null ? "miter" : (isNaN(joints) ? joints : Graphics.STROKE_JOINTS_MAP[joints]))], false),
new Command(this._setProp, ["miterLimit", (miterLimit == null ? "10" : miterLimit)], false)
];
this._ignoreScaleStroke = ignoreScale;
return this;
};
/**
* Begins a stroke with the specified color. This ends the current sub-path.
* @method beginStroke
* @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to
* null will result in no stroke.
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.beginStroke = function(color) {
if (this._active) { this._newPath(); }
this._strokeInstructions = color ? [new Command(this._setProp, ["strokeStyle", color], false)] : null;
return this;
};
/**
* Begins a linear gradient stroke defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
* example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a
* square to display it:
*
* myGraphics.setStrokeStyle(10).beginLinearGradientStroke(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120);
*
* @method beginLinearGradientStroke
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
* a gradient drawing from red to blue.
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
* 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
* @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
* @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
* @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
* @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.beginLinearGradientStroke = function(colors, ratios, x0, y0, x1, y1) {
if (this._active) { this._newPath(); }
var o = this._ctx.createLinearGradient(x0, y0, x1, y1);
for (var i=0, l=colors.length; ibeginStroke(null).
* @method endStroke
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.endStroke = function() {
this.beginStroke();
return this;
};
/**
* Maps the familiar ActionScript curveTo()
method to the functionally similar {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}}
* method.
* @method curveTo
* @type {Function}
**/
p.curveTo = p.quadraticCurveTo;
/**
* Maps the familiar ActionScript drawRect()
method to the functionally similar {{#crossLink "Graphics/rect"}}{{/crossLink}}
* method.
* @method drawRect
* @type {Function}
**/
p.drawRect = p.rect;
/**
* Draws a rounded rectangle with all corners with the specified radius.
* @method drawRoundRect
* @param {Number} x
* @param {Number} y
* @param {Number} w
* @param {Number} h
* @param {Number} radius Corner radius.
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.drawRoundRect = function(x, y, w, h, radius) {
this.drawRoundRectComplex(x, y, w, h, radius, radius, radius, radius);
return this;
};
/**
* Draws a rounded rectangle with different corner radii. Supports positive and negative corner radii.
* @method drawRoundRectComplex
* @param {Number} x
* @param {Number} y
* @param {Number} w
* @param {Number} h
* @param {Number} radiusTL Top left corner radius.
* @param {Number} radiusTR Top right corner radius.
* @param {Number} radiusBR Bottom right corner radius.
* @param {Number} radiusBL Bottom left corner radius.
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.drawRoundRectComplex = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) {
var max = (w max) { radiusTL = max; }
if (radiusTR < 0) { radiusTR *= (mTR=-1); }
if (radiusTR > max) { radiusTR = max; }
if (radiusBR < 0) { radiusBR *= (mBR=-1); }
if (radiusBR > max) { radiusBR = max; }
if (radiusBL < 0) { radiusBL *= (mBL=-1); }
if (radiusBL > max) { radiusBL = max; }
this._dirty = this._active = true;
var arcTo=this._ctx.arcTo, lineTo=this._ctx.lineTo;
this._activeInstructions.push(
new Command(this._ctx.moveTo, [x+w-radiusTR, y]),
new Command(arcTo, [x+w+radiusTR*mTR, y-radiusTR*mTR, x+w, y+radiusTR, radiusTR]),
new Command(lineTo, [x+w, y+h-radiusBR]),
new Command(arcTo, [x+w+radiusBR*mBR, y+h+radiusBR*mBR, x+w-radiusBR, y+h, radiusBR]),
new Command(lineTo, [x+radiusBL, y+h]),
new Command(arcTo, [x-radiusBL*mBL, y+h+radiusBL*mBL, x, y+h-radiusBL, radiusBL]),
new Command(lineTo, [x, y+radiusTL]),
new Command(arcTo, [x-radiusTL*mTL, y-radiusTL*mTL, x+radiusTL, y, radiusTL]),
new Command(this._ctx.closePath)
);
return this;
};
/**
* Draws a circle with the specified radius at (x, y).
*
* var g = new Graphics();
* g.setStrokeStyle(1);
* g.beginStroke(Graphics.getRGB(0,0,0));
* g.beginFill(Graphics.getRGB(255,0,0));
* g.drawCircle(0,0,3);
*
* var s = new Shape(g);
* s.x = 100;
* s.y = 100;
*
* stage.addChild(s);
* stage.update();
*
* @method drawCircle
* @param {Number} x x coordinate center point of circle.
* @param {Number} y y coordinate center point of circle.
* @param {Number} radius Radius of circle.
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.drawCircle = function(x, y, radius) {
this.arc(x, y, radius, 0, Math.PI*2);
return this;
};
/**
* Draws an ellipse (oval) with a specified width (w) and height (h). Similar to {{#crossLink "Graphics/drawCircle"}}{{/crossLink}},
* except the width and height can be different.
* @method drawEllipse
* @param {Number} x x coordinate center point of ellipse.
* @param {Number} y y coordinate center point of ellipse.
* @param {Number} w height (horizontal diameter) of ellipse. The horizontal radius will be half of this number.
* @param {Number} h width (vertical diameter) of ellipse. The vertical radius will be half of this number.
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.drawEllipse = function(x, y, w, h) {
this._dirty = this._active = true;
var k = 0.5522848;
var ox = (w / 2) * k;
var oy = (h / 2) * k;
var xe = x + w;
var ye = y + h;
var xm = x + w / 2;
var ym = y + h / 2;
this._activeInstructions.push(
new Command(this._ctx.moveTo, [x, ym]),
new Command(this._ctx.bezierCurveTo, [x, ym-oy, xm-ox, y, xm, y]),
new Command(this._ctx.bezierCurveTo, [xm+ox, y, xe, ym-oy, xe, ym]),
new Command(this._ctx.bezierCurveTo, [xe, ym+oy, xm+ox, ye, xm, ye]),
new Command(this._ctx.bezierCurveTo, [xm-ox, ye, x, ym+oy, x, ym])
);
return this;
};
/**
* Draws a star if pointSize is greater than 0, or a regular polygon if pointSize is 0 with the specified number of
* points. For example, the following code will draw a familiar 5 pointed star shape centered at 100, 100 and with a
* radius of 50:
* myGraphics.beginFill("#FF0").drawPolyStar(100, 100, 50, 5, 0.6, -90);
* // Note: -90 makes the first point vertical
*
* @method drawPolyStar
* @param {Number} x Position of the center of the shape.
* @param {Number} y Position of the center of the shape.
* @param {Number} radius The outer radius of the shape.
* @param {Number} sides The number of points on the star or sides on the polygon.
* @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular
* polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy.
* @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point
* directly to the right of the center.
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.drawPolyStar = function(x, y, radius, sides, pointSize, angle) {
this._dirty = this._active = true;
if (pointSize == null) { pointSize = 0; }
pointSize = 1-pointSize;
if (angle == null) { angle = 0; }
else { angle /= 180/Math.PI; }
var a = Math.PI/sides;
this._activeInstructions.push(new Command(this._ctx.moveTo, [x+Math.cos(angle)*radius, y+Math.sin(angle)*radius]));
for (var i=0; iA - bits 000000. First 3 bits (000) indicate a moveTo operation. 4th bit (0) indicates 2 chars per parameter.
*
n0 - 110111011100. Absolute x position of -150.0px. First bit indicates a negative value, remaining bits indicate 1500 tenths of a pixel.
*
AA - 000000000000. Absolute y position of 0.
*
I - 001100. First 3 bits (001) indicate a lineTo operation. 4th bit (1) indicates 3 chars per parameter.
*
Au4 - 000000101110111000. An x delta of 300.0px, which is added to the previous x value of -150.0px to provide an absolute position of +150.0px.
*
AAA - 000000000000000000. A y delta value of 0.
*
* @method decodePath
* @param {String} str The path string to decode.
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
**/
p.decodePath = function(str) {
var instructions = [this.moveTo, this.lineTo, this.quadraticCurveTo, this.bezierCurveTo, this.closePath];
var paramCount = [2, 2, 4, 6, 0];
var i=0, l=str.length;
var params = [];
var x=0, y=0;
var base64 = Graphics.BASE_64;
while (i>3; // highest order bits 1-3 code for operation.
var f = instructions[fi];
// check that we have a valid instruction & that the unused bits are empty:
if (!f || (n&3)) { throw("bad path data (@"+i+"): "+c); }
var pl = paramCount[fi];
if (!fi) { x=y=0; } // move operations reset the position.
params.length = 0;
i++;
var charCount = (n>>2&1)+2; // 4th header bit indicates number size for this operation.
for (var p=0; p>5) ? -1 : 1;
num = ((num&31)<<6)|(base64[str.charAt(i+1)]);
if (charCount == 3) { num = (num<<6)|(base64[str.charAt(i+2)]); }
num = sign*num/10;
if (p%2) { x = (num += x); }
else { y = (num += y); }
params[p] = num;
i += charCount;
}
f.apply(this,params);
}
return this;
};
/**
* Returns a clone of this Graphics instance.
* @method clone
* @return {Graphics} A clone of the current Graphics instance.
**/
p.clone = function() {
var o = new Graphics();
o._instructions = this._instructions.slice();
o._activeInstructions = this._activeInstructions.slice();
o._oldInstructions = this._oldInstructions.slice();
if (this._fillInstructions) { o._fillInstructions = this._fillInstructions.slice(); }
if (this._strokeInstructions) { o._strokeInstructions = this._strokeInstructions.slice(); }
if (this._strokeStyleInstructions) { o._strokeStyleInstructions = this._strokeStyleInstructions.slice(); }
o._active = this._active;
o._dirty = this._dirty;
return o;
};
/**
* Returns a string representation of this object.
* @method toString
* @return {String} a string representation of the instance.
**/
p.toString = function() {
return "[Graphics]";
};
// tiny API:
/** Shortcut to moveTo.
* @method mt
* @protected
* @type {Function}
**/
p.mt = p.moveTo;
/** Shortcut to lineTo.
* @method lt
* @protected
* @type {Function}
**/
p.lt = p.lineTo;
/** Shortcut to arcTo.
* @method at
* @protected
* @type {Function}
**/
p.at = p.arcTo;
/** Shortcut to bezierCurveTo.
* @method bt
* @protected
* @type {Function}
**/
p.bt = p.bezierCurveTo;
/** Shortcut to quadraticCurveTo / curveTo.
* @method qt
* @protected
* @type {Function}
**/
p.qt = p.quadraticCurveTo;
/** Shortcut to arc.
* @method a
* @protected
* @type {Function}
**/
p.a = p.arc;
/** Shortcut to rect.
* @method r
* @protected
* @type {Function}
**/
p.r = p.rect;
/** Shortcut to closePath.
* @method cp
* @protected
* @type {Function}
**/
p.cp = p.closePath;
/** Shortcut to clear.
* @method c
* @protected
* @type {Function}
**/
p.c = p.clear;
/** Shortcut to beginFill.
* @method f
* @protected
* @type {Function}
**/
p.f = p.beginFill;
/** Shortcut to beginLinearGradientFill.
* @method lf
* @protected
* @type {Function}
**/
p.lf = p.beginLinearGradientFill;
/** Shortcut to beginRadialGradientFill.
* @method rf
* @protected
* @type {Function}
**/
p.rf = p.beginRadialGradientFill;
/** Shortcut to beginBitmapFill.
* @method bf
* @protected
* @type {Function}
**/
p.bf = p.beginBitmapFill;
/** Shortcut to endFill.
* @method ef
* @protected
* @type {Function}
**/
p.ef = p.endFill;
/** Shortcut to setStrokeStyle.
* @method ss
* @protected
* @type {Function}
**/
p.ss = p.setStrokeStyle;
/** Shortcut to beginStroke.
* @method s
* @protected
* @type {Function}
**/
p.s = p.beginStroke;
/** Shortcut to beginLinearGradientStroke.
* @method ls
* @protected
* @type {Function}
**/
p.ls = p.beginLinearGradientStroke;
/** Shortcut to beginRadialGradientStroke.
* @method rs
* @protected
* @type {Function}
**/
p.rs = p.beginRadialGradientStroke;
/** Shortcut to beginBitmapStroke.
* @method bs
* @protected
* @type {Function}
**/
p.bs = p.beginBitmapStroke;
/** Shortcut to endStroke.
* @method es
* @protected
* @type {Function}
**/
p.es = p.endStroke;
/** Shortcut to drawRect.
* @method dr
* @protected
* @type {Function}
**/
p.dr = p.drawRect;
/** Shortcut to drawRoundRect.
* @method rr
* @protected
* @type {Function}
**/
p.rr = p.drawRoundRect;
/** Shortcut to drawRoundRectComplex.
* @method rc
* @protected
* @type {Function}
**/
p.rc = p.drawRoundRectComplex;
/** Shortcut to drawCircle.
* @method dc
* @protected
* @type {Function}
**/
p.dc = p.drawCircle;
/** Shortcut to drawEllipse.
* @method de
* @protected
* @type {Function}
**/
p.de = p.drawEllipse;
/** Shortcut to drawPolyStar.
* @method dp
* @protected
* @type {Function}
**/
p.dp = p.drawPolyStar;
/** Shortcut to decodePath.
* @method p
* @protected
* t@ype Function
**/
p.p = p.decodePath;
// private methods:
/**
* @method _updateInstructions
* @protected
**/
p._updateInstructions = function() {
this._instructions = this._oldInstructions.slice();
this._instructions.push(Graphics.beginCmd);
this._instructions.push.apply(this._instructions, this._activeInstructions);
if (this._fillInstructions) { this._instructions.push.apply(this._instructions, this._fillInstructions); }
if (this._strokeInstructions) {
if (this._strokeStyleInstructions) {
this._instructions.push.apply(this._instructions, this._strokeStyleInstructions);
}
this._instructions.push.apply(this._instructions, this._strokeInstructions);
if (this._ignoreScaleStroke) {
this._instructions.push(
new Command(this._ctx.save, [], false),
new Command(this._ctx.setTransform, [1,0,0,1,0,0], false),
Graphics.strokeCmd,
new Command(this._ctx.restore, [], false)
);
} else {
this._instructions.push(Graphics.strokeCmd);
}
}
};
/**
* @method _newPath
* @protected
**/
p._newPath = function() {
if (this._dirty) { this._updateInstructions(); }
this._oldInstructions = this._instructions;
this._activeInstructions = [];
this._active = this._dirty = false;
};
// used to create Commands that set properties:
/**
* Used to create Commands that set properties
* @method _setProp
* @param {String} name
* @param {String} value
* @protected
**/
p._setProp = function(name, value) {
this[name] = value;
};
createjs.Graphics = Graphics;
}());