Axes.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  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.Axes"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.drawing.tools.custom.Axes"] = true;
  8. dojo.provide("dojox.drawing.tools.custom.Axes");
  9. dojo.require("dojox.drawing.stencil.Path");
  10. dojox.drawing.tools.custom.Axes = dojox.drawing.util.oo.declare(
  11. // summary:
  12. // Draws a right-angle Axes (shaped like an L, not a +)
  13. // description:
  14. // This Stencil is created with a Path so that the L shape
  15. // is one continuous piece. Arrow heads are placed at the end
  16. // of each axis. The Axes can be rotated. There are custom
  17. // label methods.
  18. //
  19. dojox.drawing.stencil.Path,
  20. function(options){
  21. this.closePath = false;
  22. this.xArrow = new dojox.drawing.annotations.Arrow({stencil:this, idx1:0, idx2:1});
  23. this.yArrow = new dojox.drawing.annotations.Arrow({stencil:this, idx1:2, idx2:1});
  24. if(options.data){
  25. //Allows import of z-axis in non-enabled canvas and xy-axis in
  26. //enabled canvas
  27. this.style.zAxisEnabled = options.data.cosphi == 1 ? true : false;
  28. this.setData(options.data);
  29. }
  30. if(this.style.zAxisEnabled){
  31. // If the z-axis is enabled, all axes will be created with a z-axis on the canvas.
  32. // there is no switching back and forth for the axis, only for vectors.
  33. this.data.cosphi = 1;
  34. var ops = {};
  35. dojo.mixin(ops,options);
  36. dojo.mixin(ops,{
  37. container:this.container.createGroup(),
  38. style: this.style,
  39. showAngle: false,
  40. label: null
  41. });
  42. if(options.data && (!ops.data.radius || !ops.data.angle)){
  43. ops.data.x2 = ops.data.x4;
  44. ops.data.y2 = ops.data.y4;
  45. }
  46. ops.style.zAxis = true;
  47. this.zAxis = new dojox.drawing.tools.custom.Vector(ops);
  48. this.zAxis.minimumSize = 5;
  49. //console.log("-----constructing axes: ",this.zAxis);
  50. this.connectMult([
  51. [this, "onChangeStyle", this.zAxis, "onChangeStyle"],
  52. [this, "select", this.zAxis, "select"],
  53. [this, "deselect", this.zAxis, "deselect"],
  54. [this, "onDelete", this.zAxis, "destroy"],
  55. [this, "onDrag", this, "zSet"],
  56. [this, "onTransform", this, "zSet"],
  57. [this.zAxis, "onBeforeRender", this, "zSet"],
  58. [this, "_onPostRender", this.zAxis, "render"]
  59. ]);
  60. }
  61. if(this.points && this.points.length){
  62. this.setPoints = this._postSetPoints;
  63. // render isn't called yet because baseRender is false
  64. // instead it is called here
  65. this.render();
  66. options.label && this.setLabel(options.label);
  67. options.shadow && this.addShadow(options.shadow);
  68. }
  69. },
  70. {
  71. draws:true,
  72. type:"dojox.drawing.tools.custom.Axes",
  73. minimumSize:30,
  74. showAngle:true,
  75. closePath:false,
  76. baseRender:false,
  77. zScale:.5,
  78. zPoint: function(obj){
  79. // summary:
  80. // Finds the point for the z axis.
  81. obj.radius = this.util.length(obj);
  82. var pt = this.util.pointOnCircle(obj.start.x, obj.start.y, obj.radius*this.zScale, this.style.zAngle);
  83. return {x:pt.x, y:pt.y, skip:true, noAnchor:true};
  84. },
  85. zSet: function(){
  86. if(!this.zAxis){ return; };
  87. var c = this.points[1];
  88. var z = this.points[3];
  89. var p = [
  90. {x:c.x, y:c.y},
  91. {x:z.x, y:z.y}
  92. ];
  93. var len = this.util.length({start:{x:c.x, y:c.y}, x:z.x, y:z.y});
  94. len > this.zAxis.minimumSize ? this.zAxis.setPoints(p) : false;
  95. this.zAxis.cosphi = 1;
  96. },
  97. createLabels: function(){
  98. // summary:
  99. // Creates the label for each axis.
  100. //
  101. // NOTE: Not passing style into text because it's changing it
  102. var props = {align:"middle", valign:"middle", util:this.util, annotation:true, container:this.container, mouse:this.mouse, stencil:this};
  103. this.labelX = new dojox.drawing.annotations.Label(dojo.mixin(props,{
  104. labelPosition:this.setLabelX
  105. }));
  106. this.labelY = new dojox.drawing.annotations.Label(dojo.mixin(props,{
  107. labelPosition:this.setLabelY
  108. }));
  109. if(this.style.zAxisEnabled){
  110. this.labelZ = new dojox.drawing.annotations.Label(dojo.mixin(props,{
  111. labelPosition:this.setLabelZ
  112. }));
  113. }
  114. },
  115. setLabelX: function(){
  116. // summary:
  117. // Custom placement for x-axis label
  118. //
  119. var ax = this.points[0];
  120. var c = this.points[1];
  121. var dist = 40;
  122. var offdist = 20;
  123. var pt, px, py, pt2;
  124. pt = this.util.lineSub(c.x, c.y, ax.x, ax.y, dist);
  125. px = pt.x + (pt.y -ax.y);
  126. py = pt.y + (ax.x - pt.x);
  127. pt2 = this.util.lineSub(pt.x, pt.y, px, py, (dist-offdist));
  128. return {
  129. x: pt2.x,
  130. y: pt2.y,
  131. width:20
  132. };
  133. },
  134. setLabelY: function(){
  135. // summary:
  136. // Custom placement for y-axis label
  137. //
  138. var c = this.points[1];
  139. var ay = this.points[2];
  140. var dist = 40;
  141. var offdist = 20;
  142. var pt, px, py, pt2;
  143. pt = this.util.lineSub(c.x, c.y, ay.x, ay.y, dist);
  144. px = pt.x + (ay.y - pt.y);
  145. py = pt.y + (pt.x - ay.x);
  146. pt2 = this.util.lineSub(pt.x, pt.y, px, py, (dist-offdist));
  147. return {
  148. x: pt2.x,
  149. y: pt2.y,
  150. width:20
  151. };
  152. },
  153. setLabelZ: function(){
  154. // summary:
  155. // Custom placement for z-axis label
  156. //
  157. var c = this.points[1];
  158. var z = this.points[3];
  159. var dist = 40;
  160. var offdist = 20;
  161. var pt, px, py, pt2;
  162. pt = this.util.lineSub(c.x, c.y, z.x, z.y, dist);
  163. px = pt.x + (pt.y - z.y);
  164. py = pt.y + (z.x - pt.x);
  165. pt2 = this.util.lineSub(pt.x, pt.y, px, py, (dist-offdist));
  166. return {
  167. x:pt2.x,
  168. y:pt2.y,
  169. width:20
  170. }
  171. },
  172. setLabel: function(/* ? String*/value){
  173. // summary:
  174. // Set the text of the labels. The text would be
  175. // broken up into the two labels.
  176. // arguments:
  177. // value: [optional] String
  178. // If no argument is passed, defaults to two labels
  179. // 'x' and 'y'. If an argument is passed, that
  180. // text will be split on the word 'and' to determine
  181. // the two labels.
  182. //
  183. if(this._labelsCreated){ return; }
  184. !this.labelX && this.createLabels();
  185. var x = "x";
  186. var y = "y";
  187. var z = "z";
  188. if(value){
  189. // match first "and" or "&" and trim whitespace.
  190. // Non-greedy matches are not supported in older
  191. // browsers such as Netscape Navigator 4 or
  192. // Microsoft Internet Explorer 5.0.
  193. if(this.labelZ){
  194. var lbls = value.match(/(.*?)(and|&)(.*?)(and|&)(.*)/i);
  195. if(lbls.length>4){
  196. x = lbls[1].replace(/^\s+/,"").replace(/\s+$/,"");
  197. y = lbls[3].replace(/^\s+/,"").replace(/\s+$/,"");
  198. z = lbls[5].replace(/^\s+/,"").replace(/\s+$/,"");
  199. }
  200. }else{
  201. var lbls = value.match(/(.*?)(and|&)(.*)/i);
  202. if(lbls.length>2){
  203. x = lbls[1].replace(/^\s+/,"").replace(/\s+$/,"");
  204. y = lbls[3].replace(/^\s+/,"").replace(/\s+$/,"");
  205. }
  206. }
  207. }
  208. this.labelX.setLabel(x);
  209. this.labelY.setLabel(y);
  210. if(this.labelZ){
  211. this.labelZ.setLabel(z);
  212. }
  213. this._labelsCreated = true;
  214. },
  215. getLabel: function(){
  216. // summary:
  217. // Getter for the labels. returns an object.
  218. //
  219. if(!this.labelX){ return null; }
  220. return {
  221. x:this.labelX.getText(),
  222. y:this.labelY.getText(),
  223. z:this.labelZ?this.labelZ.getText():null
  224. }; // Object
  225. },
  226. anchorPositionCheck: function(/*Number*/x, /*Number*/y, /*manager.Anchor*/anchor){
  227. // summary:
  228. // Gets called from anchor to check if its current
  229. // position is ok. If not, its x or y transform will
  230. // be changed until this passes.
  231. //
  232. var pm = this.container.getParent().getTransform();
  233. var am = anchor.shape.getTransform();
  234. // the xaxis point has changed and is not yet set as a point
  235. // - but the center should be good (except for the transform).
  236. // Now check the yaxis point.
  237. var p = this.points;
  238. var o = {x:am.dx+anchor.org.x+pm.dx, y:am.dy+anchor.org.y+pm.dy};
  239. var c = {x:p[1].x+pm.dx, y:p[1].y+pm.dy};
  240. var ox = c.x - (c.y - o.y);
  241. var oy = c.y - (o.x - c.x);
  242. return {x:ox, y:oy};
  243. },
  244. onTransformBegin: function(/*manager.Anchor*/anchor){
  245. // summary:
  246. // Overwrites _Base.onTransformBegin
  247. //
  248. // called from anchor point up mouse down
  249. this._isBeingModified = true;
  250. },
  251. onTransformEnd: function(/*manager.Anchor*/anchor){
  252. // summary:
  253. // Overwrites _Base.onTransformEnd
  254. //
  255. // Gets called on anchor mouseup
  256. // also gets called by checkBounds - we don't want that.
  257. if(!anchor){ return; }
  258. // tell anchor to go to prev point if wrong
  259. // called from anchor point up mouse up
  260. this._isBeingModified = false;
  261. //this.deselect();
  262. this._toggleSelected();
  263. console.log("before:", Math.ceil(this.points[1].x), " x ", Math.ceil(this.points[1].y))
  264. var o = this.points[0];
  265. var c = this.points[1];
  266. var obj = {start:{x:c.x,y:c.y},x:o.x, y:o.y};
  267. var pt = this.util.constrainAngle(obj, 0, 89);
  268. var zpt = this.style.zAxisEnabled ? this.zPoint(obj) : null;
  269. if(pt.x==o.x && pt.y == o.y){
  270. // we're within the constraint, so now we snap
  271. pt = this.util.snapAngle(obj, this.angleSnap/180);
  272. obj.x = pt.x;
  273. obj.y = pt.y;
  274. var ox = obj.start.x - (obj.start.y - obj.y);
  275. var oy = obj.start.y - (obj.x - obj.start.x);
  276. if(ox<0 || oy<0){
  277. console.warn("AXES ERROR LESS THAN ZERO - ABORT");
  278. return;
  279. }
  280. this.points = [{x:obj.x, y:obj.y}, {x:obj.start.x, y:obj.start.y, noAnchor:true}];
  281. this.points.push({x:ox, y:oy, noAnchor:true});
  282. if(zpt){ this.points.push(zpt);}
  283. this.setPoints(this.points);
  284. //this.select();
  285. this.onModify(this);
  286. return;
  287. }
  288. // we're outside of the constraint. Set to the low or high.
  289. this.points[0].x = pt.x
  290. this.points[0].y = pt.y;
  291. o = this.points[0];
  292. var ox = c.x - (c.y - o.y);
  293. var oy = c.y - (o.x - c.x);
  294. this.points[2] = {x:ox, y:oy, noAnchor:true};
  295. if(zpt){ this.points.push(zpt); }
  296. this.setPoints(this.points);
  297. // reset handles render
  298. //anchor.reset(this);
  299. this.labelX.setLabel();
  300. this.labelY.setLabel();
  301. if(this.labelZ){
  302. this.labelZ.setLabel();
  303. }
  304. //this.select();
  305. this.onModify(this);
  306. },
  307. getBounds: function(/*Boolean*/absolute){
  308. // summary:
  309. // Custom getBounds overwrites _Base.getBounds
  310. //
  311. var px = this.points[0],
  312. pc = this.points[1],
  313. py = this.points[2];
  314. if(this.style.zAxisEnabled){ var pz = this.points[3]; }
  315. if(absolute){
  316. var bounds = {
  317. x:pc.x,
  318. y:pc.y,
  319. x1:pc.x,
  320. y1:pc.y,
  321. x2:px.x,
  322. y2:px.y,
  323. x3:py.x,
  324. y3:py.y
  325. };
  326. if(this.style.zAxisEnabled){
  327. bounds.x4 = pz.x;
  328. bounds.y4 = pz.y;
  329. }
  330. return bounds;
  331. }
  332. var x1 = this.style.zAxisEnabled ? (py.x < pz.x ? py.x : pz.x) : py.x;
  333. y1 = py.y < px.y ? py.y : px.y,
  334. x2 = px.x,
  335. y2 = this.style.zAxisEnabled ? pz.y : pc.y;
  336. return {
  337. x1:x1,
  338. y1:y1,
  339. x2:x2,
  340. y2:y2,
  341. x:x1,
  342. y:y1,
  343. w:x2-x1,
  344. h:y2-y1
  345. };
  346. },
  347. _postSetPoints: function(/*Array*/pts){
  348. // summary:
  349. // Because Axes only has one anchor,
  350. // we substitute a special setPoints method
  351. //
  352. this.points[0] = pts[0];
  353. if(this.pointsToData){
  354. this.data = this.pointsToData();
  355. }
  356. },
  357. onTransform: function(/*Number*/anchor){
  358. // summary:
  359. // Overwrites _Base.onTransform
  360. //
  361. // the xaxis point has changed - the center will not.
  362. // need to find the yaxis point.
  363. var o = this.points[0];
  364. var c = this.points[1];
  365. var ox = c.x - (c.y - o.y);
  366. var oy = c.y - (o.x - c.x);
  367. // 'noAnchor' on a point indicates an anchor should
  368. // not be rendered. This is the Y point being set.
  369. this.points[2] = {x:ox, y:oy, noAnchor:true};
  370. if(this.style.zAxisEnabled){
  371. this.points[3] = this.zPoint({start:{x:c.x, y:c.y}, x:o.x, y:o.y});
  372. }
  373. this.setPoints(this.points);
  374. if(!this._isBeingModified){
  375. this.onTransformBegin();
  376. }
  377. this.render();
  378. },
  379. pointsToData: function(){
  380. //summary:
  381. // Converts points to data.
  382. var p = this.points;
  383. var d = {
  384. x1:p[1].x,
  385. y1:p[1].y,
  386. x2:p[0].x,
  387. y2:p[0].y,
  388. x3:p[2].x,
  389. y3:p[2].y
  390. }
  391. if(this.style.zAxisEnabled){
  392. d.x4 = p[3].x;
  393. d.y4 = p[3].y;
  394. d.cosphi = 1;
  395. }
  396. return d;
  397. },
  398. getRadius: function(){
  399. //summary:
  400. // Possibility of z-axis makes bounds unreliable.
  401. // Use these points instead.
  402. var p = this.points;
  403. var line = {start:{x:p[1].x, y:p[1].y}, x:p[0].x, y:p[0].y};
  404. return this.util.length(line);
  405. },
  406. dataToPoints: function(/* ? Object*/o){
  407. //summary:
  408. // Converts data to points.
  409. o = o || this.data;
  410. if(o.radius || o.angle){
  411. // instead of using x1,x2,y1,y1,
  412. // it's been set as x,y,angle,radius
  413. var pt = this.util.pointOnCircle(o.x,o.y,o.radius,o.angle), zpt;
  414. var ox = o.x - (o.y - pt.y);
  415. var oy = o.y - (pt.x - o.x);
  416. if((o.cosphi && o.cosphi==1) || this.style.zAxisEnabled){
  417. this.style.zAxisEnabled = true;
  418. zpt = this.util.pointOnCircle(o.x, o.y, o.radius*this.zScale, this.style.zAngle);
  419. }
  420. this.data = o = {
  421. x1:o.x,
  422. y1:o.y,
  423. x2:pt.x,
  424. y2:pt.y,
  425. x3:ox,
  426. y3:oy
  427. }
  428. if(this.style.zAxisEnabled){
  429. this.data.x4 = o.x4 = zpt.x;
  430. this.data.y4 = o.y4 = zpt.y;
  431. this.data.cosphi = 1;
  432. }
  433. }
  434. this.points = [
  435. {x:o.x2, y:o.y2},
  436. {x:o.x1, y:o.y1, noAnchor:true},
  437. {x:o.x3, y:o.y3, noAnchor:true}
  438. ];
  439. if(this.style.zAxisEnabled){ this.points.push({x:o.x4, y:o.y4, skip:true, noAnchor:true}); }
  440. return this.points;
  441. },
  442. onDrag: function(/*EventObject*/obj){
  443. // summary: See stencil._Base.onDrag
  444. //
  445. var pt = this.util.constrainAngle(obj, 0, 89);
  446. obj.x = pt.x;
  447. obj.y = pt.y;
  448. var ox = obj.start.x - (obj.start.y - obj.y);
  449. var oy = obj.start.y - (obj.x - obj.start.x);
  450. if(ox<0 || oy<0){
  451. return;
  452. }
  453. this.points = [{x:obj.x, y:obj.y}, {x:obj.start.x, y:obj.start.y, noAnchor:true}];
  454. this.points.push({x:ox, y:oy, noAnchor:true});
  455. if(this.style.zAxisEnabled){
  456. var zpt = this.zPoint(obj);
  457. this.points.push(zpt);
  458. }
  459. this.render();
  460. },
  461. onUp: function(/*EventObject*/obj){
  462. // summary: See stencil._Base.onUp
  463. //
  464. if(!this._downOnCanvas){ return; }
  465. this._downOnCanvas = false;
  466. var p = this.points;
  467. if(!p.length){
  468. var s = obj.start, d = 100;
  469. this.points = [
  470. {x:s.x+d, y:s.y+d},
  471. {x:s.x, y:s.y+d, noAnchor:true},
  472. {x:s.x, y:s.y, noAnchor:true}
  473. ];
  474. if(this.style.zAxisEnabled){
  475. var zpt = this.zPoint({start:{x:s.x, y:s.y+d}, x:s.x+d, y:s.y+d});
  476. this.points.push(zpt);
  477. }
  478. this.setPoints = this._postSetPoints;
  479. this.pointsToData();
  480. this.render();
  481. this.onRender(this);
  482. return;
  483. }
  484. var len = this.util.distance(p[1].x ,p[1].y ,p[0].x ,p[0].y );
  485. if(!p || !p.length){
  486. return;
  487. }else if(len < this.minimumSize){
  488. this.remove(this.shape, this.hit);
  489. this.xArrow.remove(this.xArrow.shape, this.xArrow.hit);
  490. this.yArrow.remove(this.yArrow.shape, this.yArrow.hit);
  491. if(this.zArrow){
  492. this.zArrow.remove(this.zArrow.shape, this.zArrow.hit);
  493. }
  494. return;
  495. }
  496. var o = p[0];
  497. var c = p[1];
  498. obj = {start:{x:c.x,y:c.y},x:o.x,y:o.y};
  499. var pt = this.util.snapAngle(obj, this.angleSnap/180);
  500. obj.x = pt.x;
  501. obj.y = pt.y;
  502. var ox = obj.start.x - (obj.start.y - obj.y);
  503. var oy = obj.start.y - (obj.x - obj.start.x);
  504. if(ox<0 || oy<0){
  505. return;
  506. }
  507. this.points = [{x:obj.x, y:obj.y}, {x:obj.start.x, y:obj.start.y, noAnchor:true}];
  508. this.points.push({x:ox, y:oy, noAnchor:true});
  509. if(this.style.zAxisEnabled){ this.points.push(this.zPoint(obj)); }
  510. this.onRender(this);
  511. this.setPoints = this._postSetPoints;
  512. }
  513. }
  514. );
  515. dojox.drawing.tools.custom.Axes.setup = {
  516. // summary: See stencil._Base ToolsSetup
  517. //
  518. name:"dojox.drawing.tools.custom.Axes",
  519. tooltip:"Axes Tool",
  520. iconClass:"iconAxes"
  521. };
  522. dojox.drawing.register(dojox.drawing.tools.custom.Axes.setup, "tool");
  523. }