Polyline.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /**
  2. * Class to generate polyline
  3. *
  4. * @author Dmitry Farafonov
  5. */
  6. var ANCHOR_TYPE= {
  7. main: "main",
  8. middle: "middle",
  9. first: "first",
  10. last: "last"
  11. };
  12. function Anchor(uuid, type, x, y) {
  13. this.uuid = uuid;
  14. this.x = x
  15. this.y = y
  16. this.type = (type == ANCHOR_TYPE.middle) ? ANCHOR_TYPE.middle : ANCHOR_TYPE.main;
  17. };
  18. Anchor.prototype = {
  19. uuid: null,
  20. x: 0,
  21. y: 0,
  22. type: ANCHOR_TYPE.main,
  23. isFirst: false,
  24. isLast: false,
  25. ndex: 0,
  26. typeIndex: 0
  27. };
  28. function Polyline(uuid, points, strokeWidth) {
  29. /* Array on coordinates:
  30. * points: [{x: 410, y: 110}, 1
  31. * {x: 570, y: 110}, 1 2
  32. * {x: 620, y: 240}, 2 3
  33. * {x: 750, y: 270}, 3 4
  34. * {x: 650, y: 370}]; 4
  35. */
  36. this.points = points;
  37. /*
  38. * path for graph
  39. * [["M", x1, y1], ["L", x2, y2], ["C", ax, ay, bx, by, x3, y3], ["L", x3, y3]]
  40. */
  41. this.path = [];
  42. this.anchors = [];
  43. if (strokeWidth) this.strokeWidth = strokeWidth;
  44. this.closePath = false;
  45. this.init();
  46. };
  47. Polyline.prototype = {
  48. id: null,
  49. points: [],
  50. path: [],
  51. anchors: [],
  52. strokeWidth: 1,
  53. radius: 15,
  54. showDetails: false,
  55. element: null,
  56. isDefaultConditionAvailable: false,
  57. closePath: false,
  58. init: function(points){
  59. var linesCount = this.getLinesCount();
  60. if (linesCount < 1)
  61. return;
  62. this.normalizeCoordinates();
  63. // create anchors
  64. this.pushAnchor(ANCHOR_TYPE.first, this.getLine(0).x1, this.getLine(0).y1);
  65. for(var i = 1; i < linesCount; i++){
  66. var line1 = this.getLine(i-1),
  67. line2 = this.getLine(i);
  68. //this.pushAnchor(ANCHOR_TYPE.middle, line1.x1 + line1.x2-line1.x1, line1.y1 + line1.y2-line1.y1);
  69. this.pushAnchor(ANCHOR_TYPE.main, line1.x2, line1.y2);
  70. //this.pushAnchor(ANCHOR_TYPE.middle, line2.x1 + line2.x2-line2.x1, line2.y1 + line2.y2-line2.y1);
  71. }
  72. this.pushAnchor(ANCHOR_TYPE.last, this.getLine(linesCount-1).x2, this.getLine(linesCount-1).y2);
  73. this.rebuildPath();
  74. },
  75. normalizeCoordinates: function(){
  76. for(var i=0; i < this.points.length; i++){
  77. this.points[i].x = parseFloat(this.points[i].x);
  78. this.points[i].y = parseFloat(this.points[i].y);
  79. }
  80. },
  81. getLinesCount: function(){
  82. return this.points.length-1;
  83. },
  84. _getLine: function(i){
  85. return {x1: this.points[i].x, y1: this.points[i].y, x2: this.points[i+1].x, y2: this.points[i+1].y};
  86. },
  87. getLine: function(i){
  88. var line = this._getLine(i);
  89. line.angle = this.getLineAngle(i) ;
  90. return line;
  91. },
  92. getLineAngle: function(i){
  93. var line = this._getLine(i);
  94. return Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
  95. },
  96. getLineLengthX: function(i){
  97. var line = this.getLine(i);
  98. return (line.x2 - line.x1);
  99. },
  100. getLineLengthY: function(i){
  101. var line = this.getLine(i);
  102. return (line.y2 - line.y1);
  103. },
  104. getLineLength: function(i){
  105. var line = this.getLine(i);
  106. return Math.sqrt(Math.pow(this.getLineLengthX(i), 2) + Math.pow(this.getLineLengthY(i), 2));
  107. },
  108. getAnchors: function(){
  109. // ������� ��������������� ������
  110. // ????
  111. return this.anchors;
  112. },
  113. getAnchorsCount: function(type){
  114. if (!type)
  115. return this.anchors.length;
  116. else {
  117. var count = 0;
  118. for(var i=0; i < this.getAnchorsCount(); i++){
  119. var anchor = this.anchors[i];
  120. if (anchor.getType() == type) {
  121. count++;
  122. }
  123. }
  124. return count;
  125. }
  126. },
  127. pushAnchor: function(type, x, y, index){
  128. if (type == ANCHOR_TYPE.first) {
  129. index = 0;
  130. typeIndex = 0;
  131. } else if (type == ANCHOR_TYPE.last) {
  132. index = this.getAnchorsCount();
  133. typeIndex = 0;
  134. } else if (!index) {
  135. index = this.anchors.length;
  136. } else {
  137. // ��������� anchors, �������� ������� ��� �������, ������� � index
  138. //var anchor = this.getAnchor()
  139. for(var i=0; i < this.getAnchorsCount(); i++){
  140. var anchor = this.anchors[i];
  141. if (anchor.index > index) {
  142. anchor.index++;
  143. anchor.typeIndex++;
  144. }
  145. }
  146. }
  147. var anchor = new Anchor(this.id, ANCHOR_TYPE.main, x, y, index, typeIndex);
  148. this.anchors.push(anchor);
  149. },
  150. getAnchor: function(position){
  151. return this.anchors[position];
  152. },
  153. getAnchorByType: function(type, position){
  154. if (type == ANCHOR_TYPE.first)
  155. return this.anchors[0];
  156. if (type == ANCHOR_TYPE.last)
  157. return this.anchors[this.getAnchorsCount()-1];
  158. for(var i=0; i < this.getAnchorsCount(); i++){
  159. var anchor = this.anchors[i];
  160. if (anchor.type == type) {
  161. if( position == anchor.position)
  162. return anchor;
  163. }
  164. }
  165. return null;
  166. },
  167. addNewPoint: function(position, x, y){
  168. //
  169. for(var i = 0; i < this.getLinesCount(); i++){
  170. var line = this.getLine(i);
  171. if (x > line.x1 && x < line.x2 && y > line.y1 && y < line.y2) {
  172. this.points.splice(i+1,0,{x: x, y: y});
  173. break;
  174. }
  175. }
  176. this.rebuildPath();
  177. },
  178. rebuildPath: function(){
  179. var path = [];
  180. for(var i = 0; i < this.getAnchorsCount(); i++){
  181. var anchor = this.getAnchor(i);
  182. var pathType = ""
  183. if (i==0)
  184. pathType = "M";
  185. else
  186. pathType = "L";
  187. // TODO: save previous points and calculate new path just if points are updated, and then save currents values as previous
  188. var targetX = anchor.x, targetY = anchor.y;
  189. if (i>0 && i < this.getAnchorsCount()-1) {
  190. // get new x,y
  191. var cx = anchor.x, cy = anchor.y;
  192. // pivot point of prev line
  193. var AO = this.getLineLength(i-1);
  194. if (AO < this.radius) {
  195. AO = this.radius;
  196. }
  197. this.isDefaultConditionAvailable = (this.isDefaultConditionAvailable || (i == 1 && AO > 10));
  198. //console.log("isDefaultConditionAvailable", this.isDefaultConditionAvailable);
  199. var ED = this.getLineLengthY(i-1) * this.radius / AO;
  200. var OD = this.getLineLengthX(i-1) * this.radius / AO;
  201. targetX = anchor.x - OD;
  202. targetY = anchor.y - ED;
  203. if (AO < 2*this.radius && i>1) {
  204. targetX = anchor.x - this.getLineLengthX(i-1)/2;
  205. targetY = anchor.y - this.getLineLengthY(i-1)/2;;
  206. }
  207. // pivot point of next line
  208. var AO = this.getLineLength(i);
  209. if (AO < this.radius) {
  210. AO = this.radius;
  211. }
  212. var ED = this.getLineLengthY(i) * this.radius / AO;
  213. var OD = this.getLineLengthX(i) * this.radius / AO;
  214. var nextSrcX = anchor.x + OD;
  215. var nextSrcY = anchor.y + ED;
  216. if (AO < 2*this.radius && i<this.getAnchorsCount()-2) {
  217. nextSrcX = anchor.x + this.getLineLengthX(i)/2;
  218. nextSrcY = anchor.y + this.getLineLengthY(i)/2;;
  219. }
  220. var dx0 = (cx - targetX) / 3,
  221. dy0 = (cy - targetY) / 3,
  222. ax = cx - dx0,
  223. ay = cy - dy0,
  224. dx1 = (cx - nextSrcX) / 3,
  225. dy1 = (cy - nextSrcY) / 3,
  226. bx = cx - dx1,
  227. by = cy - dy1,
  228. zx=nextSrcX, zy=nextSrcY;
  229. if (this.showDetails) {
  230. var c = ProcessDiagramCanvas.g.path("M"+targetX+","+targetY+"L"+ax+","+ay).attr({stroke: Color.get(255, 153, 51), "stroke-dasharray": "- "});
  231. var c = ProcessDiagramCanvas.g.path("M"+nextSrcX+","+nextSrcY+"L"+bx+","+by).attr({stroke: Color.get(255, 153, 51), "stroke-dasharray": "- "});
  232. var c = ProcessDiagramCanvas.g.ellipse(ax, ay, 2, 2).attr({stroke: Color.SlateGrey});
  233. var c = ProcessDiagramCanvas.g.ellipse(bx, by, 2, 2).attr({stroke: Color.SlateGrey});
  234. var c = ProcessDiagramCanvas.g.ellipse(cx, cy, this.radius, this.radius).attr({stroke: Color.Gainsboro});
  235. var c = ProcessDiagramCanvas.g.ellipse(targetX, targetY, 2, 2).attr({fill: Color.red});
  236. var c = ProcessDiagramCanvas.g.ellipse(nextSrcX, nextSrcY, 2, 2).attr({fill: Color.red});
  237. }
  238. } else if (i==1 && this.getAnchorsCount() == 2){
  239. var AO = this.getLineLength(i-1);
  240. if (AO < this.radius) {
  241. AO = this.radius;
  242. }
  243. this.isDefaultConditionAvailable = (this.isDefaultConditionAvailable || (i == 1 && AO > 10));
  244. //console.log("-- isDefaultConditionAvailable", this.isDefaultConditionAvailable);
  245. }
  246. // anti smoothing
  247. if (this.strokeWidth%2 == 1) {
  248. targetX += 0.5;
  249. targetY += 0.5;
  250. }
  251. path.push([pathType, targetX, targetY]);
  252. if (i>0 && i < this.getAnchorsCount()-1) {
  253. path.push(["C", ax, ay, bx, by, zx, zy]);
  254. }
  255. }
  256. if (this.closePath) {
  257. console.log("closePath:", this.closePath);
  258. path.push(["Z"]);
  259. }
  260. this.path = path;
  261. },
  262. transform: function(transformation){
  263. this.element.transform(transformation);
  264. },
  265. attr: function(attrs){
  266. //console.log("attrs: " +attrs, "", this.element);
  267. // TODO: foreach and set each
  268. this.element.attr(attrs);
  269. }
  270. };
  271. function Polygone(points, strokeWidth) {
  272. /* Array on coordinates:
  273. * points: [{x: 410, y: 110}, 1
  274. * {x: 570, y: 110}, 1 2
  275. * {x: 620, y: 240}, 2 3
  276. * {x: 750, y: 270}, 3 4
  277. * {x: 650, y: 370}]; 4
  278. */
  279. this.points = points;
  280. /*
  281. * path for graph
  282. * [["M", x1, y1], ["L", x2, y2], ["C", ax, ay, bx, by, x3, y3], ["L", x3, y3]]
  283. */
  284. this.path = [];
  285. this.anchors = [];
  286. if (strokeWidth) this.strokeWidth = strokeWidth;
  287. this.closePath = true;
  288. this.init();
  289. };
  290. /*
  291. * Poligone is inherited from Poliline: draws closedPath of polyline
  292. */
  293. var Foo = function () { };
  294. Foo.prototype = Polyline.prototype;
  295. Polygone.prototype = new Foo();
  296. Polygone.prototype.rebuildPath = function(){
  297. var path = [];
  298. //console.log("Polygone rebuildPath");
  299. for(var i = 0; i < this.getAnchorsCount(); i++){
  300. var anchor = this.getAnchor(i);
  301. var pathType = ""
  302. if (i==0)
  303. pathType = "M";
  304. else
  305. pathType = "L";
  306. var targetX = anchor.x, targetY = anchor.y;
  307. // anti smoothing
  308. if (this.strokeWidth%2 == 1) {
  309. targetX += 0.5;
  310. targetY += 0.5;
  311. }
  312. path.push([pathType, targetX, targetY]);
  313. }
  314. if (this.closePath)
  315. path.push(["Z"]);
  316. this.path = path;
  317. };
  318. /*
  319. Polygone.prototype.transform = function(transformation){
  320. this.element.transform(transformation);
  321. };
  322. */