diff options
Diffstat (limited to 'js/dojo/dojox/gfx/path.js')
| -rw-r--r-- | js/dojo/dojox/gfx/path.js | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/js/dojo/dojox/gfx/path.js b/js/dojo/dojox/gfx/path.js new file mode 100644 index 0000000..9f76db5 --- /dev/null +++ b/js/dojo/dojox/gfx/path.js @@ -0,0 +1,446 @@ +//>>built +define("dojox/gfx/path", ["./_base", "dojo/_base/lang","dojo/_base/declare", "./matrix", "./shape"], + function(g, lang, declare, matrix, shapeLib){ +/*===== + dojox.gfx.path = { + // summary: + // This module contains the core graphics Path API. + // Path command format follows the W3C SVG 1.0 Path api. + }; + g = dojox.gfx; + shape.Shape = dojox.gfx.shape.Shape; + =====*/ + + var path = g.path = {}; + var Path = declare("dojox.gfx.path.Path", shapeLib.Shape, { + // summary: a generalized path shape + + constructor: function(rawNode){ + // summary: a path constructor + // rawNode: Node + // a DOM node to be used by this path object + this.shape = lang.clone(g.defaultPath); + this.segments = []; + this.tbbox = null; + this.absolute = true; + this.last = {}; + this.rawNode = rawNode; + this.segmented = false; + }, + + // mode manipulations + setAbsoluteMode: function(mode){ + // summary: sets an absolute or relative mode for path points + // mode: Boolean + // true/false or "absolute"/"relative" to specify the mode + this._confirmSegmented(); + this.absolute = typeof mode == "string" ? (mode == "absolute") : mode; + return this; // self + }, + getAbsoluteMode: function(){ + // summary: returns a current value of the absolute mode + this._confirmSegmented(); + return this.absolute; // Boolean + }, + + getBoundingBox: function(){ + // summary: returns the bounding box {x, y, width, height} or null + this._confirmSegmented(); + return (this.bbox && ("l" in this.bbox)) ? {x: this.bbox.l, y: this.bbox.t, width: this.bbox.r - this.bbox.l, height: this.bbox.b - this.bbox.t} : null; // dojox.gfx.Rectangle + }, + + _getRealBBox: function(){ + // summary: returns an array of four points or null + // four points represent four corners of the untransformed bounding box + this._confirmSegmented(); + if(this.tbbox){ + return this.tbbox; // Array + } + var bbox = this.bbox, matrix = this._getRealMatrix(); + this.bbox = null; + for(var i = 0, len = this.segments.length; i < len; ++i){ + this._updateWithSegment(this.segments[i], matrix); + } + var t = this.bbox; + this.bbox = bbox; + this.tbbox = t ? [ + {x: t.l, y: t.t}, + {x: t.r, y: t.t}, + {x: t.r, y: t.b}, + {x: t.l, y: t.b} + ] : null; + return this.tbbox; // Array + }, + + getLastPosition: function(){ + // summary: returns the last point in the path, or null + this._confirmSegmented(); + return "x" in this.last ? this.last : null; // Object + }, + + _applyTransform: function(){ + this.tbbox = null; + return this.inherited(arguments); + }, + + // segment interpretation + _updateBBox: function(x, y, m){ + // summary: updates the bounding box of path with new point + // x: Number + // an x coordinate + // y: Number + // a y coordinate + + if(m){ + var t = matrix.multiplyPoint(m, x, y); + x = t.x; + y = t.y; + } + + // we use {l, b, r, t} representation of a bbox + if(this.bbox && ("l" in this.bbox)){ + if(this.bbox.l > x) this.bbox.l = x; + if(this.bbox.r < x) this.bbox.r = x; + if(this.bbox.t > y) this.bbox.t = y; + if(this.bbox.b < y) this.bbox.b = y; + }else{ + this.bbox = {l: x, b: y, r: x, t: y}; + } + }, + _updateWithSegment: function(segment, matrix){ + // summary: updates the bounding box of path with new segment + // segment: Object + // a segment + var n = segment.args, l = n.length, i; + // update internal variables: bbox, absolute, last + switch(segment.action){ + case "M": + case "L": + case "C": + case "S": + case "Q": + case "T": + for(i = 0; i < l; i += 2){ + this._updateBBox(n[i], n[i + 1], matrix); + } + this.last.x = n[l - 2]; + this.last.y = n[l - 1]; + this.absolute = true; + break; + case "H": + for(i = 0; i < l; ++i){ + this._updateBBox(n[i], this.last.y, matrix); + } + this.last.x = n[l - 1]; + this.absolute = true; + break; + case "V": + for(i = 0; i < l; ++i){ + this._updateBBox(this.last.x, n[i], matrix); + } + this.last.y = n[l - 1]; + this.absolute = true; + break; + case "m": + var start = 0; + if(!("x" in this.last)){ + this._updateBBox(this.last.x = n[0], this.last.y = n[1], matrix); + start = 2; + } + for(i = start; i < l; i += 2){ + this._updateBBox(this.last.x += n[i], this.last.y += n[i + 1], matrix); + } + this.absolute = false; + break; + case "l": + case "t": + for(i = 0; i < l; i += 2){ + this._updateBBox(this.last.x += n[i], this.last.y += n[i + 1], matrix); + } + this.absolute = false; + break; + case "h": + for(i = 0; i < l; ++i){ + this._updateBBox(this.last.x += n[i], this.last.y, matrix); + } + this.absolute = false; + break; + case "v": + for(i = 0; i < l; ++i){ + this._updateBBox(this.last.x, this.last.y += n[i], matrix); + } + this.absolute = false; + break; + case "c": + for(i = 0; i < l; i += 6){ + this._updateBBox(this.last.x + n[i], this.last.y + n[i + 1], matrix); + this._updateBBox(this.last.x + n[i + 2], this.last.y + n[i + 3], matrix); + this._updateBBox(this.last.x += n[i + 4], this.last.y += n[i + 5], matrix); + } + this.absolute = false; + break; + case "s": + case "q": + for(i = 0; i < l; i += 4){ + this._updateBBox(this.last.x + n[i], this.last.y + n[i + 1], matrix); + this._updateBBox(this.last.x += n[i + 2], this.last.y += n[i + 3], matrix); + } + this.absolute = false; + break; + case "A": + for(i = 0; i < l; i += 7){ + this._updateBBox(n[i + 5], n[i + 6], matrix); + } + this.last.x = n[l - 2]; + this.last.y = n[l - 1]; + this.absolute = true; + break; + case "a": + for(i = 0; i < l; i += 7){ + this._updateBBox(this.last.x += n[i + 5], this.last.y += n[i + 6], matrix); + } + this.absolute = false; + break; + } + // add an SVG path segment + var path = [segment.action]; + for(i = 0; i < l; ++i){ + path.push(g.formatNumber(n[i], true)); + } + if(typeof this.shape.path == "string"){ + this.shape.path += path.join(""); + }else{ + Array.prototype.push.apply(this.shape.path, path); //FIXME: why not simple push()? + } + }, + + // a dictionary, which maps segment type codes to a number of their arguments + _validSegments: {m: 2, l: 2, h: 1, v: 1, c: 6, s: 4, q: 4, t: 2, a: 7, z: 0}, + + _pushSegment: function(action, args){ + // summary: adds a segment + // action: String + // valid SVG code for a segment's type + // args: Array + // a list of parameters for this segment + this.tbbox = null; + var group = this._validSegments[action.toLowerCase()], segment; + if(typeof group == "number"){ + if(group){ + if(args.length >= group){ + segment = {action: action, args: args.slice(0, args.length - args.length % group)}; + this.segments.push(segment); + this._updateWithSegment(segment); + } + }else{ + segment = {action: action, args: []}; + this.segments.push(segment); + this._updateWithSegment(segment); + } + } + }, + + _collectArgs: function(array, args){ + // summary: converts an array of arguments to plain numeric values + // array: Array + // an output argument (array of numbers) + // args: Array + // an input argument (can be values of Boolean, Number, dojox.gfx.Point, or an embedded array of them) + for(var i = 0; i < args.length; ++i){ + var t = args[i]; + if(typeof t == "boolean"){ + array.push(t ? 1 : 0); + }else if(typeof t == "number"){ + array.push(t); + }else if(t instanceof Array){ + this._collectArgs(array, t); + }else if("x" in t && "y" in t){ + array.push(t.x, t.y); + } + } + }, + + // segments + moveTo: function(){ + // summary: forms a move segment + this._confirmSegmented(); + var args = []; + this._collectArgs(args, arguments); + this._pushSegment(this.absolute ? "M" : "m", args); + return this; // self + }, + lineTo: function(){ + // summary: forms a line segment + this._confirmSegmented(); + var args = []; + this._collectArgs(args, arguments); + this._pushSegment(this.absolute ? "L" : "l", args); + return this; // self + }, + hLineTo: function(){ + // summary: forms a horizontal line segment + this._confirmSegmented(); + var args = []; + this._collectArgs(args, arguments); + this._pushSegment(this.absolute ? "H" : "h", args); + return this; // self + }, + vLineTo: function(){ + // summary: forms a vertical line segment + this._confirmSegmented(); + var args = []; + this._collectArgs(args, arguments); + this._pushSegment(this.absolute ? "V" : "v", args); + return this; // self + }, + curveTo: function(){ + // summary: forms a curve segment + this._confirmSegmented(); + var args = []; + this._collectArgs(args, arguments); + this._pushSegment(this.absolute ? "C" : "c", args); + return this; // self + }, + smoothCurveTo: function(){ + // summary: forms a smooth curve segment + this._confirmSegmented(); + var args = []; + this._collectArgs(args, arguments); + this._pushSegment(this.absolute ? "S" : "s", args); + return this; // self + }, + qCurveTo: function(){ + // summary: forms a quadratic curve segment + this._confirmSegmented(); + var args = []; + this._collectArgs(args, arguments); + this._pushSegment(this.absolute ? "Q" : "q", args); + return this; // self + }, + qSmoothCurveTo: function(){ + // summary: forms a quadratic smooth curve segment + this._confirmSegmented(); + var args = []; + this._collectArgs(args, arguments); + this._pushSegment(this.absolute ? "T" : "t", args); + return this; // self + }, + arcTo: function(){ + // summary: forms an elliptic arc segment + this._confirmSegmented(); + var args = []; + this._collectArgs(args, arguments); + this._pushSegment(this.absolute ? "A" : "a", args); + return this; // self + }, + closePath: function(){ + // summary: closes a path + this._confirmSegmented(); + this._pushSegment("Z", []); + return this; // self + }, + + _confirmSegmented: function() { + if (!this.segmented) { + var path = this.shape.path; + // switch to non-updating version of path building + this.shape.path = []; + this._setPath(path); + // switch back to the string path + this.shape.path = this.shape.path.join(""); + // become segmented + this.segmented = true; + } + }, + + // setShape + _setPath: function(path){ + // summary: forms a path using an SVG path string + // path: String + // an SVG path string + var p = lang.isArray(path) ? path : path.match(g.pathSvgRegExp); + this.segments = []; + this.absolute = true; + this.bbox = {}; + this.last = {}; + if(!p) return; + // create segments + var action = "", // current action + args = [], // current arguments + l = p.length; + for(var i = 0; i < l; ++i){ + var t = p[i], x = parseFloat(t); + if(isNaN(x)){ + if(action){ + this._pushSegment(action, args); + } + args = []; + action = t; + }else{ + args.push(x); + } + } + this._pushSegment(action, args); + }, + setShape: function(newShape){ + // summary: forms a path using a shape + // newShape: Object + // an SVG path string or a path object (see dojox.gfx.defaultPath) + this.inherited(arguments, [typeof newShape == "string" ? {path: newShape} : newShape]); + + this.segmented = false; + this.segments = []; + if(!g.lazyPathSegmentation){ + this._confirmSegmented(); + } + return this; // self + }, + + // useful constant for descendants + _2PI: Math.PI * 2 + }); + + var TextPath = declare("dojox.gfx.path.TextPath", Path, { + // summary: a generalized TextPath shape + + constructor: function(rawNode){ + // summary: a TextPath shape constructor + // rawNode: Node + // a DOM node to be used by this TextPath object + if(!("text" in this)){ + this.text = lang.clone(g.defaultTextPath); + } + if(!("fontStyle" in this)){ + this.fontStyle = lang.clone(g.defaultFont); + } + }, + getText: function(){ + // summary: returns the current text object or null + return this.text; // Object + }, + setText: function(newText){ + // summary: sets a text to be drawn along the path + this.text = g.makeParameters(this.text, + typeof newText == "string" ? {text: newText} : newText); + this._setText(); + return this; // self + }, + getFont: function(){ + // summary: returns the current font object or null + return this.fontStyle; // Object + }, + setFont: function(newFont){ + // summary: sets a font for text + this.fontStyle = typeof newFont == "string" ? + g.splitFontString(newFont) : + g.makeParameters(g.defaultFont, newFont); + this._setFont(); + return this; // self + } + }); + + return { // our hash of newly defined objects + Path: Path, + TextPath: TextPath + }; +}); |
