Vector.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.drawing.tools.custom.Vector"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.drawing.tools.custom.Vector"] = true;
  8. dojo.provide("dojox.drawing.tools.custom.Vector");
  9. dojo.require("dojox.drawing.tools.Arrow");
  10. dojo.require("dojox.drawing.util.positioning");
  11. dojox.drawing.tools.custom.Vector = dojox.drawing.util.oo.declare(
  12. // summary:
  13. // Creates a Vector Stencil.
  14. // description:
  15. // Generally the same as an arrow, except that the arrow
  16. // head is only at the end. There is additionaly functionality
  17. // to allow for a 'zero vector' - one with no length.
  18. //
  19. //
  20. dojox.drawing.tools.Arrow,
  21. function(options){
  22. this.minimumSize = this.style.arrows.length;
  23. this.addShadow({size:3, mult:2});
  24. },
  25. {
  26. draws:true,
  27. type:"dojox.drawing.tools.custom.Vector",
  28. minimumSize:30,
  29. showAngle:true,
  30. changeAxis: function(cosphi){
  31. // summary:
  32. // Converts a vector to and from the z axis.
  33. // If passed a cosphi value that is used to set
  34. // the axis, otherwise it is the opp of what it is.
  35. cosphi = cosphi!==undefined?cosphi:this.style.zAxis? 0 : 1;
  36. if(cosphi == 0){
  37. this.style.zAxis = false;
  38. this.data.cosphi = 0;
  39. }else{
  40. this.style.zAxis = true;
  41. var p = this.points;
  42. var pt = this.zPoint();
  43. this.setPoints([
  44. {x:p[0].x, y:p[0].y},
  45. {x:pt.x, y:pt.y}
  46. ]);
  47. }
  48. this.render();
  49. },
  50. _createZeroVector: function(shp, d, sty){
  51. // summary:
  52. // Special creation function for the zero-vector shape
  53. //
  54. var s = shp=="hit" ? this.minimumSize : this.minimumSize/6;
  55. var f = shp=="hit" ? sty.fill : null;
  56. d = {
  57. cx:this.data.x1,
  58. cy:this.data.y1,
  59. rx:s,
  60. ry:s
  61. };
  62. this.remove(this[shp]);
  63. this[shp] = this.container.createEllipse(d)
  64. .setStroke(sty)
  65. .setFill(f);
  66. this.util.attr(this[shp], "drawingType", "stencil");
  67. },
  68. _create: function(/*String*/shp, /*StencilData*/d, /*Object*/sty){
  69. // summary:
  70. // Creates a dojox.gfx.shape based on passed arguments.
  71. // Can be called many times by implementation to create
  72. // multiple shapes in one stencil.
  73. //
  74. this.remove(this[shp]);
  75. this[shp] = this.container.createLine(d)
  76. .setStroke(sty);
  77. this._setNodeAtts(this[shp]);
  78. },
  79. onDrag: function(/*EventObject*/obj){
  80. // summary: See stencil._Base.onDrag
  81. //
  82. if(this.created){ return; }
  83. var x1 = obj.start.x,
  84. y1 = obj.start.y,
  85. x2 = obj.x,
  86. y2 = obj.y;
  87. if(this.keys.shift && !this.style.zAxis){
  88. var pt = this.util.snapAngle(obj, 45/180);
  89. x2 = pt.x;
  90. y2 = pt.y;
  91. }
  92. if(this.keys.alt){
  93. // FIXME:
  94. // should double the length of the line
  95. // FIXME:
  96. // if alt dragging past ZERO it seems to work
  97. // but select/deselect shows bugs
  98. var dx = x2>x1 ? ((x2-x1)/2) : ((x1-x2)/-2);
  99. var dy = y2>y1 ? ((y2-y1)/2) : ((y1-y2)/-2);
  100. x1 -= dx;
  101. x2 -= dx;
  102. y1 -= dy;
  103. y2 -= dy;
  104. }
  105. if(this.style.zAxis){
  106. var pts = this.zPoint(obj);
  107. x2 = pts.x;
  108. y2 = pts.y;
  109. }
  110. this.setPoints([
  111. {x:x1, y:y1},
  112. {x:x2, y:y2}
  113. ]);
  114. this.render();
  115. },
  116. onTransform: function(/* ? manager.Anchor */anchor){
  117. // summary:
  118. // Called from anchor point mouse drag
  119. // also called from plugins.Pan.checkBounds
  120. if(!this._isBeingModified){
  121. this.onTransformBegin();
  122. }
  123. // this is not needed for anchor moves, but it
  124. // is for stencil move:
  125. this.setPoints(this.points);
  126. this.render();
  127. },
  128. anchorConstrain: function(x, y){
  129. // summary:
  130. // Called from anchor point mouse drag
  131. if(!this.style.zAxis){ return null; }
  132. var radians = this.style.zAngle*Math.PI/180;
  133. //Constrain to angle
  134. var test = x<0 ? x>-y : x<-y;
  135. var dx = test ? x : -y/Math.tan(radians);
  136. var dy = !test ? y : -Math.tan(radians)*x;
  137. return {x:dx, y:dy}
  138. },
  139. zPoint: function(obj){
  140. // summary:
  141. // Takes any point and converts it to
  142. // be on the z-axis.
  143. if(obj===undefined){
  144. if(!this.points[0]){ return null; };
  145. var d = this.pointsToData();
  146. obj = {
  147. start:{
  148. x:d.x1,
  149. y:d.y1
  150. },
  151. x:d.x2,
  152. y:d.y2
  153. };
  154. }
  155. var radius = this.util.length(obj);
  156. var angle = this.util.angle(obj);
  157. angle<0 ? angle = 360 + angle : angle;
  158. angle = angle > 135 && angle < 315 ? this.style.zAngle : this.util.oppAngle(this.style.zAngle);
  159. return this.util.pointOnCircle(obj.start.x, obj.start.y, radius, angle);
  160. },
  161. pointsToData: function(p){
  162. // summary:
  163. // Converts points to data
  164. p = p || this.points;
  165. var cosphi = 0;
  166. var obj = {start:{x:p[0].x, y:p[0].y}, x:p[1].x, y:p[1].y};
  167. if(this.style.zAxis && (this.util.length(obj)>this.minimumSize)){
  168. var angle = this.util.angle(obj);
  169. angle<0 ? angle = 360 + angle : angle;
  170. cosphi = angle > 135 && angle < 315 ? 1 : -1;
  171. }
  172. this.data = {
  173. x1: p[0].x,
  174. y1: p[0].y,
  175. x2: p[1].x,
  176. y2: p[1].y,
  177. cosphi: cosphi
  178. };
  179. return this.data;
  180. },
  181. dataToPoints: function(o){
  182. //summary:
  183. // Converts data to points.
  184. o = o || this.data;
  185. if(o.radius || o.angle){
  186. // instead of using x1,x2,y1,y1,
  187. // it's been set as x,y,angle,radius
  188. var cosphi = 0;
  189. var pt = this.util.pointOnCircle(o.x,o.y,o.radius,o.angle);
  190. if(this.style.zAxis || (o.cosphi && o.cosphi!=0)){
  191. this.style.zAxis = true;
  192. cosphi = o.angle > 135 && o.angle < 315 ? 1 : -1;
  193. }
  194. //console.log(" ---- pts:", pt.x, pt.y);
  195. this.data = o = {
  196. x1:o.x,
  197. y1:o.y,
  198. x2:pt.x,
  199. y2:pt.y,
  200. cosphi:cosphi
  201. }
  202. }
  203. this.points = [
  204. {x:o.x1, y:o.y1},
  205. {x:o.x2, y:o.y2}
  206. ];
  207. return this.points;
  208. },
  209. render: function(){
  210. // summary:
  211. // Renders the 'hit' object (the shape used for an expanded
  212. // hit area and for highlighting) and the'shape' (the actual
  213. // display object). Additionally checks if Vector should be
  214. // drawn as an arrow or a circle (zero-length)
  215. //
  216. this.onBeforeRender(this);
  217. if(this.getRadius() >= this.minimumSize){
  218. this._create("hit", this.data, this.style.currentHit);
  219. this._create("shape", this.data, this.style.current);
  220. }else{
  221. this.data.cosphi = 0;
  222. this._createZeroVector("hit", this.data, this.style.currentHit);
  223. this._createZeroVector("shape", this.data, this.style.current);
  224. }
  225. },
  226. onUp: function(/*EventObject*/obj){
  227. // summary: See stencil._Base.onUp
  228. //
  229. if(this.created || !this._downOnCanvas){ return; }
  230. this._downOnCanvas = false;
  231. //Default vector for single click
  232. if(!this.shape){
  233. var d = 100;
  234. obj.start.x = this.style.zAxis ? obj.start.x + d : obj.start.x;
  235. obj.y = obj.y+d;
  236. this.setPoints([
  237. {x:obj.start.x, y:obj.start.y},
  238. {x:obj.x, y:obj.y}
  239. ]);
  240. this.render();
  241. }
  242. // When within minimum size this sets zero vector length to zero
  243. if(this.getRadius()<this.minimumSize){
  244. var p = this.points;
  245. this.setPoints([
  246. {x:p[0].x, y:p[0].y},
  247. {x:p[0].x, y:p[0].y}
  248. ]);
  249. }else{
  250. //SnapAngle fails for the zero length vector
  251. var p = this.points;
  252. var pt = this.style.zAxis ? this.zPoint(obj) : this.util.snapAngle(obj, this.angleSnap/180);
  253. this.setPoints([
  254. {x:p[0].x, y:p[0].y},
  255. {x:pt.x, y:pt.y}
  256. ]);
  257. }
  258. this.renderedOnce = true;
  259. this.onRender(this);
  260. }
  261. }
  262. );
  263. dojox.drawing.tools.custom.Vector.setup = {
  264. // summary: See stencil._Base ToolsSetup
  265. //
  266. name:"dojox.drawing.tools.custom.Vector",
  267. tooltip:"Vector Tool",
  268. iconClass:"iconVector"
  269. };
  270. if(dojox.drawing.defaults.zAxisEnabled){
  271. dojox.drawing.tools.custom.Vector.setup.secondary = {
  272. // summary:
  273. // Creates a secondary tool for the Vector Stencil.
  274. // description:
  275. // See Toolbar.js makeButtons function. The toolbar
  276. // checks Vector.setup for a secondary tool and requires
  277. // name, label, and funct. Currently it doesn't accept icon
  278. // and only uses text from label for the button. Funct is the
  279. // function that fires when the button is clicked.
  280. //
  281. // Setup and postSetup are optional
  282. // and allow tool specific functions to be added to the
  283. // Toolbar object as if they were written there.
  284. name: "vectorSecondary",
  285. label: "z-axis",
  286. funct: function(button){
  287. button.selected ? this.zDeselect(button) : this.zSelect(button);
  288. var stencils = this.drawing.stencils.selectedStencils;
  289. for(var nm in stencils){
  290. if(stencils[nm].shortType == "vector" && (stencils[nm].style.zAxis != dojox.drawing.defaults.zAxis)){
  291. var s = stencils[nm];
  292. s.changeAxis();
  293. //Reset anchors
  294. if(s.style.zAxis){ s.deselect(); s.select(); }
  295. }
  296. }
  297. },
  298. setup: function(){
  299. // summary:
  300. // All functions, variables and connections defined here
  301. // are treated as if they were added directly to toolbar.
  302. // They are included with the tool because secondary buttons
  303. // are tool specific.
  304. var zAxis = dojox.drawing.defaults.zAxis;
  305. this.zSelect = function(button){
  306. if(!button.enabled){ return; }
  307. zAxis = true;
  308. dojox.drawing.defaults.zAxis = true;
  309. button.select();
  310. this.vectorTest();
  311. this.zSelected = button;
  312. };
  313. this.zDeselect = function(button){
  314. if(!button.enabled){ return; }
  315. zAxis = false;
  316. dojox.drawing.defaults.zAxis = false;
  317. button.deselect();
  318. this.vectorTest();
  319. this.zSelected = null;
  320. };
  321. this.vectorTest = function(){
  322. dojo.forEach(this.buttons, function(b){
  323. if(b.toolType=="vector" && b.selected){
  324. this.drawing.currentStencil.style.zAxis = zAxis;
  325. }
  326. },this);
  327. };
  328. dojo.connect(this, "onRenderStencil", this, function(){ if(this.zSelected){ this.zDeselect(this.zSelected)}});
  329. var c = dojo.connect(this.drawing, "onSurfaceReady", this, function(){
  330. dojo.disconnect(c);
  331. dojo.connect(this.drawing.stencils, "onSelect", this, function(stencil){
  332. if(stencil.shortType == "vector"){
  333. if(stencil.style.zAxis){
  334. //If stencil is on the z-axis, update button to reflect that
  335. dojo.forEach(this.buttons, function(b){
  336. if(b.toolType=="vectorSecondary"){
  337. this.zSelect(b);
  338. }
  339. },this);
  340. }else{
  341. //Update button to not be z-axis
  342. dojo.forEach(this.buttons, function(b){
  343. if(b.toolType=="vectorSecondary"){
  344. this.zDeselect(b);
  345. }
  346. },this);
  347. }
  348. };
  349. });
  350. });
  351. },
  352. postSetup: function(btn){
  353. // summary:
  354. // Depending on the secondary tool, it may need
  355. // extra functionality for some of the basic functions.
  356. // Post is passed the button so those connections can
  357. // be made.
  358. dojo.connect(btn, "enable", function(){ dojox.drawing.defaults.zAxisEnabled = true; });
  359. dojo.connect(btn, "disable", function(){ dojox.drawing.defaults.zAxisEnabled = false; });
  360. }
  361. };
  362. }
  363. dojox.drawing.register(dojox.drawing.tools.custom.Vector.setup, "tool");
  364. }