AnalogGauge.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. define("dojox/gauges/AnalogGauge", ["dojo/_base/kernel","dojo/_base/declare","dojo/_base/array","dojo/_base/lang","dojo/_base/html","dojo/_base/event",
  2. "dojox/gfx", "./_Gauge","./AnalogLineIndicator", "dojo/dom-geometry"],
  3. function(dojo, declare, arr, lang, html, event,
  4. gfx, Gauge, AnalogLineIndicator, domGeometry) {
  5. /*=====
  6. Gauge = dojox.gauges._Gauge;
  7. =====*/
  8. return declare("dojox.gauges.AnalogGauge",Gauge,{
  9. // summary:
  10. // a gauge built using the dojox.gfx package.
  11. //
  12. // description:
  13. // using dojo.gfx (and thus either SVG or VML based on what is supported), this widget
  14. // builds a gauge component, used to display numerical data in a familiar format
  15. //
  16. // example:
  17. // | <script type="text/javascript">
  18. // | require(["dojox/gauges/AnalogGauge"]);
  19. // | </script>
  20. // |
  21. // | <div dojoType="dojox.gauges.AnalogGauge"
  22. // | id="testGauge"
  23. // | width="300"
  24. // | height="200"
  25. // | cx=150
  26. // | cy=175
  27. // | radius=125
  28. // | image="gaugeOverlay.png"
  29. // | imageOverlay="false"
  30. // | imageWidth="280"
  31. // | imageHeight="155"
  32. // | imageX="12"
  33. // | imageY="38">
  34. // | </div>
  35. // startAngle: Number
  36. // angle (in degrees) for start of gauge (default is -90)
  37. startAngle: -90,
  38. // endAngle: Number
  39. // angle (in degrees) for end of gauge (default is 90)
  40. endAngle: 90,
  41. // cx: Number
  42. // center of gauge x coordinate (default is gauge width / 2)
  43. cx: 0,
  44. // cy: Number
  45. // center of gauge x coordinate (default is gauge height / 2)
  46. cy: 0,
  47. // radius: Number
  48. // radius of gauge (default is smaller of cx-25 or cy-25)
  49. radius: 0,
  50. // orientation: String
  51. // The orientation of the gauge. The value can be 'clockwise' or 'cclockwise' (default is 'clockwise')
  52. orientation: "clockwise",
  53. // _defaultIndicator: dojox.gauges._Indicator
  54. // override of dojox.gauges._Gauge._defaultIndicator
  55. _defaultIndicator: AnalogLineIndicator,
  56. startup: function(){
  57. // handle settings from HTML by making sure all the options are
  58. // converted correctly to numbers and that we calculate defaults
  59. // for cx, cy and radius
  60. // also connects mouse handling events
  61. if(this.getChildren){
  62. arr.forEach(this.getChildren(), function(child){ child.startup(); });
  63. }
  64. this.startAngle = Number(this.startAngle);
  65. this.endAngle = Number(this.endAngle);
  66. this.cx = Number(this.cx);
  67. if(!this.cx){this.cx = this.width/2;}
  68. this.cy = Number(this.cy);
  69. if(!this.cy){this.cy = this.height/2;}
  70. this.radius = Number(this.radius);
  71. if(!this.radius){this.radius = Math.min(this.cx,this.cy) - 25;}
  72. this.inherited(arguments);
  73. },
  74. _getAngle: function(/*Number*/value){
  75. // summary:
  76. // This is a helper function used to determine the angle that represents
  77. // a given value on the gauge
  78. // value: Number
  79. // A value to be converted to an angle for this gauge.
  80. var v = Number(value);
  81. var angle;
  82. if (value == null || isNaN(v) || v <= this.min)
  83. angle = this._mod360(this.startAngle);
  84. else
  85. if (v >= this.max)
  86. angle = this._mod360(this.endAngle);
  87. else {
  88. var startAngle = this._mod360(this.startAngle);
  89. var relativeValue = (v - this.min);
  90. if (this.orientation != 'clockwise')
  91. relativeValue = -relativeValue;
  92. angle = this._mod360(startAngle + this._getAngleRange() * relativeValue / Math.abs(this.min - this.max));
  93. }
  94. return angle;
  95. },
  96. _getValueForAngle: function(/*Number*/angle){
  97. // summary:
  98. // This is a helper function used to determine the value represented by a
  99. // given angle on the gauge
  100. // angle: Number
  101. // A angle to be converted to a value for this gauge.
  102. var startAngle = this._mod360(this.startAngle);
  103. var endAngle = this._mod360(this.endAngle);
  104. if (!this._angleInRange(angle)){
  105. var min1 = this._mod360(startAngle - angle);
  106. var min2 = 360 - min1;
  107. var max1 = this._mod360(endAngle - angle);
  108. var max2 = 360 - max1;
  109. if (Math.min(min1, min2) < Math.min(max1, max2))
  110. return this.min;
  111. else
  112. return this.max;
  113. }
  114. else {
  115. var range = Math.abs(this.max - this.min);
  116. var relativeAngle = this._mod360(this.orientation == 'clockwise' ?
  117. (angle - startAngle): (-angle + startAngle));
  118. return this.min + range * relativeAngle / this._getAngleRange();
  119. }
  120. },
  121. _getAngleRange: function(){
  122. // summary:
  123. // This is a helper function that returns the angle range
  124. // from startAngle to endAngle according to orientation.
  125. var range;
  126. var startAngle = this._mod360(this.startAngle);
  127. var endAngle = this._mod360(this.endAngle);
  128. if (startAngle == endAngle)
  129. return 360;
  130. if (this.orientation == 'clockwise'){
  131. if (endAngle < startAngle)
  132. range = 360 - (startAngle - endAngle);
  133. else
  134. range = endAngle - startAngle;
  135. }
  136. else {
  137. if (endAngle < startAngle)
  138. range = startAngle - endAngle;
  139. else
  140. range = 360 - (endAngle - startAngle);
  141. }
  142. return range;
  143. },
  144. _angleInRange: function(value){
  145. // summary:
  146. // Test if the angle value is in the startAngle/endAngle range
  147. var startAngle = this._mod360(this.startAngle);
  148. var endAngle = this._mod360(this.endAngle);
  149. if (startAngle == endAngle)
  150. return true;
  151. value = this._mod360(value);
  152. if (this.orientation == "clockwise"){
  153. if (startAngle < endAngle)
  154. return value >= startAngle && value <= endAngle;
  155. else
  156. return !(value > endAngle && value < startAngle);
  157. }
  158. else {
  159. if (startAngle < endAngle)
  160. return !(value > startAngle && value < endAngle);
  161. else
  162. return value >= endAngle && value <= startAngle;
  163. }
  164. },
  165. _isScaleCircular: function(){
  166. // summary:
  167. // internal method to check if the scale is fully circular
  168. return (this._mod360(this.startAngle) == this._mod360(this.endAngle));
  169. },
  170. _mod360:function(v){
  171. // summary:
  172. // returns the angle between 0 and 360;
  173. while (v>360) v = v - 360;
  174. while (v<0) v = v + 360;
  175. return v;
  176. },
  177. _getRadians: function(/*Number*/angle){
  178. // summary:
  179. // This is a helper function than converts degrees to radians
  180. // angle: Number
  181. // An angle, in degrees, to be converted to radians.
  182. return angle*Math.PI/180;
  183. },
  184. _getDegrees: function(/*Number*/radians){
  185. // summary:
  186. // This is a helper function that converts radians to degrees
  187. // radians: Number
  188. // An angle, in radians, to be converted to degrees.
  189. return radians*180/Math.PI;
  190. },
  191. drawRange: function(/*dojox.gfx.Group*/ group, /*Object*/range){
  192. // summary:
  193. // This function is used to draw (or redraw) a range
  194. // description:
  195. // Draws a range (colored area on the background of the gauge)
  196. // based on the given arguments.
  197. // group:
  198. // The GFX group where the range must be drawn.
  199. // range:
  200. // A range is a dojox.gauges.Range or an object
  201. // with similar parameters (low, high, hover, etc.).
  202. var path;
  203. if(range.shape){
  204. range.shape.parent.remove(range.shape);
  205. range.shape = null;
  206. }
  207. var a1, a2;
  208. if((range.low == this.min) && (range.high == this.max) && ((this._mod360(this.endAngle) == this._mod360(this.startAngle)))){
  209. path = group.createCircle({cx: this.cx, cy: this.cy, r: this.radius});
  210. }else{
  211. a1 = this._getRadians(this._getAngle(range.low));
  212. a2 = this._getRadians(this._getAngle(range.high));
  213. if (this.orientation == 'cclockwise')
  214. {
  215. var a = a2;
  216. a2 = a1;
  217. a1 = a;
  218. }
  219. var x1=this.cx+this.radius*Math.sin(a1),
  220. y1=this.cy-this.radius*Math.cos(a1),
  221. x2=this.cx+this.radius*Math.sin(a2),
  222. y2=this.cy-this.radius*Math.cos(a2),
  223. big=0
  224. ;
  225. var arange;
  226. if (a1<=a2)
  227. arange = a2-a1;
  228. else
  229. arange = 2*Math.PI-a1+a2;
  230. if(arange>Math.PI){big=1;}
  231. path = group.createPath();
  232. if(range.size){
  233. path.moveTo(this.cx+(this.radius-range.size)*Math.sin(a1),
  234. this.cy-(this.radius-range.size)*Math.cos(a1));
  235. }else{
  236. path.moveTo(this.cx,this.cy);
  237. }
  238. path.lineTo(x1,y1);
  239. path.arcTo(this.radius,this.radius,0,big,1,x2,y2);
  240. if(range.size){
  241. path.lineTo(this.cx+(this.radius-range.size)*Math.sin(a2),
  242. this.cy-(this.radius-range.size)*Math.cos(a2));
  243. path.arcTo((this.radius-range.size),(this.radius-range.size),0,big,0,
  244. this.cx+(this.radius-range.size)*Math.sin(a1),
  245. this.cy-(this.radius-range.size)*Math.cos(a1));
  246. }
  247. path.closePath();
  248. }
  249. if(lang.isArray(range.color) || lang.isString(range.color)){
  250. path.setStroke({color: range.color});
  251. path.setFill(range.color);
  252. }else if(range.color.type){
  253. // Color is a gradient
  254. a1 = this._getRadians(this._getAngle(range.low));
  255. a2 = this._getRadians(this._getAngle(range.high));
  256. range.color.x1 = this.cx+(this.radius*Math.sin(a1))/2;
  257. range.color.x2 = this.cx+(this.radius*Math.sin(a2))/2;
  258. range.color.y1 = this.cy-(this.radius*Math.cos(a1))/2;
  259. range.color.y2 = this.cy-(this.radius*Math.cos(a2))/2;
  260. path.setFill(range.color);
  261. path.setStroke({color: range.color.colors[0].color});
  262. }else if (gfx.svg){
  263. // We've defined a style rather than an explicit color
  264. path.setStroke({color: "green"}); // Arbitrary color, just have to indicate
  265. path.setFill("green"); // that we want it filled
  266. path.getEventSource().setAttribute("class", range.color.style);
  267. }
  268. path.connect("onmouseover", lang.hitch(this, this._handleMouseOverRange, range));
  269. path.connect("onmouseout", lang.hitch(this, this._handleMouseOutRange, range));
  270. range.shape = path;
  271. },
  272. getRangeUnderMouse: function(/*Object*/e){
  273. // summary:
  274. // Determines which range the mouse is currently over
  275. // e: Object
  276. // The event object as received by the mouse handling functions below.
  277. var range = null,
  278. pos = domGeometry.getContentBox(this.gaugeContent),
  279. x = e.clientX - pos.x,
  280. y = e.clientY - pos.y,
  281. r = Math.sqrt((y - this.cy)*(y - this.cy) + (x - this.cx)*(x - this.cx))
  282. ;
  283. if(r < this.radius){
  284. var angle = this._getDegrees(Math.atan2(y - this.cy, x - this.cx) + Math.PI/2),
  285. //if(angle > this.endAngle){angle = angle - 360;}
  286. value = this._getValueForAngle(angle)
  287. ;
  288. if(this._rangeData){
  289. for(var i=0; (i<this._rangeData.length) && !range; i++){
  290. if((Number(this._rangeData[i].low) <= value) && (Number(this._rangeData[i].high) >= value)){
  291. range = this._rangeData[i];
  292. }
  293. }
  294. }
  295. }
  296. return range;
  297. },
  298. _dragIndicator: function(/*Object*/ widget, /*Object*/ e){
  299. // summary:
  300. // Handles the dragging of an indicator to the event position, including moving/re-drawing
  301. // get angle for mouse position
  302. this._dragIndicatorAt(widget, e.pageX, e.pageY);
  303. event.stop(e);
  304. },
  305. _dragIndicatorAt: function(/*Object*/ widget, x,y){
  306. // summary:
  307. // Handles the dragging of an indicator to a specific position, including moving/re-drawing
  308. // get angle for mouse position
  309. var pos = domGeometry.position(widget.gaugeContent, true),
  310. xf = x - pos.x,
  311. yf = y - pos.y,
  312. angle = widget._getDegrees(Math.atan2(yf - widget.cy, xf - widget.cx) + Math.PI/2);
  313. // get value and restrict to our min/max
  314. var value = widget._getValueForAngle(angle);
  315. value = Math.min(Math.max(value, widget.min), widget.max);
  316. // update the indicator
  317. widget._drag.value = widget._drag.currentValue = value;
  318. // callback
  319. widget._drag.onDragMove(widget._drag);
  320. // rotate indicator
  321. widget._drag.draw(this._indicatorsGroup, true);
  322. widget._drag.valueChanged();
  323. }
  324. });
  325. });